From 92e6d9eaab3a1b4b07f25eed386c0f474adee475 Mon Sep 17 00:00:00 2001 From: Stephen Firrincieli Date: Wed, 30 Oct 2019 15:35:44 -0400 Subject: [PATCH 1/2] Add runtime and memorysize tags to enhanced metrics --- src/index.spec.ts | 10 ++--- src/index.ts | 4 +- src/metrics/enhanced-metrics.spec.ts | 63 ++++++++++++++++++++++++++++ src/metrics/enhanced-metrics.ts | 57 ++++++++++++++++++++++--- src/utils/process-version.ts | 3 ++ 5 files changed, 124 insertions(+), 13 deletions(-) create mode 100644 src/metrics/enhanced-metrics.spec.ts create mode 100644 src/utils/process-version.ts diff --git a/src/index.spec.ts b/src/index.spec.ts index 8e6f87c4..fb725f16 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -189,7 +189,7 @@ describe("datadog", () => { await wrapped({}, mockContext, () => {}); expect(mockedIncrementInvocations).toBeCalledTimes(1); - expect(mockedIncrementInvocations).toBeCalledWith(mockARN); + expect(mockedIncrementInvocations).toBeCalledWith(mockContext); await wrapped({}, mockContext, () => {}); await wrapped({}, mockContext, () => {}); @@ -213,8 +213,8 @@ describe("datadog", () => { expect(mockedIncrementInvocations).toBeCalledTimes(1); expect(mockedIncrementErrors).toBeCalledTimes(1); - expect(mockedIncrementInvocations).toBeCalledWith(mockARN); - expect(mockedIncrementErrors).toBeCalledWith(mockARN); + expect(mockedIncrementInvocations).toBeCalledWith(mockContext); + expect(mockedIncrementErrors).toBeCalledWith(mockContext); }); it("increments errors and invocations with config setting", async () => { @@ -230,8 +230,8 @@ describe("datadog", () => { expect(mockedIncrementInvocations).toBeCalledTimes(1); expect(mockedIncrementErrors).toBeCalledTimes(1); - expect(mockedIncrementInvocations).toBeCalledWith(mockARN); - expect(mockedIncrementErrors).toBeCalledWith(mockARN); + expect(mockedIncrementInvocations).toBeCalledWith(mockContext); + expect(mockedIncrementErrors).toBeCalledWith(mockContext); }); it("doesn't increment enhanced metrics without env var or config", async () => { diff --git a/src/index.ts b/src/index.ts index d68bef24..4ebdb988 100644 --- a/src/index.ts +++ b/src/index.ts @@ -81,12 +81,12 @@ export function datadog( listener.onStartInvocation(event, context); } if (finalConfig.enhancedMetrics) { - incrementInvocationsMetric(context.invokedFunctionArn); + incrementInvocationsMetric(context); } }, async (event, context, error?) => { if (finalConfig.enhancedMetrics && error) { - incrementErrorsMetric(context.invokedFunctionArn); + incrementErrorsMetric(context); } // Completion hook, (called once per handler invocation) for (const listener of listeners) { diff --git a/src/metrics/enhanced-metrics.spec.ts b/src/metrics/enhanced-metrics.spec.ts new file mode 100644 index 00000000..e42aec4b --- /dev/null +++ b/src/metrics/enhanced-metrics.spec.ts @@ -0,0 +1,63 @@ +import { Context } from "aws-lambda"; +import { _resetColdStart } from "../utils/cold-start"; +import { getProcessVersion } from "../utils/process-version"; +import { getEnhancedMetricTags, getRuntimeTag } from "./enhanced-metrics"; + +jest.mock("../utils/process-version"); + +const mockedGetProcessVersion = getProcessVersion as jest.Mock; + +const mockARN = "arn:aws:lambda:us-east-1:123497598159:function:my-test-lambda"; +const mockContext = ({ + invokedFunctionArn: mockARN, + memoryLimitInMB: "128", +} as any) as Context; + +describe("getRuntimeTag", () => { + it("returns a null runtime tag when version is not recognized", () => { + mockedGetProcessVersion.mockReturnValue("v6.2.3"); + expect(getRuntimeTag()).toBe(null); + }); + + it("returns the expected tag for v8.10", () => { + mockedGetProcessVersion.mockReturnValue("v8.10.0"); + expect(getRuntimeTag()).toBe("runtime:nodejs8.10"); + }); + + it("returns the expected tag for v10.x", () => { + mockedGetProcessVersion.mockReturnValue("v10.1.0"); + expect(getRuntimeTag()).toBe("runtime:nodejs10.x"); + }); +}); + +describe("getEnhancedMetricTags", () => { + beforeEach(() => { + _resetColdStart(); + }); + afterEach(() => { + _resetColdStart(); + }); + + it("generates tag list with runtime", () => { + mockedGetProcessVersion.mockReturnValue("v8.10.0"); + expect(getEnhancedMetricTags(mockContext)).toStrictEqual([ + "region:us-east-1", + "account_id:123497598159", + "functionname:my-test-lambda", + "cold_start:true", + "memorysize:128", + "runtime:nodejs8.10", + ]); + }); + + it("doesn't add runtime tag if version is unrecognized", () => { + mockedGetProcessVersion.mockReturnValue("v6.3.2"); + expect(getEnhancedMetricTags(mockContext)).toStrictEqual([ + "region:us-east-1", + "account_id:123497598159", + "functionname:my-test-lambda", + "cold_start:true", + "memorysize:128", + ]); + }); +}); diff --git a/src/metrics/enhanced-metrics.ts b/src/metrics/enhanced-metrics.ts index c1dcdbfa..8d2d7132 100644 --- a/src/metrics/enhanced-metrics.ts +++ b/src/metrics/enhanced-metrics.ts @@ -1,16 +1,61 @@ import { getEnvValue, sendDistributionMetric } from "../index"; +import { Context } from "aws-lambda"; import { parseTagsFromARN } from "../utils/arn"; import { getColdStartTag } from "../utils/cold-start"; +import { getProcessVersion } from "../utils/process-version"; const ENHANCED_LAMBDA_METRICS_NAMESPACE = "aws.lambda.enhanced"; -export function incrementInvocationsMetric(functionARN: string): void { - const tags = [...parseTagsFromARN(functionARN), getColdStartTag()]; - sendDistributionMetric(`${ENHANCED_LAMBDA_METRICS_NAMESPACE}.invocations`, 1, ...tags); +// Same tag strings added to normal Lambda integration metrics +enum RuntimeTagValues { + Node8 = "nodejs8.10", + Node10 = "nodejs10.x", } -export function incrementErrorsMetric(functionARN: string): void { - const tags = [...parseTagsFromARN(functionARN), getColdStartTag()]; - sendDistributionMetric(`${ENHANCED_LAMBDA_METRICS_NAMESPACE}.errors`, 1, ...tags); +/** + * Uses process.version to create a runtime tag + * If a version cannot be identified, returns null + * See https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html + */ +export function getRuntimeTag(): string | null { + const processVersion = getProcessVersion(); + let processVersionTagString: string | null = null; + + if (processVersion.startsWith("v8.10")) { + processVersionTagString = RuntimeTagValues.Node8; + } + + if (processVersion.startsWith("v10")) { + processVersionTagString = RuntimeTagValues.Node10; + } + + if (!processVersionTagString) { + return null; + } + + return `runtime:${processVersionTagString}`; +} + +export function getEnhancedMetricTags(context: Context): string[] { + const tags = [ + ...parseTagsFromARN(context.invokedFunctionArn), + getColdStartTag(), + `memorysize:${context.memoryLimitInMB}`, + ]; + + const runtimeTag = getRuntimeTag(); + if (runtimeTag) { + tags.push(runtimeTag); + } + + return tags; +} + +export function incrementInvocationsMetric(context: Context): void { + sendDistributionMetric(`${ENHANCED_LAMBDA_METRICS_NAMESPACE}.invocations`, 1, ...getEnhancedMetricTags(context)); +} + +export function incrementErrorsMetric(context: Context): void { + sendDistributionMetric(`${ENHANCED_LAMBDA_METRICS_NAMESPACE}.errors`, 1, ...getEnhancedMetricTags(context)); } diff --git a/src/utils/process-version.ts b/src/utils/process-version.ts new file mode 100644 index 00000000..e5737c73 --- /dev/null +++ b/src/utils/process-version.ts @@ -0,0 +1,3 @@ +export function getProcessVersion() { + return process.version; +} From c83d1db53aaa29e274166f07f3e01fa07603bb27 Mon Sep 17 00:00:00 2001 From: Stephen Firrincieli Date: Mon, 4 Nov 2019 11:56:31 -0500 Subject: [PATCH 2/2] Bump version and update readme --- README.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b628f265..493cce73 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ If you have the Datadog Lambda Log forwarder enabled and are sending custom metr ### DD_ENHANCED_METRICS -If you set the value of this variable to "true" then the Lambda layer will increment a Lambda integration metric called `aws.lambda.enhanced.invocations` with each invocation and `aws.lambda.enhanced.errors` if the invocation results in an error. These metrics are tagged with the function name, region, and account, as well as `cold_start:true|false`. +If you set the value of this variable to "true" then the Lambda layer will increment a Lambda integration metric called `aws.lambda.enhanced.invocations` with each invocation and `aws.lambda.enhanced.errors` if the invocation results in an error. These metrics are tagged with the function name, region, account, runtime, memorysize, and `cold_start:true|false`. ## Usage diff --git a/package.json b/package.json index 5d425e82..e132fb0c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "datadog-lambda-js", - "version": "0.6.0", + "version": "0.7.0", "description": "Lambda client library that supports hybrid tracing in node js", "main": "dist/index.js", "types": "dist/index.d.ts",