Skip to content

Add runtime and memorysize tags to enhanced metrics #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
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;
}