Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, () => {});
Expand All @@ -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 () => {
Expand All @@ -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 () => {
Expand Down
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@ export function datadog<TEvent, TResult>(
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) {
Expand Down
63 changes: 63 additions & 0 deletions src/metrics/enhanced-metrics.spec.ts
Original file line number Diff line number Diff line change
@@ -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<string>;

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",
]);
});
});
57 changes: 51 additions & 6 deletions src/metrics/enhanced-metrics.ts
Original file line number Diff line number Diff line change
@@ -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")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if it might be useful to keep the minor version, since AWS seems to reserve the right to update it 🤔 . On one hand, it's more accurate and might reveal why a regression occurred, on the other it doesn't match the aws specified runtime.

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));
}
3 changes: 3 additions & 0 deletions src/utils/process-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function getProcessVersion() {
return process.version;
}