From 4fca00a42fa36f461f329bb0844d275771298b2b Mon Sep 17 00:00:00 2001 From: Ahmed Kamel Date: Sat, 26 Nov 2022 15:19:39 +0000 Subject: [PATCH 1/5] feat(metrics): introduce MetricUtils.withMetric utility closes #999 --- docs/core/metrics.md | 24 ++++++++++++++ .../powertools/metrics/MetricsUtils.java | 20 ++++++++++++ .../powertools/metrics/MetricsLoggerTest.java | 32 +++++++++++++++++++ 3 files changed, 76 insertions(+) diff --git a/docs/core/metrics.md b/docs/core/metrics.md index 4d250df48..aaadb5296 100644 --- a/docs/core/metrics.md +++ b/docs/core/metrics.md @@ -233,4 +233,28 @@ CloudWatch EMF uses the same dimensions across all your metrics. Use `withSingle }); } } + ``` + +## Creating metrics with different configurations + +Use `withMetric` if you have one or more metrics that should have different configurations e.g. dimensions or namespace. + +=== "App.java" + + ```java hl_lines="7 8 9 10 11 12 13" + import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; + + public class App implements RequestHandler { + + @Override + public Object handleRequest(Object input, Context context) { + withMetric(metric -> { + // override default dimensions + metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); + // add metrics + metric.putMetric("CustomMetrics1", 1, Unit.COUNT); + metric.putMetric("CustomMetrics2", 5, Unit.COUNT); + }); + } + } ``` \ No newline at end of file diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java index 443ef3976..062c9f2e1 100644 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java @@ -121,6 +121,26 @@ public static void withSingleMetric(final String name, } } + /** + * Provide and immediately flush a {@link MetricsLogger}. It will use the default namespace + * specified either on {@link Metrics} annotation or via POWERTOOLS_METRICS_NAMESPACE env var. + * It by default captures function_request_id as property if used together with {@link Metrics} annotation. It will also + * capture xray_trace_id as property if tracing is enabled. + * + * @param logger the MetricsLogger + */ + public static void withMetric(final Consumer logger) { + MetricsLogger metricsLogger = logger(); + + try { + metricsLogger.setNamespace(defaultNameSpace()); + captureRequestAndTraceId(metricsLogger); + logger.accept(metricsLogger); + } finally { + metricsLogger.flush(); + } + } + public static DimensionSet[] getDefaultDimensions() { return Arrays.copyOf(defaultDimensions, defaultDimensions.length); } diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java index 449f12815..dac3dce5c 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java @@ -123,6 +123,38 @@ void singleMetricsCaptureUtilityWithDefaultNameSpace() { } } + @Test + void metricsCaptureUtilityWithDefaultNameSpace() { + try (MockedStatic mocked = mockStatic(SystemWrapper.class); + MockedStatic internalWrapper = mockStatic(software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { + mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); + mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE")).thenReturn("GlobalName"); + internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")).thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); + + MetricsUtils.withMetric(metricsLogger -> { + metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1")); + metricsLogger.putMetric("Metric1", 1, Unit.COUNT); + }); + + assertThat(out.toString()) + .satisfies(s -> { + Map logAsJson = readAsJson(s); + + assertThat(logAsJson) + .containsEntry("Metric1", 1.0) + .containsEntry("Dimension1", "Value1") + .containsKey("_aws") + .containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793"); + + Map aws = (Map) logAsJson.get("_aws"); + + assertThat(aws.get("CloudWatchMetrics")) + .asString() + .contains("Namespace=GlobalName"); + }); + } + } + @Test void shouldThrowExceptionWhenDefaultDimensionIsNull() { assertThatNullPointerException() From 6e3133ddf556d5898ce9dd1867b03b23343ec28f Mon Sep 17 00:00:00 2001 From: Ahmed Kamel Date: Sat, 26 Nov 2022 15:28:20 +0000 Subject: [PATCH 2/5] reduce duplicate code in MetricsUtils --- .../powertools/metrics/MetricsUtils.java | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java index 062c9f2e1..403b68452 100644 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java @@ -81,16 +81,10 @@ public static void withSingleMetric(final String name, final double value, final Unit unit, final Consumer logger) { - MetricsLogger metricsLogger = logger(); - - try { - metricsLogger.setNamespace(defaultNameSpace()); + withMetric(metricsLogger -> { metricsLogger.putMetric(name, value, unit); - captureRequestAndTraceId(metricsLogger); logger.accept(metricsLogger); - } finally { - metricsLogger.flush(); - } + }); } /** @@ -109,16 +103,11 @@ public static void withSingleMetric(final String name, final Unit unit, final String namespace, final Consumer logger) { - MetricsLogger metricsLogger = logger(); - - try { + withMetric(metricsLogger -> { metricsLogger.setNamespace(namespace); metricsLogger.putMetric(name, value, unit); - captureRequestAndTraceId(metricsLogger); logger.accept(metricsLogger); - } finally { - metricsLogger.flush(); - } + }); } /** From 28707e0ed2448d7c73219cce8f96385e8a78e7a9 Mon Sep 17 00:00:00 2001 From: Ahmed Kamel Date: Fri, 13 Jan 2023 15:58:29 +0000 Subject: [PATCH 3/5] Update metrics.md --- docs/core/metrics.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/core/metrics.md b/docs/core/metrics.md index aaadb5296..213beb494 100644 --- a/docs/core/metrics.md +++ b/docs/core/metrics.md @@ -242,7 +242,7 @@ Use `withMetric` if you have one or more metrics that should have different conf === "App.java" ```java hl_lines="7 8 9 10 11 12 13" - import static software.amazon.lambda.powertools.metrics.MetricsUtils.withSingleMetric; + import static software.amazon.lambda.powertools.metrics.MetricsUtils.withMetric; public class App implements RequestHandler { @@ -257,4 +257,4 @@ Use `withMetric` if you have one or more metrics that should have different conf }); } } - ``` \ No newline at end of file + ``` From eaf704e3d5a0840a6a8f6618e4ba6ebb9f641c79 Mon Sep 17 00:00:00 2001 From: Ahmed Kamel Date: Wed, 15 Feb 2023 15:53:04 +0000 Subject: [PATCH 4/5] Rename withMetric to withMetricsLogger --- docs/core/metrics.md | 10 +++++----- .../amazon/lambda/powertools/metrics/MetricsUtils.java | 8 ++++---- .../lambda/powertools/metrics/MetricsLoggerTest.java | 9 ++++----- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/docs/core/metrics.md b/docs/core/metrics.md index 213beb494..1fe5db7d5 100644 --- a/docs/core/metrics.md +++ b/docs/core/metrics.md @@ -242,18 +242,18 @@ Use `withMetric` if you have one or more metrics that should have different conf === "App.java" ```java hl_lines="7 8 9 10 11 12 13" - import static software.amazon.lambda.powertools.metrics.MetricsUtils.withMetric; + import static software.amazon.lambda.powertools.metrics.MetricsUtils.withMetricsLogger; public class App implements RequestHandler { @Override public Object handleRequest(Object input, Context context) { - withMetric(metric -> { + withMetricsLogger(logger -> { // override default dimensions - metric.setDimensions(DimensionSet.of("AnotherService", "CustomService")); + logger.setDimensions(DimensionSet.of("AnotherService", "CustomService")); // add metrics - metric.putMetric("CustomMetrics1", 1, Unit.COUNT); - metric.putMetric("CustomMetrics2", 5, Unit.COUNT); + logger.putMetric("CustomMetrics1", 1, Unit.COUNT); + logger.putMetric("CustomMetrics2", 5, Unit.COUNT); }); } } diff --git a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java index 403b68452..d58dd52d6 100644 --- a/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java +++ b/powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java @@ -81,7 +81,7 @@ public static void withSingleMetric(final String name, final double value, final Unit unit, final Consumer logger) { - withMetric(metricsLogger -> { + withMetricLogger(metricsLogger -> { metricsLogger.putMetric(name, value, unit); logger.accept(metricsLogger); }); @@ -103,7 +103,7 @@ public static void withSingleMetric(final String name, final Unit unit, final String namespace, final Consumer logger) { - withMetric(metricsLogger -> { + withMetricLogger(metricsLogger -> { metricsLogger.setNamespace(namespace); metricsLogger.putMetric(name, value, unit); logger.accept(metricsLogger); @@ -111,14 +111,14 @@ public static void withSingleMetric(final String name, } /** - * Provide and immediately flush a {@link MetricsLogger}. It will use the default namespace + * Provide and immediately flush a {@link MetricsLogger}. It uses the default namespace * specified either on {@link Metrics} annotation or via POWERTOOLS_METRICS_NAMESPACE env var. * It by default captures function_request_id as property if used together with {@link Metrics} annotation. It will also * capture xray_trace_id as property if tracing is enabled. * * @param logger the MetricsLogger */ - public static void withMetric(final Consumer logger) { + public static void withMetricLogger(final Consumer logger) { MetricsLogger metricsLogger = logger(); try { diff --git a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java index dac3dce5c..20f56c3e2 100644 --- a/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java +++ b/powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java @@ -6,7 +6,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -16,9 +15,9 @@ import software.amazon.cloudwatchlogs.emf.model.DimensionSet; import software.amazon.cloudwatchlogs.emf.model.Unit; -import static java.util.Collections.*; -import static org.assertj.core.api.Assertions.*; +import static java.util.Collections.emptyMap; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; import static org.mockito.Mockito.mockStatic; import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv; @@ -124,14 +123,14 @@ void singleMetricsCaptureUtilityWithDefaultNameSpace() { } @Test - void metricsCaptureUtilityWithDefaultNameSpace() { + void metricsLoggerCaptureUtilityWithDefaultNameSpace() { try (MockedStatic mocked = mockStatic(SystemWrapper.class); MockedStatic internalWrapper = mockStatic(software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) { mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda"); mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE")).thenReturn("GlobalName"); internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")).thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\""); - MetricsUtils.withMetric(metricsLogger -> { + MetricsUtils.withMetricLogger(metricsLogger -> { metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1")); metricsLogger.putMetric("Metric1", 1, Unit.COUNT); }); From 7bebd9c213c5cc9d86a5e4a108bda9fdcedaeed3 Mon Sep 17 00:00:00 2001 From: Ahmed Kamel Date: Wed, 15 Feb 2023 18:28:15 +0000 Subject: [PATCH 5/5] Update metrics.md --- docs/core/metrics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/core/metrics.md b/docs/core/metrics.md index 1fe5db7d5..f84508669 100644 --- a/docs/core/metrics.md +++ b/docs/core/metrics.md @@ -237,7 +237,7 @@ CloudWatch EMF uses the same dimensions across all your metrics. Use `withSingle ## Creating metrics with different configurations -Use `withMetric` if you have one or more metrics that should have different configurations e.g. dimensions or namespace. +Use `withMetricsLogger` if you have one or more metrics that should have different configurations e.g. dimensions or namespace. === "App.java"