Skip to content

Commit 7dfe9c3

Browse files
authored
feat(metrics): introduce MetricsUtils.withMetricsLogger utility (#1000)
feat(metrics): introduce MetricsUtils.withMetricsLogger utility (#1000)
1 parent 1f4b89c commit 7dfe9c3

File tree

3 files changed

+78
-14
lines changed

3 files changed

+78
-14
lines changed

docs/core/metrics.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,4 +233,28 @@ CloudWatch EMF uses the same dimensions across all your metrics. Use `withSingle
233233
});
234234
}
235235
}
236-
```
236+
```
237+
238+
## Creating metrics with different configurations
239+
240+
Use `withMetricsLogger` if you have one or more metrics that should have different configurations e.g. dimensions or namespace.
241+
242+
=== "App.java"
243+
244+
```java hl_lines="7 8 9 10 11 12 13"
245+
import static software.amazon.lambda.powertools.metrics.MetricsUtils.withMetricsLogger;
246+
247+
public class App implements RequestHandler<Object, Object> {
248+
249+
@Override
250+
public Object handleRequest(Object input, Context context) {
251+
withMetricsLogger(logger -> {
252+
// override default dimensions
253+
logger.setDimensions(DimensionSet.of("AnotherService", "CustomService"));
254+
// add metrics
255+
logger.putMetric("CustomMetrics1", 1, Unit.COUNT);
256+
logger.putMetric("CustomMetrics2", 5, Unit.COUNT);
257+
});
258+
}
259+
}
260+
```

powertools-metrics/src/main/java/software/amazon/lambda/powertools/metrics/MetricsUtils.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,16 +81,10 @@ public static void withSingleMetric(final String name,
8181
final double value,
8282
final Unit unit,
8383
final Consumer<MetricsLogger> logger) {
84-
MetricsLogger metricsLogger = logger();
85-
86-
try {
87-
metricsLogger.setNamespace(defaultNameSpace());
84+
withMetricLogger(metricsLogger -> {
8885
metricsLogger.putMetric(name, value, unit);
89-
captureRequestAndTraceId(metricsLogger);
9086
logger.accept(metricsLogger);
91-
} finally {
92-
metricsLogger.flush();
93-
}
87+
});
9488
}
9589

9690
/**
@@ -109,11 +103,26 @@ public static void withSingleMetric(final String name,
109103
final Unit unit,
110104
final String namespace,
111105
final Consumer<MetricsLogger> logger) {
106+
withMetricLogger(metricsLogger -> {
107+
metricsLogger.setNamespace(namespace);
108+
metricsLogger.putMetric(name, value, unit);
109+
logger.accept(metricsLogger);
110+
});
111+
}
112+
113+
/**
114+
* Provide and immediately flush a {@link MetricsLogger}. It uses the default namespace
115+
* specified either on {@link Metrics} annotation or via POWERTOOLS_METRICS_NAMESPACE env var.
116+
* It by default captures function_request_id as property if used together with {@link Metrics} annotation. It will also
117+
* capture xray_trace_id as property if tracing is enabled.
118+
*
119+
* @param logger the MetricsLogger
120+
*/
121+
public static void withMetricLogger(final Consumer<MetricsLogger> logger) {
112122
MetricsLogger metricsLogger = logger();
113123

114124
try {
115-
metricsLogger.setNamespace(namespace);
116-
metricsLogger.putMetric(name, value, unit);
125+
metricsLogger.setNamespace(defaultNameSpace());
117126
captureRequestAndTraceId(metricsLogger);
118127
logger.accept(metricsLogger);
119128
} finally {

powertools-metrics/src/test/java/software/amazon/lambda/powertools/metrics/MetricsLoggerTest.java

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import com.fasterxml.jackson.core.JsonProcessingException;
88
import com.fasterxml.jackson.databind.ObjectMapper;
9-
import org.assertj.core.api.Assertions;
109
import org.junit.jupiter.api.AfterEach;
1110
import org.junit.jupiter.api.BeforeAll;
1211
import org.junit.jupiter.api.BeforeEach;
@@ -16,9 +15,9 @@
1615
import software.amazon.cloudwatchlogs.emf.model.DimensionSet;
1716
import software.amazon.cloudwatchlogs.emf.model.Unit;
1817

19-
import static java.util.Collections.*;
20-
import static org.assertj.core.api.Assertions.*;
18+
import static java.util.Collections.emptyMap;
2119
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
2221
import static org.mockito.Mockito.mockStatic;
2322
import static software.amazon.lambda.powertools.core.internal.SystemWrapper.getenv;
2423

@@ -123,6 +122,38 @@ void singleMetricsCaptureUtilityWithDefaultNameSpace() {
123122
}
124123
}
125124

125+
@Test
126+
void metricsLoggerCaptureUtilityWithDefaultNameSpace() {
127+
try (MockedStatic<SystemWrapper> mocked = mockStatic(SystemWrapper.class);
128+
MockedStatic<software.amazon.lambda.powertools.core.internal.SystemWrapper> internalWrapper = mockStatic(software.amazon.lambda.powertools.core.internal.SystemWrapper.class)) {
129+
mocked.when(() -> SystemWrapper.getenv("AWS_EMF_ENVIRONMENT")).thenReturn("Lambda");
130+
mocked.when(() -> SystemWrapper.getenv("POWERTOOLS_METRICS_NAMESPACE")).thenReturn("GlobalName");
131+
internalWrapper.when(() -> getenv("_X_AMZN_TRACE_ID")).thenReturn("Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1\"");
132+
133+
MetricsUtils.withMetricLogger(metricsLogger -> {
134+
metricsLogger.setDimensions(DimensionSet.of("Dimension1", "Value1"));
135+
metricsLogger.putMetric("Metric1", 1, Unit.COUNT);
136+
});
137+
138+
assertThat(out.toString())
139+
.satisfies(s -> {
140+
Map<String, Object> logAsJson = readAsJson(s);
141+
142+
assertThat(logAsJson)
143+
.containsEntry("Metric1", 1.0)
144+
.containsEntry("Dimension1", "Value1")
145+
.containsKey("_aws")
146+
.containsEntry("xray_trace_id", "1-5759e988-bd862e3fe1be46a994272793");
147+
148+
Map<String, Object> aws = (Map<String, Object>) logAsJson.get("_aws");
149+
150+
assertThat(aws.get("CloudWatchMetrics"))
151+
.asString()
152+
.contains("Namespace=GlobalName");
153+
});
154+
}
155+
}
156+
126157
@Test
127158
void shouldThrowExceptionWhenDefaultDimensionIsNull() {
128159
assertThatNullPointerException()

0 commit comments

Comments
 (0)