-
Notifications
You must be signed in to change notification settings - Fork 90
feat: Metrics utility #91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
6ad846f
initial commit
msailes 3d8dc67
Fix parent module version for metrics
21f9cad
Initial wip version of metrics
e87ca55
Fix parent pom version for module
5242459
Move extract context method for reuse
97be6e4
Cold start EMF capture feature parity
fc34661
Single metrics capture utility
b0ab5eb
Example usage in the demo app
5db1bdc
Test cases for metric util
61c479b
docs: adding javadocs for the metrics core utility.
msailes b1b516f
Use GA version of emf lib
48b088c
Real example of namespace in sample app
5f25a5e
feat: Raise error on empty metrics and validations
23ba633
Donot build on java 9 and 10
38dee7f
docs: added default value.
msailes 97ec6be
Single dimension for Cold start
79ac8d2
Default dimension follow Camel Cases
88708cd
Max dimension validation in sync with python
0ef27cf
Note on simplification
cab3043
docs: adding the metrics doc page.
msailes 3e3dceb
docs: adding the metrics doc page.
msailes File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
--- | ||
title: Metrics | ||
description: Core utility | ||
--- | ||
|
||
Metrics creates custom metrics asynchronously by logging metrics to standard output following Amazon CloudWatch Embedded Metric Format (EMF). | ||
|
||
These metrics can be visualized through [Amazon CloudWatch Console](https://console.aws.amazon.com/cloudwatch/). | ||
|
||
**Key features** | ||
|
||
* Aggregate up to 100 metrics using a single CloudWatch EMF object (large JSON blob) | ||
* Validate against common metric definitions mistakes (metric unit, values, max dimensions, max metrics, etc) | ||
* Metrics are created asynchronously by the CloudWatch service, no custom stacks needed | ||
* Context manager to create a one off metric with a different dimension | ||
|
||
## Initialization | ||
|
||
Set `POWERTOOLS_SERVICE_NAME` and `POWERTOOLS_METRICS_NAMESPACE` env vars as a start - Here is an example using AWS Serverless Application Model (SAM) | ||
|
||
```yaml:title=template.yaml | ||
Resources: | ||
HelloWorldFunction: | ||
Type: AWS::Serverless::Function | ||
Properties: | ||
... | ||
Runtime: java8 | ||
Environment: | ||
Variables: | ||
POWERTOOLS_SERVICE_NAME: payment # highlight-line | ||
POWERTOOLS_METRICS_NAMESPACE: ServerlessAirline # highlight-line | ||
``` | ||
|
||
We recommend you use your application or main service as a metric namespace. | ||
You can explicitly set a namespace name an annotation variable `namespace` param or via `POWERTOOLS_METRICS_NAMESPACE` env var. | ||
|
||
This sets **namespace** key that will be used for all metrics. | ||
You can also pass a service name via `service` param or `POWERTOOLS_SERVICE_NAME` env var. This will create a dimension with the service name. | ||
|
||
```java:title=Handler.java | ||
package example; | ||
|
||
import com.amazonaws.services.lambda.runtime.Context; | ||
import com.amazonaws.services.lambda.runtime.RequestHandler; | ||
import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; | ||
import software.amazon.cloudwatchlogs.emf.model.Unit; | ||
import software.amazon.lambda.powertools.metrics.PowertoolsMetrics; | ||
import software.amazon.lambda.powertools.metrics.PowertoolsMetricsLogger; | ||
|
||
public class PowertoolsMetricsEnabledHandler implements RequestHandler<Object, Object> { | ||
|
||
MetricsLogger metricsLogger = PowertoolsMetricsLogger.metricsLogger(); | ||
|
||
@Override | ||
@PowertoolsMetrics(namespace = "ExampleApplication", service = "booking") | ||
public Object handleRequest(Object input, Context context) { | ||
... | ||
} | ||
} | ||
``` | ||
|
||
You can initialize Metrics anywhere in your code as many times as you need - It'll keep track of your aggregate metrics in memory. | ||
|
||
## Creating metrics | ||
|
||
You can create metrics using `putMetric`, and manually create dimensions for all your aggregate metrics using `add_dimension`. | ||
|
||
```java:title=app.py | ||
public class PowertoolsMetricsEnabledHandler implements RequestHandler<Object, Object> { | ||
|
||
MetricsLogger metricsLogger = PowertoolsMetricsLogger.metricsLogger(); | ||
|
||
@Override | ||
@PowertoolsMetrics(namespace = "ExampleApplication", service = "booking") | ||
public Object handleRequest(Object input, Context context) { | ||
# highlight-start | ||
metricsLogger.putDimensions(DimensionSet.of("environment", "prod")); | ||
metricsLogger.putMetric("SuccessfulBooking", 1, Unit.COUNT); | ||
# highlight-end | ||
... | ||
} | ||
} | ||
``` | ||
|
||
The `Unit` enum facilitate finding a supported metric unit by CloudWatch. | ||
|
||
CloudWatch EMF supports a max of 100 metrics. Metrics utility will flush all metrics when adding the 100th metric while subsequent metrics will be aggregated into a new EMF object, for your convenience. | ||
|
||
## Creating a metric with a different dimension | ||
|
||
CloudWatch EMF uses the same dimensions across all your metrics. Use `single_metric` if you have a metric that should have different dimensions. | ||
|
||
<Note type="info"> | ||
Generally, this would be an edge case since you <a href="https://aws.amazon.com/cloudwatch/pricing/">pay for unique metric</a>. Keep the following formula in mind: | ||
<br/><br/> | ||
<strong>unique metric = (metric_name + dimension_name + dimension_value)</strong> | ||
</Note><br/> | ||
|
||
```java:title=Handler.java | ||
withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> { | ||
metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); | ||
}); | ||
``` | ||
|
||
## Adding metadata | ||
|
||
You can use `putMetadata` for advanced use cases, where you want to metadata as part of the serialized metrics object. | ||
|
||
<Note type="info"> | ||
<strong>This will not be available during metrics visualization</strong> - Use <strong>dimensions</strong> for this purpose | ||
</Note><br/> | ||
|
||
```javv:title=Handler.java | ||
@PowertoolsMetrics(namespace = "ServerlessAirline", service = "payment") | ||
public APIGatewayProxyResponseEvent handleRequest(Object input, Context context) { | ||
metricsLogger().putMetric("CustomMetric1", 1, Unit.COUNT); | ||
metricsLogger().putMetadata("booking_id", "1234567890"); # highlight-line | ||
|
||
... | ||
``` | ||
|
||
This will be available in CloudWatch Logs to ease operations on high cardinal data. | ||
|
||
The `@PowertoolsMetrics` annotation **validates**, **serializes**, and **flushes** all your metrics. During metrics validation, if no metrics are provided then a warning will be logged, but no exception will be raised. | ||
|
||
If metrics are provided, and any of the following criteria are not met, `ValidationException` exception will be raised: | ||
|
||
* Minimum of 1 dimension | ||
* Maximum of 9 dimensions | ||
* Namespace is set, and no more than one | ||
* Metric units must be [supported by CloudWatch](https://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_MetricDatum.html) | ||
msailes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
If you want to ensure that at least one metric is emitted, you can pass `raiseOnEmptyMetrics = true` to the **@PowertoolsMetrics** annotation: | ||
|
||
```java:title=Handler.java | ||
@PowertoolsMetrics(raiseOnEmptyMetrics = true) | ||
public Object handleRequest(Object input, Context context) { | ||
... | ||
``` | ||
|
||
## Capturing cold start metric | ||
|
||
You can capture cold start metrics automatically with `@PowertoolsMetrics` via the `captureColdStart` variable. | ||
|
||
```java:title=Handler.java | ||
@PowertoolsMetrics(captureColdStart = true) | ||
public Object handleRequest(Object input, Context context) { | ||
... | ||
``` | ||
|
||
If it's a cold start invocation, this feature will: | ||
|
||
* Create a separate EMF blob solely containing a metric named `ColdStart` | ||
* Add `FunctionName` and `Service` dimensions | ||
|
||
This has the advantage of keeping cold start metric separate from your application metrics. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.