Skip to content

Commit c96415d

Browse files
committed
emit enhanced error metric and create span when an exception is raised outside of the handler function
1 parent 856ff60 commit c96415d

File tree

5 files changed

+64
-13
lines changed

5 files changed

+64
-13
lines changed

src/handler.cjs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const {
33
datadogHandlerEnvVar,
44
lambdaTaskRootEnvVar,
55
traceExtractorEnvVar,
6+
emitTelemetryOnErrorOutsideHandler,
67
getEnvValue,
78
} = require("./index.js");
89
const { logDebug, logError } = require("./utils/index.js");
@@ -32,4 +33,9 @@ if (extractorEnv) {
3233
}
3334
}
3435

35-
exports.handler = datadog(loadSync(taskRootEnv, handlerEnv), { traceExtractor });
36+
try {
37+
exports.handler = datadog(loadSync(taskRootEnv, handlerEnv), { traceExtractor });
38+
} catch (error) {
39+
emitTelemetryOnErrorOutsideHandler(error, handlerEnv, Date.now());
40+
throw error;
41+
}

src/handler.mjs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { datadog, datadogHandlerEnvVar, lambdaTaskRootEnvVar, traceExtractorEnvVar, getEnvValue } from "./index.js";
1+
import {
2+
datadog,
3+
datadogHandlerEnvVar,
4+
lambdaTaskRootEnvVar,
5+
traceExtractorEnvVar,
6+
getEnvValue,
7+
emitTelemetryOnErrorOutsideHandler,
8+
} from "./index.js";
29
import { logDebug, logError } from "./utils/index.js";
310
import { load } from "./runtime/index.js";
411
import { initTracer } from "./runtime/module_importer.js";
@@ -26,4 +33,12 @@ if (extractorEnv) {
2633
}
2734
}
2835

29-
export const handler = datadog(await load(taskRootEnv, handlerEnv), { traceExtractor });
36+
let wrapped_handler;
37+
try {
38+
wrapped_handler = datadog(await load(taskRootEnv, handlerEnv), { traceExtractor });
39+
} catch (error) {
40+
emitTelemetryOnErrorOutsideHandler(error, handlerEnv, Date.now());
41+
throw error;
42+
}
43+
44+
export const handler = wrapped_handler;

src/index.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { subscribeToDC } from "./runtime";
1313
import { logDebug, Logger, LogLevel, promisifiedHandler, setSandboxInit, setLogger, setLogLevel } from "./utils";
1414
import { getEnhancedMetricTags } from "./metrics/enhanced-metrics";
1515
import { DatadogTraceHeaders } from "./trace/context/extractor";
16+
import { SpanWrapper } from "./trace/span-wrapper";
17+
import { SpanOptions, TracerWrapper } from "./trace/tracer-wrapper";
1618

1719
// Backwards-compatible export, TODO deprecate in next major
1820
export { DatadogTraceHeaders as TraceHeaders } from "./trace/context/extractor";
@@ -402,3 +404,26 @@ function getRuntimeTag(): string {
402404
const version = process.version;
403405
return `dd_lambda_layer:datadog-node${version}`;
404406
}
407+
408+
export function emitTelemetryOnErrorOutsideHandler(error: Error, functionName: string, startTime: number): void {
409+
const metricsListener = new MetricsListener(new KMSService(), defaultConfig);
410+
incrementErrorsMetric(metricsListener);
411+
const options: SpanOptions = {
412+
tags: {
413+
service: "aws.lambda",
414+
operation_name: "aws.lambda",
415+
resource_names: functionName,
416+
"resource.name": functionName,
417+
"span.type": "serverless",
418+
"error.status": 500,
419+
"error.type": typeof error,
420+
"error.message": error.message,
421+
"error.stack": error.stack,
422+
status: error,
423+
},
424+
startTime,
425+
};
426+
const tracerWrapper = new TracerWrapper();
427+
const span = new SpanWrapper(tracerWrapper.startSpan("aws.lambda", options), {});
428+
span.finish();
429+
}

src/metrics/enhanced-metrics.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ describe("getEnhancedMetricTags", () => {
5555
"account_id:123497598159",
5656
"functionname:my-test-lambda",
5757
"resource:my-test-lambda",
58-
"cold_start:true",
5958
"memorysize:128",
59+
"cold_start:true",
6060
"datadog_lambda:vX.X.X",
6161
"runtime:nodejs20.x",
6262
]);
@@ -66,8 +66,8 @@ describe("getEnhancedMetricTags", () => {
6666
mockedGetProcessVersion.mockReturnValue("v20.9.0");
6767
expect(getEnhancedMetricTags(mockContextLocal)).toStrictEqual([
6868
"functionname:my-test-lambda",
69-
"cold_start:true",
7069
"memorysize:128",
70+
"cold_start:true",
7171
"datadog_lambda:vX.X.X",
7272
"runtime:nodejs20.x",
7373
]);
@@ -80,8 +80,8 @@ describe("getEnhancedMetricTags", () => {
8080
"account_id:123497598159",
8181
"functionname:my-test-lambda",
8282
"resource:my-test-lambda",
83-
"cold_start:true",
8483
"memorysize:128",
84+
"cold_start:true",
8585
"datadog_lambda:vX.X.X",
8686
]);
8787
});

src/metrics/enhanced-metrics.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,17 @@ export function getRuntimeTag(): string | null {
4949
return `runtime:${processVersionTagString}`;
5050
}
5151

52-
export function getEnhancedMetricTags(context: Context): string[] {
53-
let arnTags = [`functionname:${context.functionName}`];
54-
if (context.invokedFunctionArn) {
55-
arnTags = parseTagsFromARN(context.invokedFunctionArn, context.functionVersion);
52+
export function getEnhancedMetricTags(context?: Context): string[] {
53+
let tags: string[] = [];
54+
if (context) {
55+
let arnTags = [`functionname:${context.functionName}`];
56+
if (context.invokedFunctionArn) {
57+
arnTags = parseTagsFromARN(context.invokedFunctionArn, context.functionVersion);
58+
}
59+
tags = [...arnTags, `memorysize:${context.memoryLimitInMB}`];
5660
}
57-
const tags = [...arnTags, ...getSandboxInitTags(), `memorysize:${context.memoryLimitInMB}`, getVersionTag()];
61+
62+
tags.push(...getSandboxInitTags(), getVersionTag());
5863

5964
const runtimeTag = getRuntimeTag();
6065
if (runtimeTag) {
@@ -69,7 +74,7 @@ export function getEnhancedMetricTags(context: Context): string[] {
6974
* @param context object passed to invocation by AWS
7075
* @param metricName name of the enhanced metric without namespace prefix, i.e. "invocations" or "errors"
7176
*/
72-
function incrementEnhancedMetric(listener: MetricsListener, metricName: string, context: Context) {
77+
function incrementEnhancedMetric(listener: MetricsListener, metricName: string, context?: Context) {
7378
// Always write enhanced metrics to standard out
7479
listener.sendDistributionMetric(`aws.lambda.enhanced.${metricName}`, 1, true, ...getEnhancedMetricTags(context));
7580
}
@@ -78,6 +83,6 @@ export function incrementInvocationsMetric(listener: MetricsListener, context: C
7883
incrementEnhancedMetric(listener, "invocations", context);
7984
}
8085

81-
export function incrementErrorsMetric(listener: MetricsListener, context: Context): void {
86+
export function incrementErrorsMetric(listener: MetricsListener, context?: Context): void {
8287
incrementEnhancedMetric(listener, "errors", context);
8388
}

0 commit comments

Comments
 (0)