|
| 1 | +--- |
| 2 | +title: Metrics |
| 3 | +description: Core utility |
| 4 | +--- |
| 5 | + |
| 6 | +Metrics creates custom metrics asynchronously by logging metrics to standard output following Amazon CloudWatch Embedded Metric Format (EMF). |
| 7 | + |
| 8 | +These metrics can be visualized through [Amazon CloudWatch Console](https://console.aws.amazon.com/cloudwatch/). |
| 9 | + |
| 10 | +**Key features** |
| 11 | + |
| 12 | +* Aggregate up to 100 metrics using a single CloudWatch EMF object (large JSON blob) |
| 13 | +* Validate against common metric definitions mistakes (metric unit, values, max dimensions, max metrics, etc) |
| 14 | +* Metrics are created asynchronously by the CloudWatch service, no custom stacks needed |
| 15 | +* Context manager to create a one off metric with a different dimension |
| 16 | + |
| 17 | +## Initialization |
| 18 | + |
| 19 | +Set `POWERTOOLS_SERVICE_NAME` and `POWERTOOLS_METRICS_NAMESPACE` env vars as a start - Here is an example using AWS Serverless Application Model (SAM) |
| 20 | + |
| 21 | +```yaml:title=template.yaml |
| 22 | +Resources: |
| 23 | + HelloWorldFunction: |
| 24 | + Type: AWS::Serverless::Function |
| 25 | + Properties: |
| 26 | + ... |
| 27 | + Runtime: java8 |
| 28 | + Environment: |
| 29 | + Variables: |
| 30 | + POWERTOOLS_SERVICE_NAME: payment # highlight-line |
| 31 | + POWERTOOLS_METRICS_NAMESPACE: ServerlessAirline # highlight-line |
| 32 | +``` |
| 33 | + |
| 34 | +We recommend you use your application or main service as a metric namespace. |
| 35 | +You can explicitly set a namespace name an annotation variable `namespace` param or via `POWERTOOLS_METRICS_NAMESPACE` env var. |
| 36 | + |
| 37 | +This sets **namespace** key that will be used for all metrics. |
| 38 | +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. |
| 39 | + |
| 40 | +```java:title=Handler.java |
| 41 | +package example; |
| 42 | + |
| 43 | +import com.amazonaws.services.lambda.runtime.Context; |
| 44 | +import com.amazonaws.services.lambda.runtime.RequestHandler; |
| 45 | +import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; |
| 46 | +import software.amazon.cloudwatchlogs.emf.model.Unit; |
| 47 | +import software.amazon.lambda.powertools.metrics.PowertoolsMetrics; |
| 48 | +import software.amazon.lambda.powertools.metrics.PowertoolsMetricsLogger; |
| 49 | + |
| 50 | +public class PowertoolsMetricsEnabledHandler implements RequestHandler<Object, Object> { |
| 51 | + |
| 52 | + MetricsLogger metricsLogger = PowertoolsMetricsLogger.metricsLogger(); |
| 53 | + |
| 54 | + @Override |
| 55 | + @PowertoolsMetrics(namespace = "ExampleApplication", service = "booking") |
| 56 | + public Object handleRequest(Object input, Context context) { |
| 57 | + ... |
| 58 | + } |
| 59 | +} |
| 60 | +``` |
| 61 | + |
| 62 | +You can initialize Metrics anywhere in your code as many times as you need - It'll keep track of your aggregate metrics in memory. |
| 63 | + |
| 64 | +## Creating metrics |
| 65 | + |
| 66 | +You can create metrics using `putMetric`, and manually create dimensions for all your aggregate metrics using `add_dimension`. |
| 67 | + |
| 68 | +```java:title=app.py |
| 69 | +public class PowertoolsMetricsEnabledHandler implements RequestHandler<Object, Object> { |
| 70 | + |
| 71 | + MetricsLogger metricsLogger = PowertoolsMetricsLogger.metricsLogger(); |
| 72 | + |
| 73 | + @Override |
| 74 | + @PowertoolsMetrics(namespace = "ExampleApplication", service = "booking") |
| 75 | + public Object handleRequest(Object input, Context context) { |
| 76 | + # highlight-start |
| 77 | + metricsLogger.putDimensions(DimensionSet.of("environment", "prod")); |
| 78 | + metricsLogger.putMetric("SuccessfulBooking", 1, Unit.COUNT); |
| 79 | + # highlight-end |
| 80 | + ... |
| 81 | + } |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +The `Unit` enum facilitate finding a supported metric unit by CloudWatch. |
| 86 | + |
| 87 | +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. |
| 88 | + |
| 89 | +## Creating a metric with a different dimension |
| 90 | + |
| 91 | +CloudWatch EMF uses the same dimensions across all your metrics. Use `single_metric` if you have a metric that should have different dimensions. |
| 92 | + |
| 93 | +<Note type="info"> |
| 94 | + 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: |
| 95 | + <br/><br/> |
| 96 | + <strong>unique metric = (metric_name + dimension_name + dimension_value)</strong> |
| 97 | +</Note><br/> |
| 98 | + |
| 99 | +```java:title=Handler.java |
| 100 | +withSingleMetric("CustomMetrics2", 1, Unit.COUNT, "Another", (metric) -> { |
| 101 | + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); |
| 102 | +}); |
| 103 | +``` |
| 104 | + |
| 105 | +## Adding metadata |
| 106 | + |
| 107 | +You can use `putMetadata` for advanced use cases, where you want to metadata as part of the serialized metrics object. |
| 108 | + |
| 109 | +<Note type="info"> |
| 110 | + <strong>This will not be available during metrics visualization</strong> - Use <strong>dimensions</strong> for this purpose |
| 111 | +</Note><br/> |
| 112 | + |
| 113 | +```javv:title=Handler.java |
| 114 | +@PowertoolsMetrics(namespace = "ServerlessAirline", service = "payment") |
| 115 | +public APIGatewayProxyResponseEvent handleRequest(Object input, Context context) { |
| 116 | + metricsLogger().putMetric("CustomMetric1", 1, Unit.COUNT); |
| 117 | + metricsLogger().putMetadata("booking_id", "1234567890"); # highlight-line |
| 118 | + |
| 119 | + ... |
| 120 | +``` |
| 121 | + |
| 122 | +This will be available in CloudWatch Logs to ease operations on high cardinal data. |
| 123 | + |
| 124 | +The `@PowertoolsMetrics` annotation **validates**, **serializes**, and **flushes** all your metrics. During metrics validation, if no metrics are provided no exception will be raised. |
| 125 | + |
| 126 | +If metrics are provided, and any of the following criteria are not met, `ValidationException` exception will be raised: |
| 127 | + |
| 128 | +* Minimum of 1 dimension |
| 129 | +* Maximum of 9 dimensions |
| 130 | + |
| 131 | +If you want to ensure that at least one metric is emitted, you can pass `raiseOnEmptyMetrics = true` to the **@PowertoolsMetrics** annotation: |
| 132 | + |
| 133 | +```java:title=Handler.java |
| 134 | + @PowertoolsMetrics(raiseOnEmptyMetrics = true) |
| 135 | + public Object handleRequest(Object input, Context context) { |
| 136 | + ... |
| 137 | +``` |
| 138 | + |
| 139 | +## Capturing cold start metric |
| 140 | + |
| 141 | +You can capture cold start metrics automatically with `@PowertoolsMetrics` via the `captureColdStart` variable. |
| 142 | + |
| 143 | +```java:title=Handler.java |
| 144 | + @PowertoolsMetrics(captureColdStart = true) |
| 145 | + public Object handleRequest(Object input, Context context) { |
| 146 | + ... |
| 147 | +``` |
| 148 | + |
| 149 | +If it's a cold start invocation, this feature will: |
| 150 | +
|
| 151 | +* Create a separate EMF blob solely containing a metric named `ColdStart` |
| 152 | +* Add `FunctionName` and `Service` dimensions |
| 153 | +
|
| 154 | +This has the advantage of keeping cold start metric separate from your application metrics. |
0 commit comments