Skip to content

Commit 2ca8a71

Browse files
authored
additional cw-metrics (#298)
1 parent 04cf26f commit 2ca8a71

File tree

6 files changed

+92
-7
lines changed

6 files changed

+92
-7
lines changed

src/main/java/software/amazon/cloudformation/LambdaWrapper.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.time.Instant;
3030
import java.util.Arrays;
3131
import java.util.Date;
32+
import java.util.EnumSet;
3233
import java.util.HashMap;
3334
import java.util.List;
3435
import java.util.Map;
@@ -231,6 +232,11 @@ public void handleRequest(final InputStream inputStream, final OutputStream outp
231232
} finally {
232233
// A response will be output on all paths, though CloudFormation will
233234
// not block on invoking the handlers, but rather listen for callbacks
235+
236+
if (handlerResponse != null) {
237+
publishExceptionCodeAndCountMetric(request == null ? null : request.getAction(), handlerResponse.getErrorCode(),
238+
handlerResponse.getStatus() == OperationStatus.FAILED);
239+
}
234240
writeResponse(outputStream, handlerResponse);
235241
}
236242
}
@@ -475,6 +481,21 @@ private void publishExceptionMetric(final Action action, final Throwable ex, fin
475481
}
476482
}
477483

484+
/*
485+
* null-safe exception metrics delivery
486+
*/
487+
private void
488+
publishExceptionCodeAndCountMetric(final Action action, final HandlerErrorCode handlerErrorCode, final boolean thrown) {
489+
if (this.metricsPublisherProxy != null) {
490+
EnumSet.allOf(HandlerErrorCode.class).stream()
491+
// publishing 0 value for all (if not thrown) otherwise filtered
492+
.filter(errorCode -> errorCode.equals(handlerErrorCode) || !thrown)
493+
.forEach(errorCode -> this.metricsPublisherProxy.publishExceptionByErrorCodeMetric(Instant.now(), action,
494+
errorCode, thrown));
495+
this.metricsPublisherProxy.publishExceptionCountMetric(Instant.now(), action, thrown);
496+
}
497+
}
498+
478499
/**
479500
* null-safe logger redirect
480501
*

src/main/java/software/amazon/cloudformation/metrics/Metric.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public class Metric {
1818

1919
public static final String METRIC_NAMESPACE_ROOT = "AWS/CloudFormation";
2020
public static final String METRIC_NAME_HANDLER_EXCEPTION = "HandlerException";
21+
public static final String METRIC_NAME_HANDLER_EXCEPTION_BY_ERROR_CODE = "HandlerExceptionByErrorCode";
22+
public static final String METRIC_NAME_HANDLER_EXCEPTION_BY_EXCEPTION_COUNT = "HandlerExceptionByExceptionCount";
2123
public static final String METRIC_NAME_HANDLER_DURATION = "HandlerInvocationDuration";
2224
public static final String METRIC_NAME_HANDLER_INVOCATION_COUNT = "HandlerInvocationCount";
2325

src/main/java/software/amazon/cloudformation/metrics/MetricsPublisher.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ public void publishExceptionMetric(final Instant timestamp,
4242
final HandlerErrorCode handlerErrorCode) {
4343
}
4444

45+
public void publishExceptionByErrorCodeMetric(final Instant timestamp,
46+
final Action action,
47+
final HandlerErrorCode handlerErrorCode,
48+
final boolean thrown) {
49+
}
50+
51+
public void publishExceptionCountMetric(final Instant timestamp, final Action action, final boolean thrown) {
52+
}
53+
4554
public void publishInvocationMetric(final Instant timestamp, final Action action) {
4655
}
4756

src/main/java/software/amazon/cloudformation/metrics/MetricsPublisherImpl.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,27 @@ public void publishExceptionMetric(final Instant timestamp,
6666
publishMetric(Metric.METRIC_NAME_HANDLER_EXCEPTION, dimensions, StandardUnit.COUNT, 1.0, timestamp);
6767
}
6868

69+
@Override
70+
public void publishExceptionByErrorCodeMetric(final Instant timestamp,
71+
final Action action,
72+
final HandlerErrorCode handlerErrorCode,
73+
final boolean thrown) {
74+
Map<String, String> dimensions = new HashMap<>();
75+
dimensions.put(Metric.DIMENSION_KEY_ACTION_TYPE, action == null ? "NO_ACTION" : action.name());
76+
dimensions.put(Metric.DIMENSION_KEY_HANDLER_ERROR_CODE, handlerErrorCode.name());
77+
78+
publishMetric(Metric.METRIC_NAME_HANDLER_EXCEPTION_BY_ERROR_CODE, dimensions, StandardUnit.COUNT, thrown ? 1.0 : 0.0,
79+
timestamp);
80+
}
81+
82+
public void publishExceptionCountMetric(final Instant timestamp, final Action action, final boolean thrown) {
83+
Map<String, String> dimensions = new HashMap<>();
84+
dimensions.put(Metric.DIMENSION_KEY_ACTION_TYPE, action == null ? "NO_ACTION" : action.name());
85+
86+
publishMetric(Metric.METRIC_NAME_HANDLER_EXCEPTION_BY_EXCEPTION_COUNT, dimensions, StandardUnit.COUNT, thrown ? 1.0 : 0.0,
87+
timestamp);
88+
}
89+
6990
@Override
7091
public void publishProviderLogDeliveryExceptionMetric(final Instant timestamp, final Throwable e) {
7192
Map<String, String> dimensions = new HashMap<>();

src/main/java/software/amazon/cloudformation/proxy/MetricsPublisherProxy.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,19 @@ public void publishExceptionMetric(final Instant timestamp,
3535
.forEach(metricsPublisher -> metricsPublisher.publishExceptionMetric(timestamp, action, e, handlerErrorCode));
3636
}
3737

38+
public void publishExceptionByErrorCodeMetric(final Instant timestamp,
39+
final Action action,
40+
final HandlerErrorCode handlerErrorCode,
41+
final boolean thrown) {
42+
metricsPublishers.stream().forEach(
43+
metricsPublisher -> metricsPublisher.publishExceptionByErrorCodeMetric(timestamp, action, handlerErrorCode, thrown));
44+
}
45+
46+
public void publishExceptionCountMetric(final Instant timestamp, final Action action, final boolean thrown) {
47+
metricsPublishers.stream()
48+
.forEach(metricsPublisher -> metricsPublisher.publishExceptionCountMetric(timestamp, action, thrown));
49+
}
50+
3851
public void publishInvocationMetric(final Instant timestamp, final Action action) {
3952
metricsPublishers.stream().forEach(metricsPublisher -> metricsPublisher.publishInvocationMetric(timestamp, action));
4053
}

src/test/java/software/amazon/cloudformation/LambdaWrapperTest.java

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,15 @@ public void invokeHandler_nullResponse_returnsFailure(final String requestDataPa
158158
verifyInitialiseRuntime();
159159

160160
// validation failure metric should be published for final error handling
161-
verify(providerMetricsPublisher, times(1)).publishExceptionMetric(any(Instant.class), any(),
162-
any(TerminalException.class), any(HandlerErrorCode.class));
161+
verify(providerMetricsPublisher).publishExceptionMetric(any(Instant.class), any(), any(TerminalException.class),
162+
any(HandlerErrorCode.class));
163+
verify(providerMetricsPublisher).publishExceptionByErrorCodeMetric(any(Instant.class), any(),
164+
any(HandlerErrorCode.class), eq(Boolean.TRUE));
165+
verify(providerMetricsPublisher).publishExceptionCountMetric(any(Instant.class), any(), any(Boolean.class));
163166

164167
// all metrics should be published even on terminal failure
165-
verify(providerMetricsPublisher, times(1)).publishInvocationMetric(any(Instant.class), eq(action));
166-
verify(providerMetricsPublisher, times(1)).publishDurationMetric(any(Instant.class), eq(action), anyLong());
168+
verify(providerMetricsPublisher).publishInvocationMetric(any(Instant.class), eq(action));
169+
verify(providerMetricsPublisher).publishDurationMetric(any(Instant.class), eq(action), anyLong());
167170

168171
// verify that model validation occurred for CREATE/UPDATE/DELETE
169172
if (action == Action.CREATE || action == Action.UPDATE || action == Action.DELETE) {
@@ -399,14 +402,21 @@ public void invokeHandler_InProgress_returnsInProgress(final String requestDataP
399402
// verify output response
400403
verifyHandlerResponse(out, ProgressEvent.<TestModel, TestContext>builder().status(OperationStatus.IN_PROGRESS)
401404
.resourceModel(TestModel.builder().property1("abc").property2(123).build()).build());
405+
verify(providerMetricsPublisher, atLeastOnce()).publishExceptionByErrorCodeMetric(any(Instant.class), eq(action),
406+
any(), eq(Boolean.FALSE));
407+
verify(providerMetricsPublisher).publishExceptionCountMetric(any(Instant.class), eq(action), eq(Boolean.FALSE));
402408
} else {
403409
verifyHandlerResponse(out,
404410
ProgressEvent.<TestModel, TestContext>builder().status(OperationStatus.FAILED)
405411
.errorCode(HandlerErrorCode.InternalFailure).message("READ and LIST handlers must return synchronously.")
406412
.build());
407-
verify(providerMetricsPublisher, times(1)).publishExceptionMetric(any(Instant.class), eq(action),
413+
verify(providerMetricsPublisher).publishExceptionMetric(any(Instant.class), eq(action),
408414
any(TerminalException.class), eq(HandlerErrorCode.InternalFailure));
415+
verify(providerMetricsPublisher).publishExceptionByErrorCodeMetric(any(Instant.class), eq(action),
416+
eq(HandlerErrorCode.InternalFailure), eq(Boolean.TRUE));
417+
verify(providerMetricsPublisher).publishExceptionCountMetric(any(Instant.class), eq(action), eq(Boolean.TRUE));
409418
}
419+
410420
// validation failure metric should not be published
411421
verifyNoMoreInteractions(providerMetricsPublisher);
412422

@@ -446,8 +456,11 @@ public void reInvokeHandler_InProgress_returnsInProgress(final String requestDat
446456
verifyInitialiseRuntime();
447457

448458
// all metrics should be published, once for a single invocation
449-
verify(providerMetricsPublisher, times(1)).publishInvocationMetric(any(Instant.class), eq(action));
450-
verify(providerMetricsPublisher, times(1)).publishDurationMetric(any(Instant.class), eq(action), anyLong());
459+
verify(providerMetricsPublisher).publishInvocationMetric(any(Instant.class), eq(action));
460+
verify(providerMetricsPublisher).publishDurationMetric(any(Instant.class), eq(action), anyLong());
461+
verify(providerMetricsPublisher, atLeastOnce()).publishExceptionByErrorCodeMetric(any(Instant.class), eq(action),
462+
any(), eq(Boolean.FALSE));
463+
verify(providerMetricsPublisher).publishExceptionCountMetric(any(Instant.class), eq(action), eq(Boolean.FALSE));
451464

452465
// validation failure metric should not be published
453466
verifyNoMoreInteractions(providerMetricsPublisher);
@@ -798,6 +811,12 @@ public void invokeHandler_metricPublisherThrowable_returnsFailureResponse() thro
798811
// verify initialiseRuntime was called and initialised dependencies
799812
verifyInitialiseRuntime();
800813

814+
verify(providerMetricsPublisher).publishExceptionByErrorCodeMetric(any(Instant.class), any(Action.class),
815+
any(HandlerErrorCode.class), any(Boolean.class));
816+
817+
verify(providerMetricsPublisher).publishExceptionCountMetric(any(Instant.class), any(Action.class),
818+
any(Boolean.class));
819+
801820
// no further calls to metrics publisher should occur
802821
verifyNoMoreInteractions(providerMetricsPublisher);
803822

0 commit comments

Comments
 (0)