Skip to content

Commit 861845d

Browse files
committed
fixes #117 Add CloudWatchMetricsMiddleware for metrics reporting
1 parent 3bcd720 commit 861845d

File tree

9 files changed

+187
-306
lines changed

9 files changed

+187
-306
lines changed

pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
<version.lambda-core>1.2.3</version.lambda-core>
8787
<version.lambda-events>3.11.6</version.lambda-events>
8888
<version.lambda-awssdk>2.26.4</version.lambda-awssdk>
89+
<version.aws-metrics>4.2.0</version.aws-metrics>
8990
<version.dynamodb>1.12.667</version.dynamodb>
9091
<version.lambda-iam>1.12.745</version.lambda-iam>
9192
<version.jose4j>0.9.6</version.jose4j>
@@ -303,6 +304,11 @@
303304
<artifactId>aws-lambda-java-core</artifactId>
304305
<version>${version.lambda-core}</version>
305306
</dependency>
307+
<dependency>
308+
<groupId>software.amazon.cloudwatchlogs</groupId>
309+
<artifactId>aws-embedded-metrics</artifactId>
310+
<version>${version.aws-metrics}</version>
311+
</dependency>
306312
<dependency>
307313
<groupId>com.amazonaws</groupId>
308314
<artifactId>aws-java-sdk-dynamodb</artifactId>

src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/APMMetricsMiddleware.java

-170
This file was deleted.

src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/AbstractMetricsMiddleware.java

+21-83
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,22 @@
22

33
import com.networknt.aws.lambda.handler.MiddlewareHandler;
44
import com.networknt.aws.lambda.LightLambdaExchange;
5+
import com.networknt.aws.lambda.handler.middleware.audit.AuditMiddleware;
56
import com.networknt.config.JsonMapper;
67
import com.networknt.metrics.JVMMetricsDbReporter;
78
import com.networknt.metrics.MetricsConfig;
89
import com.networknt.metrics.TimeSeriesDbSender;
910
import com.networknt.status.Status;
1011
import com.networknt.utility.Constants;
12+
import io.dropwizard.metrics.Metric;
1113
import io.dropwizard.metrics.MetricFilter;
1214
import io.dropwizard.metrics.MetricName;
1315
import io.dropwizard.metrics.MetricRegistry;
1416
import org.slf4j.Logger;
1517
import org.slf4j.LoggerFactory;
18+
import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger;
19+
import software.amazon.cloudwatchlogs.emf.model.MetricsContext;
20+
import software.amazon.cloudwatchlogs.emf.model.Unit;
1621

1722
import java.util.HashMap;
1823
import java.util.Map;
@@ -24,43 +29,28 @@
2429

