From 861845db75e89f9df7d154d6bcb63fd168cc8a69 Mon Sep 17 00:00:00 2001 From: Steve Hu Date: Thu, 5 Sep 2024 08:38:18 -0400 Subject: [PATCH] fixes #117 Add CloudWatchMetricsMiddleware for metrics reporting --- pom.xml | 6 + .../metrics/APMMetricsMiddleware.java | 170 ------------------ .../metrics/AbstractMetricsMiddleware.java | 104 +++-------- .../metrics/CloudWatchMetricsMiddleware.java | 152 ++++++++++++++++ .../metrics/InfluxMetricsMiddleware.java | 46 ----- .../proxy/LambdaProxyMiddleware.java | 4 +- .../router/LambdaRouterMiddleware.java | 6 +- src/main/resources/config/values.yml | 3 +- src/test/resources/config/values.yml | 2 +- 9 files changed, 187 insertions(+), 306 deletions(-) delete mode 100644 src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/APMMetricsMiddleware.java create mode 100644 src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/CloudWatchMetricsMiddleware.java delete mode 100644 src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/InfluxMetricsMiddleware.java diff --git a/pom.xml b/pom.xml index 293ec00..4bd6257 100644 --- a/pom.xml +++ b/pom.xml @@ -86,6 +86,7 @@ 1.2.3 3.11.6 2.26.4 + 4.2.0 1.12.667 1.12.745 0.9.6 @@ -303,6 +304,11 @@ aws-lambda-java-core ${version.lambda-core} + + software.amazon.cloudwatchlogs + aws-embedded-metrics + ${version.aws-metrics} + com.amazonaws aws-java-sdk-dynamodb diff --git a/src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/APMMetricsMiddleware.java b/src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/APMMetricsMiddleware.java deleted file mode 100644 index 43522ca..0000000 --- a/src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/APMMetricsMiddleware.java +++ /dev/null @@ -1,170 +0,0 @@ -package com.networknt.aws.lambda.handler.middleware.metrics; - -import com.networknt.aws.lambda.LightLambdaExchange; -import com.networknt.aws.lambda.app.LambdaAppConfig; -import com.networknt.config.Config; -import com.networknt.config.JsonMapper; -import com.networknt.metrics.APMAgentReporter; -import com.networknt.metrics.MetricsConfig; -import com.networknt.metrics.TimeSeriesDbSender; -import com.networknt.status.Status; -import com.networknt.utility.Constants; -import com.networknt.utility.ModuleRegistry; -import io.dropwizard.metrics.Clock; -import io.dropwizard.metrics.MetricFilter; -import io.dropwizard.metrics.MetricName; -import io.dropwizard.metrics.MetricRegistry; -import io.dropwizard.metrics.broadcom.APMEPAgentSender; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.MalformedURLException; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static com.networknt.aws.lambda.handler.middleware.audit.AuditMiddleware.AUDIT_ATTACHMENT_KEY; - -public class APMMetricsMiddleware extends AbstractMetricsMiddleware { - static final Logger logger = LoggerFactory.getLogger(APMMetricsMiddleware.class); - public static final LambdaAppConfig LAMBDA_APP_CONFIG = (LambdaAppConfig) Config.getInstance().getJsonObjectConfig(LambdaAppConfig.CONFIG_NAME, LambdaAppConfig.class); - // this is the indicator to start the reporter and construct the common tags. It cannot be static as - // the currentPort and currentAddress are not available during the handler initialization. - private boolean firstTime = true; - private long startTime; - - public APMMetricsMiddleware() { - config = MetricsConfig.load(); - if(config.getIssuerRegex() != null) { - pattern = Pattern.compile(config.getIssuerRegex()); - } - ModuleRegistry.registerModule(MetricsConfig.CONFIG_NAME, APMMetricsMiddleware.class.getName(), Config.getNoneDecryptedInstance().getJsonMapConfigNoCache(MetricsConfig.CONFIG_NAME), null); - if(logger.isDebugEnabled()) logger.debug("ApmMetricsMiddleware is constructed!"); - } - - @Override - public Status execute(LightLambdaExchange exchange) { - if (firstTime) { - commonTags.put("api", LAMBDA_APP_CONFIG.getLambdaAppId()); -// commonTags.put("env", ); -// commonTags.put("addr", Server.currentAddress); -// commonTags.put("port", "" + (ServerConfig.getInstance().isEnableHttps() ? Server.currentHttpsPort : Server.currentHttpPort)); -// InetAddress inetAddress = Util.getInetAddress(); -// commonTags.put("host", inetAddress == null ? "unknown" : inetAddress.getHostName()); // will be container id if in docker. - if (logger.isDebugEnabled()) { - logger.debug(commonTags.toString()); - } - - try { - TimeSeriesDbSender sender = - new APMEPAgentSender(config.getServerProtocol(), config.getServerHost(), config.getServerPort(), config.getServerPath(), LAMBDA_APP_CONFIG.getLambdaAppId(), config.getProductName()); - APMAgentReporter reporter = APMAgentReporter - .forRegistry(registry) - .convertRatesTo(TimeUnit.SECONDS) - .convertDurationsTo(TimeUnit.MILLISECONDS) - .filter(MetricFilter.ALL) - .build(sender); - reporter.start(config.getReportInMinutes(), TimeUnit.MINUTES); - - logger.info("apmmetrics is enabled and reporter is started"); - } catch (MalformedURLException e) { - logger.error("apmmetrics has failed to initialize APMEPAgentSender", e); - } - - // reset the flag so that this block will only be called once. - firstTime = false; - } - - if(exchange.isRequestInProgress()) { - startTime = Clock.defaultClock().getTick(); - } - - exchange.addResponseCompleteListener(finalExchange -> { - Map auditInfo = (Map)finalExchange.getAttachment(AUDIT_ATTACHMENT_KEY); - if(logger.isTraceEnabled()) logger.trace("auditInfo = {}", auditInfo); - if (auditInfo != null && !auditInfo.isEmpty()) { - Map tags = new HashMap<>(); - tags.put("endpoint", (String) auditInfo.get(Constants.ENDPOINT_STRING)); - String clientId = auditInfo.get(Constants.CLIENT_ID_STRING) != null ? (String) auditInfo.get(Constants.CLIENT_ID_STRING) : "unknown"; - if(logger.isTraceEnabled()) logger.trace("clientId = {}", clientId); - tags.put("clientId", clientId); - // scope client id will only be available if two token is used. For example, authorization code flow. - if (config.isSendScopeClientId()) { - tags.put("scopeClientId", auditInfo.get(Constants.SCOPE_CLIENT_ID_STRING) != null ? (String) auditInfo.get(Constants.SCOPE_CLIENT_ID_STRING) : "unknown"); - } - // caller id is the calling serviceId that is passed from the caller. It is not always available but some organizations enforce it. - if (config.isSendCallerId()) { - tags.put("callerId", auditInfo.get(Constants.CALLER_ID_STRING) != null ? (String) auditInfo.get(Constants.CALLER_ID_STRING) : "unknown"); - } - if (config.isSendIssuer()) { - String issuer = (String) auditInfo.get(Constants.ISSUER_CLAIMS); - if (issuer != null) { - // we need to send issuer as a tag. Do we need to apply regex to extract only a part of the issuer? - if(config.getIssuerRegex() != null) { - Matcher matcher = pattern.matcher(issuer); - if (matcher.find()) { - String iss = matcher.group(1); - if(logger.isTraceEnabled()) logger.trace("Extracted issuer {} from Original issuer {] is sent.", iss, issuer); - tags.put("issuer", iss != null ? iss : "unknown"); - } - } else { - if(logger.isTraceEnabled()) logger.trace("Original issuer {} is sent.", issuer); - tags.put("issuer", issuer); - } - } - } - MetricName metricName = new MetricName("response_time"); - metricName = metricName.tagged(commonTags); - metricName = metricName.tagged(tags); - long time = Clock.defaultClock().getTick() - startTime; - registry.getOrAdd(metricName, MetricRegistry.MetricBuilder.TIMERS).update(time, TimeUnit.NANOSECONDS); - if(logger.isTraceEnabled()) - logger.trace("metricName = {} commonTags = {} tags = {}", metricName, JsonMapper.toJson(commonTags), JsonMapper.toJson(tags)); - incCounterForStatusCode(finalExchange.getFinalizedResponse(true).getStatusCode(), commonTags, tags); - } else { - // when we reach here, it will be in light-gateway so no specification is loaded on the server and also the security verification is failed. - // we need to come up with the endpoint at last to ensure we have some meaningful metrics info populated. - logger.error("auditInfo is null or empty. Please move the path prefix handler to the top of the handler chain after metrics."); - } - }); - return successMiddlewareStatus(); - } - - @Override - public void register() { - ModuleRegistry.registerModule( - MetricsConfig.CONFIG_NAME, - APMMetricsMiddleware.class.getName(), - Config.getNoneDecryptedInstance().getJsonMapConfigNoCache(MetricsConfig.CONFIG_NAME), - null - ); - - } - - @Override - public void reload() { - - } - - @Override - public boolean isAsynchronous() { - return false; - } - - @Override - public boolean isContinueOnFailure() { - return false; - } - - @Override - public boolean isAudited() { - return false; - } - - @Override - public void getCachedConfigurations() { - - } -} diff --git a/src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/AbstractMetricsMiddleware.java b/src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/AbstractMetricsMiddleware.java index 376f151..04972ed 100644 --- a/src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/AbstractMetricsMiddleware.java +++ b/src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/AbstractMetricsMiddleware.java @@ -2,17 +2,22 @@ import com.networknt.aws.lambda.handler.MiddlewareHandler; import com.networknt.aws.lambda.LightLambdaExchange; +import com.networknt.aws.lambda.handler.middleware.audit.AuditMiddleware; import com.networknt.config.JsonMapper; import com.networknt.metrics.JVMMetricsDbReporter; import com.networknt.metrics.MetricsConfig; import com.networknt.metrics.TimeSeriesDbSender; import com.networknt.status.Status; import com.networknt.utility.Constants; +import io.dropwizard.metrics.Metric; import io.dropwizard.metrics.MetricFilter; import io.dropwizard.metrics.MetricName; import io.dropwizard.metrics.MetricRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; +import software.amazon.cloudwatchlogs.emf.model.MetricsContext; +import software.amazon.cloudwatchlogs.emf.model.Unit; import java.util.HashMap; import java.util.Map; @@ -24,43 +29,28 @@ public abstract class AbstractMetricsMiddleware implements MiddlewareHandler { static final Logger logger = LoggerFactory.getLogger(AbstractMetricsMiddleware.class); + public static final LightLambdaExchange.Attachable METRICS_LOGGER_ATTACHMENT_KEY = LightLambdaExchange.Attachable.createAttachable(AbstractMetricsMiddleware.class); // The metrics.yml configuration that supports reload. public static MetricsConfig config; - static Pattern pattern; - // The structure that collect all the metrics entries. Even others will be using this structure to inject. - public static final MetricRegistry registry = new MetricRegistry(); - public Map commonTags = new HashMap<>(); public AbstractMetricsMiddleware() { } - @Override public boolean isEnabled() { return config.isEnabled(); } - public void createJVMMetricsReporter(final TimeSeriesDbSender sender) { - JVMMetricsDbReporter jvmReporter = new JVMMetricsDbReporter(new MetricRegistry(), sender, "jvm-reporter", - MetricFilter.ALL, TimeUnit.SECONDS, TimeUnit.MILLISECONDS, commonTags); - jvmReporter.start(config.getReportInMinutes(), TimeUnit.MINUTES); - } - - public void incCounterForStatusCode(int statusCode, Map commonTags, Map tags) { - MetricName metricName = new MetricName("request").tagged(commonTags).tagged(tags); - registry.getOrAdd(metricName, MetricRegistry.MetricBuilder.COUNTERS).inc(); + public void incCounterForStatusCode(MetricsLogger metricsLogger, int statusCode) { + metricsLogger.putMetric("request", 1, Unit.COUNT); if (statusCode >= 200 && statusCode < 400) { - metricName = new MetricName("success").tagged(commonTags).tagged(tags); - registry.getOrAdd(metricName, MetricRegistry.MetricBuilder.COUNTERS).inc(); + metricsLogger.putMetric("success", 1, Unit.COUNT); } else if (statusCode == 401 || statusCode == 403) { - metricName = new MetricName("auth_error").tagged(commonTags).tagged(tags); - registry.getOrAdd(metricName, MetricRegistry.MetricBuilder.COUNTERS).inc(); + metricsLogger.putMetric("auth_error", 1, Unit.COUNT); } else if (statusCode >= 400 && statusCode < 500) { - metricName = new MetricName("request_error").tagged(commonTags).tagged(tags); - registry.getOrAdd(metricName, MetricRegistry.MetricBuilder.COUNTERS).inc(); + metricsLogger.putMetric("request_error", 1, Unit.COUNT); } else if (statusCode >= 500) { - metricName = new MetricName("server_error").tagged(commonTags).tagged(tags); - registry.getOrAdd(metricName, MetricRegistry.MetricBuilder.COUNTERS).inc(); + metricsLogger.putMetric("server_error", 1, Unit.COUNT); } } @@ -70,69 +60,17 @@ public void incCounterForStatusCode(int statusCode, Map commonTa * @param exchange the LightLambdaExchange that is used to get the auditInfo to collect the metrics tag. * @param startTime the start time passed in to calculate the response time. * @param metricsName the name of the metrics that is collected. - * @param endpoint the endpoint that is used to collect the metrics. It is optional and only provided by the external handlers. */ - public void injectMetrics(LightLambdaExchange exchange, long startTime, String metricsName, String endpoint) { - Map auditInfo = (Map)exchange.getAttachment(AUDIT_ATTACHMENT_KEY); - if(logger.isTraceEnabled()) logger.trace("auditInfo = " + auditInfo); - Map tags = new HashMap<>(); - if (auditInfo != null) { - // for external handlers, the endpoint must be unknown in the auditInfo. If that is the case, use the endpoint passed in. - if (endpoint != null) { - tags.put(Constants.ENDPOINT_STRING, endpoint); - } else { - tags.put(Constants.ENDPOINT_STRING, (String) auditInfo.get(Constants.ENDPOINT_STRING)); - } - String clientId = auditInfo.get(Constants.CLIENT_ID_STRING) != null ? (String) auditInfo.get(Constants.CLIENT_ID_STRING) : "unknown"; - if(logger.isTraceEnabled()) logger.trace("clientId = {}", clientId); - tags.put("clientId", clientId); - // scope client id will only be available if two token is used. For example, authorization code flow. - if (config.isSendScopeClientId()) { - tags.put("scopeClientId", auditInfo.get(Constants.SCOPE_CLIENT_ID_STRING) != null ? (String) auditInfo.get(Constants.SCOPE_CLIENT_ID_STRING) : "unknown"); - } - // caller id is the calling serviceId that is passed from the caller. It is not always available but some organizations enforce it. - if (config.isSendCallerId()) { - tags.put("callerId", auditInfo.get(Constants.CALLER_ID_STRING) != null ? (String) auditInfo.get(Constants.CALLER_ID_STRING) : "unknown"); - } - if (config.isSendIssuer()) { - String issuer = (String) auditInfo.get(Constants.ISSUER_CLAIMS); - if (issuer != null) { - // we need to send issuer as a tag. Do we need to apply regex to extract only a part of the issuer? - if(config.getIssuerRegex() != null) { - Matcher matcher = pattern.matcher(issuer); - if (matcher.find()) { - String iss = matcher.group(1); - if(logger.isTraceEnabled()) logger.trace("Extracted issuer {} from Original issuer {] is sent.", iss, issuer); - tags.put("issuer", iss != null ? iss : "unknown"); - } - } else { - if(logger.isTraceEnabled()) logger.trace("Original issuer {} is sent.", issuer); - tags.put("issuer", issuer); - } - } - } - } else { - // for MRAS and Salesforce handlers that do not have auditInfo in the exchange as they may be called anonymously. - tags.put(Constants.ENDPOINT_STRING, endpoint == null ? "unknown" : endpoint); - tags.put("clientId", "unknown"); - if (config.isSendScopeClientId()) { - tags.put("scopeClientId", "unknown"); - } - if (config.isSendCallerId()) { - tags.put("callerId", "unknown"); - } - if (config.isSendIssuer()) { - tags.put("issuer", "unknown"); - } + public void injectMetrics(LightLambdaExchange exchange, long startTime, String metricsName) { + MetricsLogger metricsLogger = (exchange.getAttachment(METRICS_LOGGER_ATTACHMENT_KEY) != null) ? (MetricsLogger) exchange.getAttachment(METRICS_LOGGER_ATTACHMENT_KEY) : null; + if(metricsLogger == null) { + if(logger.isTraceEnabled()) logger.trace("metricsContext is null, create one."); + metricsLogger = new MetricsLogger(); + exchange.addAttachment(METRICS_LOGGER_ATTACHMENT_KEY, metricsLogger); } - MetricName metricName = new MetricName(metricsName); - metricName = metricName.tagged(commonTags); - metricName = metricName.tagged(tags); - long time = System.nanoTime() - startTime; - registry.getOrAdd(metricName, MetricRegistry.MetricBuilder.TIMERS).update(time, TimeUnit.NANOSECONDS); + long time = System.currentTimeMillis() - startTime; + metricsLogger.putMetric(metricsName, time, Unit.MILLISECONDS); if(logger.isTraceEnabled()) - logger.trace("metricName = {} commonTags = {} tags = {}", metricName, JsonMapper.toJson(commonTags), JsonMapper.toJson(tags)); - // the metrics handler will collect the status code metrics and increase the counter. Here we don't want to increase it again. - // incCounterForStatusCode(httpServerExchange.getStatusCode(), commonTags, tags); + logger.trace("metricName {} is injected with time {}", metricsName, time); } } diff --git a/src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/CloudWatchMetricsMiddleware.java b/src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/CloudWatchMetricsMiddleware.java new file mode 100644 index 0000000..e427338 --- /dev/null +++ b/src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/CloudWatchMetricsMiddleware.java @@ -0,0 +1,152 @@ +package com.networknt.aws.lambda.handler.middleware.metrics; + +import com.networknt.aws.lambda.LightLambdaExchange; +import com.networknt.aws.lambda.app.LambdaAppConfig; +import software.amazon.cloudwatchlogs.emf.logger.MetricsLogger; +import software.amazon.cloudwatchlogs.emf.model.DimensionSet; +import software.amazon.cloudwatchlogs.emf.model.Unit; +import com.networknt.config.Config; +import com.networknt.metrics.MetricsConfig; +import com.networknt.status.Status; +import com.networknt.utility.Constants; +import com.networknt.utility.ModuleRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.networknt.aws.lambda.handler.middleware.audit.AuditMiddleware.AUDIT_ATTACHMENT_KEY; + +public class CloudWatchMetricsMiddleware extends AbstractMetricsMiddleware { + static final Logger logger = LoggerFactory.getLogger(CloudWatchMetricsMiddleware.class); + public static final LambdaAppConfig LAMBDA_APP_CONFIG = (LambdaAppConfig) Config.getInstance().getJsonObjectConfig(LambdaAppConfig.CONFIG_NAME, LambdaAppConfig.class); + static Pattern pattern; + private long startTime; + + public CloudWatchMetricsMiddleware() { + config = MetricsConfig.load(); + if(config.getIssuerRegex() != null) { + pattern = Pattern.compile(config.getIssuerRegex()); + } + ModuleRegistry.registerModule(MetricsConfig.CONFIG_NAME, CloudWatchMetricsMiddleware.class.getName(), Config.getNoneDecryptedInstance().getJsonMapConfigNoCache(MetricsConfig.CONFIG_NAME), null); + if(logger.isDebugEnabled()) logger.debug("CloudWatchMetricsMiddleware is constructed!"); + + } + + @Override + public Status execute(LightLambdaExchange exchange) { + if(exchange.isRequestInProgress()) { + // this is in the request chain time. + startTime = System.currentTimeMillis(); + } + + exchange.addResponseCompleteListener(finalExchange -> { + MetricsLogger metricsLogger = (exchange.getAttachment(METRICS_LOGGER_ATTACHMENT_KEY) != null) ? (MetricsLogger) exchange.getAttachment(METRICS_LOGGER_ATTACHMENT_KEY) : null; + if(metricsLogger == null) { + if(logger.isTraceEnabled()) logger.trace("metricsLogger is null, create one."); + metricsLogger = new MetricsLogger(); + exchange.addAttachment(METRICS_LOGGER_ATTACHMENT_KEY, metricsLogger); + } + + // this is in the response chain. + Map auditInfo = (Map)finalExchange.getAttachment(AUDIT_ATTACHMENT_KEY); + if(logger.isTraceEnabled()) logger.trace("auditInfo = {}", auditInfo); + if (auditInfo != null && !auditInfo.isEmpty()) { + Map tags = new HashMap<>(); + metricsLogger.putProperty("endpoint", auditInfo.get(Constants.ENDPOINT_STRING)); + String clientId = auditInfo.get(Constants.CLIENT_ID_STRING) != null ? (String) auditInfo.get(Constants.CLIENT_ID_STRING) : "unknown"; + if (logger.isTraceEnabled()) logger.trace("clientId = {}", clientId); + metricsLogger.putProperty("clientId", clientId); + // scope client id will only be available if two token is used. For example, authorization code flow. + if (config.isSendScopeClientId()) { + metricsLogger.putProperty("scopeClientId", auditInfo.get(Constants.SCOPE_CLIENT_ID_STRING) != null ? auditInfo.get(Constants.SCOPE_CLIENT_ID_STRING) : "unknown"); + } + // caller id is the calling serviceId that is passed from the caller. It is not always available but some organizations enforce it. + if (config.isSendCallerId()) { + metricsLogger.putProperty("callerId", auditInfo.get(Constants.CALLER_ID_STRING) != null ? auditInfo.get(Constants.CALLER_ID_STRING) : "unknown"); + } + if (config.isSendIssuer()) { + String issuer = (String) auditInfo.get(Constants.ISSUER_CLAIMS); + if (issuer != null) { + // we need to send issuer as a tag. Do we need to apply regex to extract only a part of the issuer? + if (config.getIssuerRegex() != null) { + Matcher matcher = pattern.matcher(issuer); + if (matcher.find()) { + String iss = matcher.group(1); + if (logger.isTraceEnabled()) + logger.trace("Extracted issuer {} from Original issuer {} is sent.", iss, issuer); + metricsLogger.putProperty("issuer", iss != null ? iss : "unknown"); + } + } else { + if (logger.isTraceEnabled()) logger.trace("Original issuer {} is sent.", issuer); + metricsLogger.putProperty("issuer", issuer); + } + } + } + } else { + // when we reach here, it will be in an instance without specification is loaded on the server and also + // the security verification is failed or disabled. + // we need to come up with the endpoint at last to ensure we have some meaningful metrics info populated. + metricsLogger.putProperty(Constants.ENDPOINT_STRING, "unknown"); + metricsLogger.putProperty("clientId", "unknown"); + if (config.isSendScopeClientId()) { + metricsLogger.putProperty("scopeClientId", "unknown"); + } + if (config.isSendCallerId()) { + metricsLogger.putProperty("callerId", "unknown"); + } + if (config.isSendIssuer()) { + metricsLogger.putProperty("issuer", "unknown"); + } + logger.error("auditInfo is null or empty. Please move the path prefix handler to the top of the handler chain after metrics."); + } + + long time = System.currentTimeMillis() - startTime; + metricsLogger.putMetric("response_time", time, Unit.MILLISECONDS); + incCounterForStatusCode(metricsLogger, finalExchange.getFinalizedResponse(true).getStatusCode()); + metricsLogger.putDimensions(DimensionSet.of("Service", "Aggregator")); + metricsLogger.putProperty("api", LAMBDA_APP_CONFIG.getLambdaAppId()); + metricsLogger.flush(); + }); + return successMiddlewareStatus(); + } + + @Override + public boolean isContinueOnFailure() { + return false; + } + + @Override + public boolean isAudited() { + return false; + } + + @Override + public void getCachedConfigurations() { + + } + + @Override + public void register() { + ModuleRegistry.registerModule( + MetricsConfig.CONFIG_NAME, + CloudWatchMetricsMiddleware.class.getName(), + Config.getNoneDecryptedInstance().getJsonMapConfigNoCache(MetricsConfig.CONFIG_NAME), + null + ); + + } + + @Override + public void reload() { + + } + + @Override + public boolean isAsynchronous() { + return false; + } +} diff --git a/src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/InfluxMetricsMiddleware.java b/src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/InfluxMetricsMiddleware.java deleted file mode 100644 index d431f36..0000000 --- a/src/main/java/com/networknt/aws/lambda/handler/middleware/metrics/InfluxMetricsMiddleware.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.networknt.aws.lambda.handler.middleware.metrics; - -import com.networknt.aws.lambda.LightLambdaExchange; -import com.networknt.status.Status; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class InfluxMetricsMiddleware extends AbstractMetricsMiddleware { - private static final Logger LOG = LoggerFactory.getLogger(InfluxMetricsMiddleware.class); - - @Override - public Status execute(LightLambdaExchange exchange) { - if(LOG.isDebugEnabled()) LOG.debug("InfluxMetricsMiddleware.execute starts."); - return null; - } - - @Override - public void register() { - - } - - @Override - public void reload() { - - } - - @Override - public boolean isAsynchronous() { - return false; - } - - @Override - public boolean isContinueOnFailure() { - return false; - } - - @Override - public boolean isAudited() { - return false; - } - - @Override - public void getCachedConfigurations() { - - } -} diff --git a/src/main/java/com/networknt/aws/lambda/handler/middleware/proxy/LambdaProxyMiddleware.java b/src/main/java/com/networknt/aws/lambda/handler/middleware/proxy/LambdaProxyMiddleware.java index 8154e91..b301560 100644 --- a/src/main/java/com/networknt/aws/lambda/handler/middleware/proxy/LambdaProxyMiddleware.java +++ b/src/main/java/com/networknt/aws/lambda/handler/middleware/proxy/LambdaProxyMiddleware.java @@ -116,14 +116,14 @@ private String invokeFunction(final LambdaAsyncClient client, String functionNam .logType(CONFIG.getLogType()) .payload(payload) .build(); - long startTime = System.nanoTime(); + long startTime = System.currentTimeMillis(); CompletableFuture futureResponse = client.invoke(request) .thenApply(res -> { if(CONFIG.isMetricsInjection()) { if(metricsMiddleware == null) lookupMetricsMiddleware(); if(metricsMiddleware != null) { if (LOG.isTraceEnabled()) LOG.trace("Inject metrics for {}", CONFIG.getMetricsName()); - metricsMiddleware.injectMetrics(exchange, startTime, CONFIG.getMetricsName(), null); + metricsMiddleware.injectMetrics(exchange, startTime, CONFIG.getMetricsName()); } } if (LOG.isTraceEnabled()) LOG.trace("LambdaProxyMiddleware.invokeFunction response: {}", res); diff --git a/src/main/java/com/networknt/aws/lambda/handler/middleware/router/LambdaRouterMiddleware.java b/src/main/java/com/networknt/aws/lambda/handler/middleware/router/LambdaRouterMiddleware.java index 6148e35..33a0171 100644 --- a/src/main/java/com/networknt/aws/lambda/handler/middleware/router/LambdaRouterMiddleware.java +++ b/src/main/java/com/networknt/aws/lambda/handler/middleware/router/LambdaRouterMiddleware.java @@ -88,7 +88,7 @@ public Status execute(LightLambdaExchange exchange) { } if(LOG.isTraceEnabled()) LOG.trace("Discovered host {} for ServiceId {}", host, serviceId); // call the downstream service based on the request methods. - long startTime = System.nanoTime(); + long startTime = System.currentTimeMillis(); if("get".equalsIgnoreCase(method) || "delete".equalsIgnoreCase(method)) { HttpClientRequest request = new HttpClientRequest(); try { @@ -104,7 +104,7 @@ public Status execute(LightLambdaExchange exchange) { if(metricsMiddleware == null) lookupMetricsMiddleware(); if(metricsMiddleware != null) { if (LOG.isTraceEnabled()) LOG.trace("Inject metrics for {}", CONFIG.getMetricsName()); - metricsMiddleware.injectMetrics(exchange, startTime, CONFIG.getMetricsName(), null); + metricsMiddleware.injectMetrics(exchange, startTime, CONFIG.getMetricsName()); } } if(LOG.isTraceEnabled()) LOG.trace("Response: {}", JsonMapper.toJson(res)); @@ -128,7 +128,7 @@ public Status execute(LightLambdaExchange exchange) { if(metricsMiddleware == null) lookupMetricsMiddleware(); if(metricsMiddleware != null) { if (LOG.isTraceEnabled()) LOG.trace("Inject metrics for {}", CONFIG.getMetricsName()); - metricsMiddleware.injectMetrics(exchange, startTime, CONFIG.getMetricsName(), null); + metricsMiddleware.injectMetrics(exchange, startTime, CONFIG.getMetricsName()); } } if(LOG.isTraceEnabled()) LOG.trace("Response: {}", JsonMapper.toJson(res)); diff --git a/src/main/resources/config/values.yml b/src/main/resources/config/values.yml index 30d4d67..913e664 100644 --- a/src/main/resources/config/values.yml +++ b/src/main/resources/config/values.yml @@ -62,7 +62,7 @@ header.enabled: true # handler.yml handler.handlers: # Light-framework cross-cutting concerns implemented in the microservice - - com.networknt.aws.lambda.handler.middleware.metrics.APMMetricsMiddleware@metrics + - com.networknt.aws.lambda.handler.middleware.metrics.CloudWatchMetricsMiddleware@metrics - com.networknt.aws.lambda.handler.middleware.correlation.CorrelationMiddleware@correlation - com.networknt.aws.lambda.handler.middleware.traceability.TraceabilityMiddleware@traceability - com.networknt.aws.lambda.handler.middleware.header.RequestHeaderMiddleware@requestHeader @@ -87,6 +87,7 @@ handler.handlers: - com.networknt.aws.lambda.handler.info.ServerInfoHandler@info - com.networknt.aws.lambda.handler.logger.LoggerGetHandler@getLogger - com.networknt.aws.lambda.handler.logger.LoggerSetHandler@setLogger + - com.networknt.aws.lambda.handler.cache.CacheExplorerHandler@cache # client.yml client.tokenKeyServerUrl: https://networknt.oktapreview.com diff --git a/src/test/resources/config/values.yml b/src/test/resources/config/values.yml index 4821fe4..9b1bebd 100644 --- a/src/test/resources/config/values.yml +++ b/src/test/resources/config/values.yml @@ -75,7 +75,7 @@ header.pathPrefixHeader: # handler.yml handler.handlers: # Light-framework cross-cutting concerns implemented in the microservice - - com.networknt.aws.lambda.handler.middleware.metrics.APMMetricsMiddleware@metrics + - com.networknt.aws.lambda.handler.middleware.metrics.CloudWatchMetricsMiddleware@metrics - com.networknt.aws.lambda.handler.middleware.correlation.CorrelationMiddleware@correlation - com.networknt.aws.lambda.handler.middleware.traceability.TraceabilityMiddleware@traceability - com.networknt.aws.lambda.handler.middleware.header.RequestHeaderMiddleware@requestHeader