2530
public abstract class AbstractMetricsMiddleware implements MiddlewareHandler {
2631
static final Logger logger = LoggerFactory.getLogger(AbstractMetricsMiddleware.class);
32+
public static final LightLambdaExchange.Attachable<AbstractMetricsMiddleware> METRICS_LOGGER_ATTACHMENT_KEY = LightLambdaExchange.Attachable.createAttachable(AbstractMetricsMiddleware.class);
2733
// The metrics.yml configuration that supports reload.
2834
public static MetricsConfig config;
29-
static Pattern pattern;
30-
// The structure that collect all the metrics entries. Even others will be using this structure to inject.
31-
public static final MetricRegistry registry = new MetricRegistry();
32-
public Map<String, String> commonTags = new HashMap<>();
3335

3436
public AbstractMetricsMiddleware() {
3537
}
3638

37-
3839
@Override
3940
public boolean isEnabled() {
4041
return config.isEnabled();
4142
}
4243

43-
public void createJVMMetricsReporter(final TimeSeriesDbSender sender) {
44-
JVMMetricsDbReporter jvmReporter = new JVMMetricsDbReporter(new MetricRegistry(), sender, "jvm-reporter",
45-
MetricFilter.ALL, TimeUnit.SECONDS, TimeUnit.MILLISECONDS, commonTags);
46-
jvmReporter.start(config.getReportInMinutes(), TimeUnit.MINUTES);
47-
}
48-
49-
public void incCounterForStatusCode(int statusCode, Map<String, String> commonTags, Map<String, String> tags) {
50-
MetricName metricName = new MetricName("request").tagged(commonTags).tagged(tags);
51-
registry.getOrAdd(metricName, MetricRegistry.MetricBuilder.COUNTERS).inc();
44+
public void incCounterForStatusCode(MetricsLogger metricsLogger, int statusCode) {
45+
metricsLogger.putMetric("request", 1, Unit.COUNT);
5246
if (statusCode >= 200 && statusCode < 400) {
53-
metricName = new MetricName("success").tagged(commonTags).tagged(tags);
54-
registry.getOrAdd(metricName, MetricRegistry.MetricBuilder.COUNTERS).inc();
47+
metricsLogger.putMetric("success", 1, Unit.COUNT);
5548
} else if (statusCode == 401 || statusCode == 403) {
56-
metricName = new MetricName("auth_error").tagged(commonTags).tagged(tags);
57-
registry.getOrAdd(metricName, MetricRegistry.MetricBuilder.COUNTERS).inc();
49+
metricsLogger.putMetric("auth_error", 1, Unit.COUNT);
5850
} else if (statusCode >= 400 && statusCode < 500) {
59-
metricName = new MetricName("request_error").tagged(commonTags).tagged(tags);
60-
registry.getOrAdd(metricName, MetricRegistry.MetricBuilder.COUNTERS).inc();
51+
metricsLogger.putMetric("request_error", 1, Unit.COUNT);
6152
} else if (statusCode >= 500) {
62-
metricName = new MetricName("server_error").tagged(commonTags).tagged(tags);
63-
registry.getOrAdd(metricName, MetricRegistry.MetricBuilder.COUNTERS).inc();
53+
metricsLogger.putMetric("server_error", 1, Unit.COUNT);
6454
}
6555
}
6656

@@ -70,69 +60,17 @@ public void incCounterForStatusCode(int statusCode, Map<String, String> commonTa
7060
* @param exchange the LightLambdaExchange that is used to get the auditInfo to collect the metrics tag.
7161
* @param startTime the start time passed in to calculate the response time.
7262
* @param metricsName the name of the metrics that is collected.
73-
* @param endpoint the endpoint that is used to collect the metrics. It is optional and only provided by the external handlers.
7463
*/
75-
public void injectMetrics(LightLambdaExchange exchange, long startTime, String metricsName, String endpoint) {
76-
Map<String, Object> auditInfo = (Map<String, Object>)exchange.getAttachment(AUDIT_ATTACHMENT_KEY);
77-
if(logger.isTraceEnabled()) logger.trace("auditInfo = " + auditInfo);
78-
Map<String, String> tags = new HashMap<>();
79-
if (auditInfo != null) {
80-
// for external handlers, the endpoint must be unknown in the auditInfo. If that is the case, use the endpoint passed in.
81-
if (endpoint != null) {
82-
tags.put(Constants.ENDPOINT_STRING, endpoint);
83-
} else {
84-
tags.put(Constants.ENDPOINT_STRING, (String) auditInfo.get(Constants.ENDPOINT_STRING));
85-
}
86-
String clientId = auditInfo.get(Constants.CLIENT_ID_STRING) != null ? (String) auditInfo.get(Constants.CLIENT_ID_STRING) : "unknown";
87-
if(logger.isTraceEnabled()) logger.trace("clientId = {}", clientId);
88-
tags.put("clientId", clientId);
89-
// scope client id will only be available if two token is used. For example, authorization code flow.
90-
if (config.isSendScopeClientId()) {
91-
tags.put("scopeClientId", auditInfo.get(Constants.SCOPE_CLIENT_ID_STRING) != null ? (String) auditInfo.get(Constants.SCOPE_CLIENT_ID_STRING) : "unknown");
92-
}
93-
// caller id is the calling serviceId that is passed from the caller. It is not always available but some organizations enforce it.
94-
if (config.isSendCallerId()) {
95-
tags.put("callerId", auditInfo.get(Constants.CALLER_ID_STRING) != null ? (String) auditInfo.get(Constants.CALLER_ID_STRING) : "unknown");
96-
}
97-
if (config.isSendIssuer()) {
98-
String issuer = (String) auditInfo.get(Constants.ISSUER_CLAIMS);
99-
if (issuer != null) {
100-
// we need to send issuer as a tag. Do we need to apply regex to extract only a part of the issuer?
101-
if(config.getIssuerRegex() != null) {
102-
Matcher matcher = pattern.matcher(issuer);
103-
if (matcher.find()) {
104-
String iss = matcher.group(1);
105-
if(logger.isTraceEnabled()) logger.trace("Extracted issuer {} from Original issuer {] is sent.", iss, issuer);
106-
tags.put("issuer", iss != null ? iss : "unknown");
107-
}
108-
} else {
109-
if(logger.isTraceEnabled()) logger.trace("Original issuer {} is sent.", issuer);
110-
tags.put("issuer", issuer);
111-
}
112-
}
113-
}
114-
} else {
115-
// for MRAS and Salesforce handlers that do not have auditInfo in the exchange as they may be called anonymously.
116-
tags.put(Constants.ENDPOINT_STRING, endpoint == null ? "unknown" : endpoint);
117-
tags.put("clientId", "unknown");
118-
if (config.isSendScopeClientId()) {
119-
tags.put("scopeClientId", "unknown");
120-
}
121-
if (config.isSendCallerId()) {
122-
tags.put("callerId", "unknown");
123-
}
124-
if (config.isSendIssuer()) {
125-
tags.put("issuer", "unknown");
126-
}
64+
public void injectMetrics(LightLambdaExchange exchange, long startTime, String metricsName) {
65+
MetricsLogger metricsLogger = (exchange.getAttachment(METRICS_LOGGER_ATTACHMENT_KEY) != null) ? (MetricsLogger) exchange.getAttachment(METRICS_LOGGER_ATTACHMENT_KEY) : null;
66+
if(metricsLogger == null) {
67+
if(logger.isTraceEnabled()) logger.trace("metricsContext is null, create one.");
68+
metricsLogger = new MetricsLogger();
69+
exchange.addAttachment(METRICS_LOGGER_ATTACHMENT_KEY, metricsLogger);
12770
}
128-
MetricName metricName = new MetricName(metricsName);
129-
metricName = metricName.tagged(commonTags);
130-
metricName = metricName.tagged(tags);
131-
long time = System.nanoTime() - startTime;
132-
registry.getOrAdd(metricName, MetricRegistry.MetricBuilder.TIMERS).update(time, TimeUnit.NANOSECONDS);
71+
long time = System.currentTimeMillis() - startTime;
72+
metricsLogger.putMetric(metricsName, time, Unit.MILLISECONDS);
13373
if(logger.isTraceEnabled())
134-
logger.trace("metricName = {} commonTags = {} tags = {}", metricName, JsonMapper.toJson(commonTags), JsonMapper.toJson(tags));
135-
// the metrics handler will collect the status code metrics and increase the counter. Here we don't want to increase it again.
136-
// incCounterForStatusCode(httpServerExchange.getStatusCode(), commonTags, tags);
74+
logger.trace("metricName {} is injected with time {}", metricsName, time);
13775
}
13876
}

0 commit comments

Comments
 (0)