From 3fa5913e5a78b4490bd08c3432595951d2ec3010 Mon Sep 17 00:00:00 2001 From: AJ Stuyvenberg Date: Tue, 17 Jan 2023 14:55:12 -0500 Subject: [PATCH 01/11] feat: Cold Start Tracing support --- src/index.ts | 4 ++ src/runtime/index.ts | 1 + src/runtime/require-tracer.spec.ts | 29 +++++++++++++++ src/runtime/require-tracer.ts | 56 ++++++++++++++++++++++++++++ src/trace/listener.ts | 59 +++++++++++++++++++++++++++++- 5 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 src/runtime/require-tracer.spec.ts create mode 100644 src/runtime/require-tracer.ts diff --git a/src/index.ts b/src/index.ts index 3e3a25d6..81cc2694 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ import { MetricsListener, } from "./metrics"; import { TraceConfig, TraceHeaders, TraceListener } from "./trace"; +import { subscribeToDC } from "./runtime"; import { logDebug, logError, @@ -80,6 +81,9 @@ export const defaultConfig: Config = { let currentMetricsListener: MetricsListener | undefined; let currentTraceListener: TraceListener | undefined; +if (!process.env.DD_DISABLE_COLD_START_TRACING) { + subscribeToDC(); +} /** * Wraps your AWS lambda handler functions to add tracing/metrics support * @param handler A lambda handler function. diff --git a/src/runtime/index.ts b/src/runtime/index.ts index d55f827f..cf085cf6 100644 --- a/src/runtime/index.ts +++ b/src/runtime/index.ts @@ -1 +1,2 @@ export { load, loadSync } from "./user-function"; +export { subscribeToDC, getTraceTree, RequireNode } from "./require-tracer" diff --git a/src/runtime/require-tracer.spec.ts b/src/runtime/require-tracer.spec.ts new file mode 100644 index 00000000..214d9f9a --- /dev/null +++ b/src/runtime/require-tracer.spec.ts @@ -0,0 +1,29 @@ +import { subscribeToDC, getTraceTree, RequireNode } from "./require-tracer"; +const dc = require('diagnostics_channel') + +describe('require-tracer', () => { + it('generates a trace tree', () => { + subscribeToDC() + const moduleLoadStartChannel = dc.channel('dd-trace:moduleLoadStart') + const moduleLoadEndChannel = dc.channel('dd-trace:moduleLoadEnd') + + // require('myLibrary') + moduleLoadStartChannel.publish({ + request: 'myLibrary' + }) + // require('myChildLibrary') + moduleLoadStartChannel.publish({ + request: 'myChildLibrary' + }) + moduleLoadEndChannel.publish() + moduleLoadEndChannel.publish() + const res = getTraceTree() + expect(res).toBeDefined + expect(res[0].id).toBe('myLibrary') + const resChildren = res[0].children as RequireNode[] + expect(resChildren).toHaveLength(1) + const resChild = resChildren.pop() as RequireNode + expect(resChild.id).toBe('myChildLibrary') + }) + +}) diff --git a/src/runtime/require-tracer.ts b/src/runtime/require-tracer.ts new file mode 100644 index 00000000..9c99a357 --- /dev/null +++ b/src/runtime/require-tracer.ts @@ -0,0 +1,56 @@ +const dc = require('diagnostics_channel') + +export class RequireNode { + public id: string + public startTime: number + public endTime: number + public children: RequireNode[] + + constructor(id: string, startTime: number) { + this.id = id + this.startTime = startTime + this.endTime = startTime + this.children = [] + } + + public set setEnd(endTime: number) { + this.endTime = endTime + } +} + +const moduleLoadStartChannel = dc.channel('dd-trace:moduleLoadStart') +const moduleLoadEndChannel = dc.channel('dd-trace:moduleLoadEnd') +const rootNodes: RequireNode[] = [] + +const requireStack: RequireNode[] = [] +const pushNode = (data: any) => { + const startTime = Date.now() + + const reqNode = new RequireNode(data.request, startTime) + const maybeParent = requireStack[requireStack.length - 1] + + if (maybeParent) { + maybeParent.children.push(reqNode) + } + requireStack.push(reqNode) +} + +const popNode = () => { + const endTime = Date.now() + const reqNode = requireStack.pop() + if (reqNode){ + reqNode.endTime = endTime + } + if (requireStack.length <= 0 && reqNode) { + rootNodes.push(reqNode) + } +} + +export const subscribeToDC = () => { + moduleLoadStartChannel.subscribe(pushNode) + moduleLoadEndChannel.subscribe(popNode) +} + +export const getTraceTree = (): RequireNode[] => { + return rootNodes +} diff --git a/src/trace/listener.ts b/src/trace/listener.ts index a335e181..8979bacb 100644 --- a/src/trace/listener.ts +++ b/src/trace/listener.ts @@ -15,9 +15,10 @@ import { didFunctionColdStart } from "../utils/cold-start"; import { datadogLambdaVersion } from "../constants"; import { Source, ddtraceVersion, parentSpanFinishTimeHeader, authorizingRequestIdHeader } from "./constants"; import { patchConsole } from "./patch-console"; -import { SpanContext, TraceOptions, TracerWrapper } from "./tracer-wrapper"; +import { SpanContext, SpanOptions, TraceOptions, TracerWrapper } from "./tracer-wrapper"; import { SpanInferrer } from "./span-inferrer"; import { SpanWrapper } from "./span-wrapper"; +import { RequireNode, getTraceTree } from "../runtime/index"; export type TraceExtractor = (event: any, context: Context) => TraceContext; export interface TraceConfig { @@ -120,12 +121,58 @@ export class TraceListener { this.config.encodeAuthorizerContext, ); } + this.lambdaSpanParentContext = this.inferredSpan?.span || parentSpanContext; this.context = context; this.triggerTags = extractTriggerTags(event, context); this.stepFunctionContext = readStepFunctionContextFromEvent(event); } + private createColdStartSpan(startTime: number, parentSpan: SpanWrapper | undefined): SpanWrapper { + const options: SpanOptions = { + tags: { + service: "aws.lambda", + operation_name: "aws.lambda.require", + resource_names: this.context?.functionName?.toLowerCase(), + "resource.name": this.context?.functionName?.toLowerCase(), + }, + startTime: startTime, + }; + if (parentSpan) { + options.childOf = parentSpan.span; + } else { + options.childOf = this.wrappedCurrentSpan?.span; + } + const newSpan = new SpanWrapper(this.tracerWrapper.startSpan("aws.lambda.load", options), {}); + newSpan.finish(this.wrappedCurrentSpan?.startTime()); + return newSpan; + } + + private traceTree(reqNode: RequireNode, parentSpan: SpanWrapper | undefined): void { + if (reqNode.endTime - reqNode.startTime <= 3) { + return; + } + const options: SpanOptions = { + tags: { + service: "aws.lambda", + operation_name: "aws.lambda.require", + resource_names: reqNode.id, + "resource.name": reqNode.id, + }, + startTime: reqNode.startTime, + }; + if (parentSpan) { + options.childOf = parentSpan.span; + } + const newSpan = new SpanWrapper(this.tracerWrapper.startSpan("aws.lambda.require", options), {}); + if (reqNode.endTime - reqNode.startTime > 3) { + for (let node of reqNode.children || []) { + this.traceTree(node, newSpan); + } + } + newSpan?.finish(reqNode.endTime); + } + /** * onEndingInvocation runs after the user function has returned * but before the wrapped function has returned @@ -145,7 +192,15 @@ export class TraceListener { tagObject(this.tracerWrapper.currentSpan, "function.request", event); tagObject(this.tracerWrapper.currentSpan, "function.response", result); } - + const coldStartNodes = getTraceTree(); + if (coldStartNodes.length > 0 && didFunctionColdStart()) { + const coldStartSpanStartTime = + coldStartNodes.length > 0 ? coldStartNodes[0]?.startTime : this.wrappedCurrentSpan?.startTime(); + const coldStartSpan = this.createColdStartSpan(coldStartSpanStartTime, this.inferredSpan); + for (let coldStartNode of coldStartNodes) { + this.traceTree(coldStartNode as RequireNode, coldStartSpan); + } + } if (this.triggerTags) { const statusCode = extractHTTPStatusCodeTag(this.triggerTags, result); From 4289c00f44097a9f5866bf026dcb9d32856bbeea Mon Sep 17 00:00:00 2001 From: AJ Stuyvenberg Date: Mon, 23 Jan 2023 19:35:11 -0500 Subject: [PATCH 02/11] feat: Add filename to cold start spans --- src/runtime/require-tracer.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/runtime/require-tracer.ts b/src/runtime/require-tracer.ts index 9c99a357..6db2e163 100644 --- a/src/runtime/require-tracer.ts +++ b/src/runtime/require-tracer.ts @@ -2,12 +2,14 @@ const dc = require('diagnostics_channel') export class RequireNode { public id: string + public filename: string public startTime: number public endTime: number public children: RequireNode[] - constructor(id: string, startTime: number) { + constructor(id: string, filename: string, startTime: number) { this.id = id + this.filename = filename this.startTime = startTime this.endTime = startTime this.children = [] @@ -26,7 +28,7 @@ const requireStack: RequireNode[] = [] const pushNode = (data: any) => { const startTime = Date.now() - const reqNode = new RequireNode(data.request, startTime) + const reqNode = new RequireNode(data.request, data.filename, startTime) const maybeParent = requireStack[requireStack.length - 1] if (maybeParent) { From 1fd57c237c3ee14dc8cca90ed9beab293d687188 Mon Sep 17 00:00:00 2001 From: AJ Stuyvenberg Date: Mon, 23 Jan 2023 19:36:48 -0500 Subject: [PATCH 03/11] feat: Break out cold start tracer to class. Add specs. Prettier. --- src/runtime/user-function.ts | 5 +- src/trace/cold-start-tracer.spec.ts | 124 ++++++++++++++++++++++++++++ src/trace/cold-start-tracer.ts | 95 +++++++++++++++++++++ src/trace/listener.ts | 66 ++++----------- 4 files changed, 236 insertions(+), 54 deletions(-) create mode 100644 src/trace/cold-start-tracer.spec.ts create mode 100644 src/trace/cold-start-tracer.ts diff --git a/src/runtime/user-function.ts b/src/runtime/user-function.ts index b9243bf8..5071238f 100644 --- a/src/runtime/user-function.ts +++ b/src/runtime/user-function.ts @@ -18,8 +18,7 @@ import { MalformedHandlerName, ImportModuleError, UserCodeSyntaxError, -} from "./errors.js"; -import { logDebug } from "../utils/log.js"; +} from "./errors"; const module_importer = require("./module_importer"); const FUNCTION_EXPR = /^([^.]*)\.(.*)$/; @@ -340,4 +339,4 @@ export const loadSync = function ( } return handlerFunc; -}; \ No newline at end of file +}; diff --git a/src/trace/cold-start-tracer.spec.ts b/src/trace/cold-start-tracer.spec.ts new file mode 100644 index 00000000..7e5b892a --- /dev/null +++ b/src/trace/cold-start-tracer.spec.ts @@ -0,0 +1,124 @@ +import { RequireNode } from "../runtime/require-tracer"; +import { ColdStartTracerConfig, ColdStartTracer } from "./cold-start-tracer"; +import { TracerWrapper, SpanOptions } from "./tracer-wrapper"; +import { SpanWrapper } from "./span-wrapper"; + +let mockStartSpan: jest.Mock; +let mockFinishSpan: jest.Mock; + +jest.mock("./tracer-wrapper", () => { + mockFinishSpan = jest.fn(); + mockStartSpan = jest.fn().mockImplementation((spanName, spanOptions) => { + return { spanName, spanOptions, finish: mockFinishSpan }; + }); + class MockTraceWrapper { + get isTraceAvailable() { + return true; + } + + constructor() {} + + startSpan(spanName: string, spanOptions: SpanOptions): any { + return mockStartSpan(spanName, spanOptions); + } + } + return { + TracerWrapper: MockTraceWrapper, + }; +}); + +describe("ColdStartTracer", () => { + beforeEach(() => { + mockStartSpan.mockClear(); + mockFinishSpan.mockClear(); + }); + + it("generates a trace tree", () => { + const requireNodes: RequireNode[] = [ + { + id: "handler", + filename: "/var/task/handler.js", + startTime: 1, + endTime: 6, + children: [ + { + id: "myChildModule", + filename: "/opt/nodejs/node_modules/my-child-module.js", + startTime: 2, + endTime: 3, + }, + { + id: "myCoreModule", + filename: "http", + startTime: 4, + endTime: 5, + }, + { + id: "aws-sdk", + filename: "/var/runtime/aws-sdk", + startTime: 4, + endTime: 5, + }, + ], + } as any as RequireNode, + ]; + const coldStartConfig: ColdStartTracerConfig = { + tracerWrapper: new TracerWrapper(), + parentSpan: { + span: {}, + name: "my-lambda-span", + } as any as SpanWrapper, + lambdaFunctionName: "my-function-name", + coldStartSpanFinishTime: 500, + minDuration: 1, + }; + const coldStartTracer = new ColdStartTracer(coldStartConfig); + coldStartTracer.trace(requireNodes); + expect(mockStartSpan).toHaveBeenCalledTimes(5); + expect(mockFinishSpan).toHaveBeenCalledTimes(5); + const span1 = mockStartSpan.mock.calls[0]; + expect(span1[0]).toEqual("aws.lambda.load"); + expect(span1[1].tags).toEqual({ + operation_name: "aws.lambda.require", + "resource.name": "my-function-name", + resource_names: "my-function-name", + service: "aws.lambda", + }); + const span2 = mockStartSpan.mock.calls[1]; + expect(span2[0]).toEqual("aws.lambda.require"); + expect(span2[1].tags).toEqual({ + operation_name: "aws.lambda.require", + "resource.name": "handler", + resource_names: "handler", + service: "aws.lambda", + filename: "/var/task/handler.js", + }); + const span3 = mockStartSpan.mock.calls[2]; + expect(span3[0]).toEqual("aws.lambda.require_layer"); + expect(span3[1].tags).toEqual({ + filename: "/opt/nodejs/node_modules/my-child-module.js", + operation_name: "aws.lambda.require_layer", + "resource.name": "myChildModule", + resource_names: "myChildModule", + service: "aws.lambda", + }); + const span4 = mockStartSpan.mock.calls[3]; + expect(span4[0]).toEqual("aws.lambda.require_core_module"); + expect(span4[1].tags).toEqual({ + filename: "http", + operation_name: "aws.lambda.require_core_module", + "resource.name": "myCoreModule", + resource_names: "myCoreModule", + service: "aws.lambda", + }); + const span5 = mockStartSpan.mock.calls[4]; + expect(span5[0]).toEqual("aws.lambda.require_runtime"); + expect(span5[1].tags).toEqual({ + filename: "/var/runtime/aws-sdk", + operation_name: "aws.lambda.require_runtime", + "resource.name": "aws-sdk", + resource_names: "aws-sdk", + service: "aws.lambda", + }); + }); +}); diff --git a/src/trace/cold-start-tracer.ts b/src/trace/cold-start-tracer.ts new file mode 100644 index 00000000..19e01186 --- /dev/null +++ b/src/trace/cold-start-tracer.ts @@ -0,0 +1,95 @@ +import { RequireNode } from "../runtime/require-tracer"; +import { SpanWrapper } from "./span-wrapper"; +import { TracerWrapper, SpanOptions } from "./tracer-wrapper"; + +export interface ColdStartTracerConfig { + tracerWrapper: TracerWrapper; + parentSpan?: SpanWrapper; + lambdaFunctionName?: string; + coldStartSpanFinishTime: number; // Equivalent to the Lambda Span Start Time + minDuration?: number; +} + +export class ColdStartTracer { + private tracerWrapper: TracerWrapper; + private parentSpan?: SpanWrapper; + private lambdaFunctionName?: string; + private coldStartSpanFinishTime: number; + private minDuration: number; + + constructor(coldStartTracerConfig: ColdStartTracerConfig) { + this.tracerWrapper = coldStartTracerConfig.tracerWrapper; + this.parentSpan = coldStartTracerConfig.parentSpan; + this.lambdaFunctionName = coldStartTracerConfig.lambdaFunctionName; + this.coldStartSpanFinishTime = coldStartTracerConfig.coldStartSpanFinishTime; + this.minDuration = coldStartTracerConfig.minDuration || 3; + } + + trace(rootNodes: RequireNode[]) { + const coldStartSpanStartTime = rootNodes[0]?.startTime; + const coldStartSpan = this.createColdStartSpan(coldStartSpanStartTime, this.parentSpan); + for (let coldStartNode of rootNodes) { + this.traceTree(coldStartNode, coldStartSpan); + } + } + + private createColdStartSpan(startTime: number, parentSpan: SpanWrapper | undefined): SpanWrapper { + const options: SpanOptions = { + tags: { + service: "aws.lambda", + operation_name: "aws.lambda.require", + resource_names: this.lambdaFunctionName, + "resource.name": this.lambdaFunctionName, + }, + startTime: startTime, + }; + if (parentSpan) { + options.childOf = parentSpan.span; + } + const newSpan = new SpanWrapper(this.tracerWrapper.startSpan("aws.lambda.load", options), {}); + newSpan.finish(this.coldStartSpanFinishTime); + return newSpan; + } + + private coldStartSpanOperationName(filename: string): string { + if (filename.startsWith("/opt/")) { + return "aws.lambda.require_layer"; + } else if (filename.startsWith("/var/runtime/")) { + return "aws.lambda.require_runtime"; + } else if (filename.includes("/")) { + return "aws.lambda.require"; + } else { + return "aws.lambda.require_core_module"; + } + } + + private traceTree(reqNode: RequireNode, parentSpan: SpanWrapper | undefined): void { + if (reqNode.endTime - reqNode.startTime < this.minDuration) { + return; + } + const options: SpanOptions = { + tags: { + service: "aws.lambda", + operation_name: this.coldStartSpanOperationName(reqNode.filename), + resource_names: reqNode.id, + "resource.name": reqNode.id, + filename: reqNode.filename, + }, + startTime: reqNode.startTime, + }; + if (parentSpan) { + options.childOf = parentSpan.span; + } + const newSpan = new SpanWrapper( + this.tracerWrapper.startSpan(this.coldStartSpanOperationName(reqNode.filename), options), + {}, + ); + if (reqNode.endTime - reqNode.startTime > this.minDuration) { + for (let node of reqNode.children || []) { + this.traceTree(node, newSpan); + } + } + // TODO move to save memory + newSpan?.finish(reqNode.endTime); + } +} diff --git a/src/trace/listener.ts b/src/trace/listener.ts index 8979bacb..0f6a80c3 100644 --- a/src/trace/listener.ts +++ b/src/trace/listener.ts @@ -9,6 +9,7 @@ import { import { patchHttp, unpatchHttp } from "./patch-http"; import { TraceContextService } from "./trace-context-service"; import { extractTriggerTags, extractHTTPStatusCodeTag, eventSubTypes, parseEventSourceSubType } from "./trigger"; +import { ColdStartTracerConfig, ColdStartTracer } from "./cold-start-tracer"; import { logDebug, tagObject } from "../utils"; import { didFunctionColdStart } from "../utils/cold-start"; @@ -56,6 +57,10 @@ export interface TraceConfig { * Custom trace extractor function */ traceExtractor?: TraceExtractor; + /** + * Minimum duration dependency to trace + */ + coldStartTraceMinDuration?: number; } export class TraceListener { @@ -68,6 +73,7 @@ export class TraceListener { private wrappedCurrentSpan?: SpanWrapper; private triggerTags?: { [key: string]: string }; private lambdaSpanParentContext?: SpanContext; + private coldStartTraceMinDuration?: number; public get currentTraceHeaders() { return this.contextService.currentTraceHeaders; @@ -128,51 +134,6 @@ export class TraceListener { this.stepFunctionContext = readStepFunctionContextFromEvent(event); } - private createColdStartSpan(startTime: number, parentSpan: SpanWrapper | undefined): SpanWrapper { - const options: SpanOptions = { - tags: { - service: "aws.lambda", - operation_name: "aws.lambda.require", - resource_names: this.context?.functionName?.toLowerCase(), - "resource.name": this.context?.functionName?.toLowerCase(), - }, - startTime: startTime, - }; - if (parentSpan) { - options.childOf = parentSpan.span; - } else { - options.childOf = this.wrappedCurrentSpan?.span; - } - const newSpan = new SpanWrapper(this.tracerWrapper.startSpan("aws.lambda.load", options), {}); - newSpan.finish(this.wrappedCurrentSpan?.startTime()); - return newSpan; - } - - private traceTree(reqNode: RequireNode, parentSpan: SpanWrapper | undefined): void { - if (reqNode.endTime - reqNode.startTime <= 3) { - return; - } - const options: SpanOptions = { - tags: { - service: "aws.lambda", - operation_name: "aws.lambda.require", - resource_names: reqNode.id, - "resource.name": reqNode.id, - }, - startTime: reqNode.startTime, - }; - if (parentSpan) { - options.childOf = parentSpan.span; - } - const newSpan = new SpanWrapper(this.tracerWrapper.startSpan("aws.lambda.require", options), {}); - if (reqNode.endTime - reqNode.startTime > 3) { - for (let node of reqNode.children || []) { - this.traceTree(node, newSpan); - } - } - newSpan?.finish(reqNode.endTime); - } - /** * onEndingInvocation runs after the user function has returned * but before the wrapped function has returned @@ -194,12 +155,15 @@ export class TraceListener { } const coldStartNodes = getTraceTree(); if (coldStartNodes.length > 0 && didFunctionColdStart()) { - const coldStartSpanStartTime = - coldStartNodes.length > 0 ? coldStartNodes[0]?.startTime : this.wrappedCurrentSpan?.startTime(); - const coldStartSpan = this.createColdStartSpan(coldStartSpanStartTime, this.inferredSpan); - for (let coldStartNode of coldStartNodes) { - this.traceTree(coldStartNode as RequireNode, coldStartSpan); - } + const coldStartConfig: ColdStartTracerConfig = { + tracerWrapper: this.tracerWrapper, + coldStartSpanFinishTime: this.wrappedCurrentSpan?.startTime(), + parentSpan: this.inferredSpan || this.wrappedCurrentSpan, + lambdaFunctionName: this.context?.functionName, + minDuration: this.coldStartTraceMinDuration, + }; + const coldStartTracer = new ColdStartTracer(coldStartConfig); + coldStartTracer.trace(coldStartNodes); } if (this.triggerTags) { const statusCode = extractHTTPStatusCodeTag(this.triggerTags, result); From 49f579eb759ab836761ffc7d73bb606c5bf850d8 Mon Sep 17 00:00:00 2001 From: AJ Stuyvenberg Date: Mon, 23 Jan 2023 20:30:14 -0500 Subject: [PATCH 04/11] feat: Expose minColdStartDuration and coldStartTracing config --- src/index.ts | 9 ++++++++- src/trace/cold-start-tracer.ts | 4 ++-- src/trace/listener.spec.ts | 1 + src/trace/listener.ts | 5 ++--- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index 81cc2694..59564ea1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -37,6 +37,8 @@ export const traceExtractorEnvVar = "DD_TRACE_EXTRACTOR"; export const defaultSiteURL = "datadoghq.com"; export const encodeAuthorizerContextEnvVar = "DD_ENCODE_AUTHORIZER_CONTEXT"; export const decodeAuthorizerContextEnvVar = "DD_DECODE_AUTHORIZER_CONTEXT"; +export const coldStartTracingEnvVar = "DD_COLD_START_TRACING"; +export const minColdStartTraceDurationEnvVar = "DD_MIN_COLD_START_DURATION"; interface GlobalConfig { /** @@ -76,12 +78,13 @@ export const defaultConfig: Config = { mergeDatadogXrayTraces: false, shouldRetryMetrics: false, siteURL: "", + minColdStartTraceDuration: 3 } as const; let currentMetricsListener: MetricsListener | undefined; let currentTraceListener: TraceListener | undefined; -if (!process.env.DD_DISABLE_COLD_START_TRACING) { +if (getEnvValue(coldStartTracingEnvVar, "true")) { subscribeToDC(); } /** @@ -291,6 +294,10 @@ function getConfig(userConfig?: Partial): Config { config.decodeAuthorizerContext = result === "true"; } + if (userConfig === undefined || userConfig.minColdStartTraceDuration === undefined) { + config.minColdStartTraceDuration = Number(getEnvValue(minColdStartTraceDurationEnvVar, '3')) + } + return config; } diff --git a/src/trace/cold-start-tracer.ts b/src/trace/cold-start-tracer.ts index 19e01186..7a088731 100644 --- a/src/trace/cold-start-tracer.ts +++ b/src/trace/cold-start-tracer.ts @@ -7,7 +7,7 @@ export interface ColdStartTracerConfig { parentSpan?: SpanWrapper; lambdaFunctionName?: string; coldStartSpanFinishTime: number; // Equivalent to the Lambda Span Start Time - minDuration?: number; + minDuration: number; } export class ColdStartTracer { @@ -22,7 +22,7 @@ export class ColdStartTracer { this.parentSpan = coldStartTracerConfig.parentSpan; this.lambdaFunctionName = coldStartTracerConfig.lambdaFunctionName; this.coldStartSpanFinishTime = coldStartTracerConfig.coldStartSpanFinishTime; - this.minDuration = coldStartTracerConfig.minDuration || 3; + this.minDuration = coldStartTracerConfig.minDuration; } trace(rootNodes: RequireNode[]) { diff --git a/src/trace/listener.spec.ts b/src/trace/listener.spec.ts index b6f30cc1..a286d9d0 100644 --- a/src/trace/listener.spec.ts +++ b/src/trace/listener.spec.ts @@ -81,6 +81,7 @@ describe("TraceListener", () => { decodeAuthorizerContext: true, mergeDatadogXrayTraces: false, injectLogContext: false, + minColdStartTraceDuration: 3 }; const context = { invokedFunctionArn: "arn:aws:lambda:us-east-1:123456789101:function:my-lambda", diff --git a/src/trace/listener.ts b/src/trace/listener.ts index 0f6a80c3..f16bf18c 100644 --- a/src/trace/listener.ts +++ b/src/trace/listener.ts @@ -60,7 +60,7 @@ export interface TraceConfig { /** * Minimum duration dependency to trace */ - coldStartTraceMinDuration?: number; + minColdStartTraceDuration: number; } export class TraceListener { @@ -73,7 +73,6 @@ export class TraceListener { private wrappedCurrentSpan?: SpanWrapper; private triggerTags?: { [key: string]: string }; private lambdaSpanParentContext?: SpanContext; - private coldStartTraceMinDuration?: number; public get currentTraceHeaders() { return this.contextService.currentTraceHeaders; @@ -160,7 +159,7 @@ export class TraceListener { coldStartSpanFinishTime: this.wrappedCurrentSpan?.startTime(), parentSpan: this.inferredSpan || this.wrappedCurrentSpan, lambdaFunctionName: this.context?.functionName, - minDuration: this.coldStartTraceMinDuration, + minDuration: this.config.minColdStartTraceDuration, }; const coldStartTracer = new ColdStartTracer(coldStartConfig); coldStartTracer.trace(coldStartNodes); From f87f129083117fcd60fd0f6c9b99035c720496e7 Mon Sep 17 00:00:00 2001 From: AJ Stuyvenberg Date: Tue, 24 Jan 2023 13:58:24 -0500 Subject: [PATCH 05/11] feat: Reduce number of open spans --- src/trace/cold-start-tracer.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/trace/cold-start-tracer.ts b/src/trace/cold-start-tracer.ts index 7a088731..9da718c7 100644 --- a/src/trace/cold-start-tracer.ts +++ b/src/trace/cold-start-tracer.ts @@ -84,12 +84,11 @@ export class ColdStartTracer { this.tracerWrapper.startSpan(this.coldStartSpanOperationName(reqNode.filename), options), {}, ); + newSpan?.finish(reqNode.endTime); if (reqNode.endTime - reqNode.startTime > this.minDuration) { for (let node of reqNode.children || []) { this.traceTree(node, newSpan); } } - // TODO move to save memory - newSpan?.finish(reqNode.endTime); } } From db4415f73a7519915c9a27a10d5645871d3bb0c0 Mon Sep 17 00:00:00 2001 From: AJ Stuyvenberg Date: Wed, 25 Jan 2023 07:55:52 -0500 Subject: [PATCH 06/11] feat: Remove unneeded await --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 59564ea1..5852f10d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -127,7 +127,7 @@ export function datadog( currentTraceListener = traceListener; try { - await traceListener.onStartInvocation(event, context); + traceListener.onStartInvocation(event, context); await metricsListener.onStartInvocation(event); if (finalConfig.enhancedMetrics) { incrementInvocationsMetric(metricsListener, context); From 601bb7d08c0cb798c9bc1d0d8b95e983f4ca5742 Mon Sep 17 00:00:00 2001 From: AJ Stuyvenberg Date: Wed, 25 Jan 2023 08:43:41 -0500 Subject: [PATCH 07/11] feat: integrations --- .../snapshots/logs/esm_node12.log | 12 +++++----- .../logs/process-input-traced_node12.log | 22 +++++++++++++++++ .../logs/process-input-traced_node14.log | 22 +++++++++++++++++ .../logs/process-input-traced_node16.log | 23 ++++++++++++++++++ .../logs/process-input-traced_node18.log | 23 ++++++++++++++++++ .../logs/status-code-500s_node12.log | 22 +++++++++++++++++ .../logs/status-code-500s_node14.log | 24 ++++++++++++++++++- .../logs/status-code-500s_node16.log | 23 ++++++++++++++++++ .../logs/status-code-500s_node18.log | 23 ++++++++++++++++++ 9 files changed, 187 insertions(+), 7 deletions(-) diff --git a/integration_tests/snapshots/logs/esm_node12.log b/integration_tests/snapshots/logs/esm_node12.log index 0cc84323..267da1f5 100644 --- a/integration_tests/snapshots/logs/esm_node12.log +++ b/integration_tests/snapshots/logs/esm_node12.log @@ -1,5 +1,5 @@ -XXXX-XX-XX XX:XX:XX.XXX ERROR Uncaught Exception {"errorType":"Runtime.ImportModuleError","errorMessage":"Error: Cannot find module 'esm'\nRequire stack:\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/handler.js\n- /var/runtime/UserFunction.js\n- /var/runtime/index.js","stack":["Runtime.ImportModuleError: Error: Cannot find module 'esm'","Require stack:","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/handler.js","- /var/runtime/UserFunction.js","- /var/runtime/index.js"," at ImportModuleError.ExtendedError [as constructor] (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at new ImportModuleError (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at _loadUserAppSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at loadSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at Object. (/opt/nodejs/node_modules/datadog-lambda-js/handler.js:XXX:XXX)"," at Module._compile (internal/modules/cjs/loader.js:XXX:XXX)"," at Object.Module._extensions..js (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.load (internal/modules/cjs/loader.js:XXX:XXX)"," at Function.Module._load (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.require (internal/modules/cjs/loader.js:XXX:XXX)"]} +XXXX-XX-XX XX:XX:XX.XXX ERROR Uncaught Exception {"errorType":"Runtime.ImportModuleError","errorMessage":"Error: Cannot find module 'esm'\nRequire stack:\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/trace/listener.js\n- /opt/nodejs/node_modules/datadog-lambda-js/trace/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/handler.js\n- /var/runtime/UserFunction.js\n- /var/runtime/index.js","stack":["Runtime.ImportModuleError: Error: Cannot find module 'esm'","Require stack:","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/trace/listener.js","- /opt/nodejs/node_modules/datadog-lambda-js/trace/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/handler.js","- /var/runtime/UserFunction.js","- /var/runtime/index.js"," at ImportModuleError.ExtendedError [as constructor] (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at new ImportModuleError (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at _loadUserAppSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at loadSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at Object. (/opt/nodejs/node_modules/datadog-lambda-js/handler.js:XXX:XXX)"," at Module._compile (internal/modules/cjs/loader.js:XXX:XXX)"," at Object.Module._extensions..js (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.load (internal/modules/cjs/loader.js:XXX:XXX)"," at Function.Module._load (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.require (internal/modules/cjs/loader.js:XXX:XXX)"]} { "traces": [ [ @@ -68,7 +68,7 @@ XXXX-XX-XX XX:XX:XX.XXX ERROR Uncaught Exception {"errorType":"Runtime.ImportMo ] ] } -XXXX-XX-XX XX:XX:XX.XXX ERROR Uncaught Exception {"errorType":"Runtime.ImportModuleError","errorMessage":"Error: Cannot find module 'esm'\nRequire stack:\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/handler.js\n- /var/runtime/UserFunction.js\n- /var/runtime/index.js","stack":["Runtime.ImportModuleError: Error: Cannot find module 'esm'","Require stack:","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/handler.js","- /var/runtime/UserFunction.js","- /var/runtime/index.js"," at ImportModuleError.ExtendedError [as constructor] (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at new ImportModuleError (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at _loadUserAppSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at loadSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at Object. (/opt/nodejs/node_modules/datadog-lambda-js/handler.js:XXX:XXX)"," at Module._compile (internal/modules/cjs/loader.js:XXX:XXX)"," at Object.Module._extensions..js (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.load (internal/modules/cjs/loader.js:XXX:XXX)"," at Function.Module._load (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.require (internal/modules/cjs/loader.js:XXX:XXX)"]} +XXXX-XX-XX XX:XX:XX.XXX ERROR Uncaught Exception {"errorType":"Runtime.ImportModuleError","errorMessage":"Error: Cannot find module 'esm'\nRequire stack:\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/trace/listener.js\n- /opt/nodejs/node_modules/datadog-lambda-js/trace/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/handler.js\n- /var/runtime/UserFunction.js\n- /var/runtime/index.js","stack":["Runtime.ImportModuleError: Error: Cannot find module 'esm'","Require stack:","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/trace/listener.js","- /opt/nodejs/node_modules/datadog-lambda-js/trace/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/handler.js","- /var/runtime/UserFunction.js","- /var/runtime/index.js"," at ImportModuleError.ExtendedError [as constructor] (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at new ImportModuleError (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at _loadUserAppSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at loadSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at Object. (/opt/nodejs/node_modules/datadog-lambda-js/handler.js:XXX:XXX)"," at Module._compile (internal/modules/cjs/loader.js:XXX:XXX)"," at Object.Module._extensions..js (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.load (internal/modules/cjs/loader.js:XXX:XXX)"," at Function.Module._load (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.require (internal/modules/cjs/loader.js:XXX:XXX)"]} { "traces": [ [ @@ -141,7 +141,7 @@ START Unknown application error occurred Runtime.ImportModuleError END Duration: XXXX ms Memory Used: XXXX MB -XXXX-XX-XX XX:XX:XX.XXX ERROR Uncaught Exception {"errorType":"Runtime.ImportModuleError","errorMessage":"Error: Cannot find module 'esm'\nRequire stack:\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/handler.js\n- /var/runtime/UserFunction.js\n- /var/runtime/index.js","stack":["Runtime.ImportModuleError: Error: Cannot find module 'esm'","Require stack:","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/handler.js","- /var/runtime/UserFunction.js","- /var/runtime/index.js"," at ImportModuleError.ExtendedError [as constructor] (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at new ImportModuleError (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at _loadUserAppSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at loadSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at Object. (/opt/nodejs/node_modules/datadog-lambda-js/handler.js:XXX:XXX)"," at Module._compile (internal/modules/cjs/loader.js:XXX:XXX)"," at Object.Module._extensions..js (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.load (internal/modules/cjs/loader.js:XXX:XXX)"," at Function.Module._load (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.require (internal/modules/cjs/loader.js:XXX:XXX)"]} +XXXX-XX-XX XX:XX:XX.XXX ERROR Uncaught Exception {"errorType":"Runtime.ImportModuleError","errorMessage":"Error: Cannot find module 'esm'\nRequire stack:\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/trace/listener.js\n- /opt/nodejs/node_modules/datadog-lambda-js/trace/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/handler.js\n- /var/runtime/UserFunction.js\n- /var/runtime/index.js","stack":["Runtime.ImportModuleError: Error: Cannot find module 'esm'","Require stack:","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/trace/listener.js","- /opt/nodejs/node_modules/datadog-lambda-js/trace/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/handler.js","- /var/runtime/UserFunction.js","- /var/runtime/index.js"," at ImportModuleError.ExtendedError [as constructor] (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at new ImportModuleError (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at _loadUserAppSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at loadSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at Object. (/opt/nodejs/node_modules/datadog-lambda-js/handler.js:XXX:XXX)"," at Module._compile (internal/modules/cjs/loader.js:XXX:XXX)"," at Object.Module._extensions..js (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.load (internal/modules/cjs/loader.js:XXX:XXX)"," at Function.Module._load (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.require (internal/modules/cjs/loader.js:XXX:XXX)"]} { "traces": [ [ @@ -215,7 +215,7 @@ Unknown application error occurred Runtime.ImportModuleError END Duration: XXXX ms Memory Used: XXXX MB -XXXX-XX-XX XX:XX:XX.XXX ERROR Uncaught Exception {"errorType":"Runtime.ImportModuleError","errorMessage":"Error: Cannot find module 'esm'\nRequire stack:\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/handler.js\n- /var/runtime/UserFunction.js\n- /var/runtime/index.js","stack":["Runtime.ImportModuleError: Error: Cannot find module 'esm'","Require stack:","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/handler.js","- /var/runtime/UserFunction.js","- /var/runtime/index.js"," at ImportModuleError.ExtendedError [as constructor] (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at new ImportModuleError (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at _loadUserAppSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at loadSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at Object. (/opt/nodejs/node_modules/datadog-lambda-js/handler.js:XXX:XXX)"," at Module._compile (internal/modules/cjs/loader.js:XXX:XXX)"," at Object.Module._extensions..js (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.load (internal/modules/cjs/loader.js:XXX:XXX)"," at Function.Module._load (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.require (internal/modules/cjs/loader.js:XXX:XXX)"]} +XXXX-XX-XX XX:XX:XX.XXX ERROR Uncaught Exception {"errorType":"Runtime.ImportModuleError","errorMessage":"Error: Cannot find module 'esm'\nRequire stack:\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/trace/listener.js\n- /opt/nodejs/node_modules/datadog-lambda-js/trace/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/handler.js\n- /var/runtime/UserFunction.js\n- /var/runtime/index.js","stack":["Runtime.ImportModuleError: Error: Cannot find module 'esm'","Require stack:","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/trace/listener.js","- /opt/nodejs/node_modules/datadog-lambda-js/trace/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/handler.js","- /var/runtime/UserFunction.js","- /var/runtime/index.js"," at ImportModuleError.ExtendedError [as constructor] (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at new ImportModuleError (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at _loadUserAppSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at loadSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at Object. (/opt/nodejs/node_modules/datadog-lambda-js/handler.js:XXX:XXX)"," at Module._compile (internal/modules/cjs/loader.js:XXX:XXX)"," at Object.Module._extensions..js (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.load (internal/modules/cjs/loader.js:XXX:XXX)"," at Function.Module._load (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.require (internal/modules/cjs/loader.js:XXX:XXX)"]} { "traces": [ [ @@ -284,7 +284,7 @@ XXXX-XX-XX XX:XX:XX.XXX ERROR Uncaught Exception {"errorType":"Runtime.ImportMo ] ] } -XXXX-XX-XX XX:XX:XX.XXX ERROR Uncaught Exception {"errorType":"Runtime.ImportModuleError","errorMessage":"Error: Cannot find module 'esm'\nRequire stack:\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/handler.js\n- /var/runtime/UserFunction.js\n- /var/runtime/index.js","stack":["Runtime.ImportModuleError: Error: Cannot find module 'esm'","Require stack:","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/handler.js","- /var/runtime/UserFunction.js","- /var/runtime/index.js"," at ImportModuleError.ExtendedError [as constructor] (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at new ImportModuleError (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at _loadUserAppSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at loadSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at Object. (/opt/nodejs/node_modules/datadog-lambda-js/handler.js:XXX:XXX)"," at Module._compile (internal/modules/cjs/loader.js:XXX:XXX)"," at Object.Module._extensions..js (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.load (internal/modules/cjs/loader.js:XXX:XXX)"," at Function.Module._load (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.require (internal/modules/cjs/loader.js:XXX:XXX)"]} +XXXX-XX-XX XX:XX:XX.XXX ERROR Uncaught Exception {"errorType":"Runtime.ImportModuleError","errorMessage":"Error: Cannot find module 'esm'\nRequire stack:\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/trace/listener.js\n- /opt/nodejs/node_modules/datadog-lambda-js/trace/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/handler.js\n- /var/runtime/UserFunction.js\n- /var/runtime/index.js","stack":["Runtime.ImportModuleError: Error: Cannot find module 'esm'","Require stack:","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/trace/listener.js","- /opt/nodejs/node_modules/datadog-lambda-js/trace/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/handler.js","- /var/runtime/UserFunction.js","- /var/runtime/index.js"," at ImportModuleError.ExtendedError [as constructor] (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at new ImportModuleError (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at _loadUserAppSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at loadSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at Object. (/opt/nodejs/node_modules/datadog-lambda-js/handler.js:XXX:XXX)"," at Module._compile (internal/modules/cjs/loader.js:XXX:XXX)"," at Object.Module._extensions..js (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.load (internal/modules/cjs/loader.js:XXX:XXX)"," at Function.Module._load (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.require (internal/modules/cjs/loader.js:XXX:XXX)"]} { "traces": [ [ @@ -358,7 +358,7 @@ Unknown application error occurred Runtime.ImportModuleError END Duration: XXXX ms Memory Used: XXXX MB -XXXX-XX-XX XX:XX:XX.XXX ERROR Uncaught Exception {"errorType":"Runtime.ImportModuleError","errorMessage":"Error: Cannot find module 'esm'\nRequire stack:\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/handler.js\n- /var/runtime/UserFunction.js\n- /var/runtime/index.js","stack":["Runtime.ImportModuleError: Error: Cannot find module 'esm'","Require stack:","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/handler.js","- /var/runtime/UserFunction.js","- /var/runtime/index.js"," at ImportModuleError.ExtendedError [as constructor] (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at new ImportModuleError (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at _loadUserAppSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at loadSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at Object. (/opt/nodejs/node_modules/datadog-lambda-js/handler.js:XXX:XXX)"," at Module._compile (internal/modules/cjs/loader.js:XXX:XXX)"," at Object.Module._extensions..js (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.load (internal/modules/cjs/loader.js:XXX:XXX)"," at Function.Module._load (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.require (internal/modules/cjs/loader.js:XXX:XXX)"]} +XXXX-XX-XX XX:XX:XX.XXX ERROR Uncaught Exception {"errorType":"Runtime.ImportModuleError","errorMessage":"Error: Cannot find module 'esm'\nRequire stack:\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js\n- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/trace/listener.js\n- /opt/nodejs/node_modules/datadog-lambda-js/trace/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/index.js\n- /opt/nodejs/node_modules/datadog-lambda-js/handler.js\n- /var/runtime/UserFunction.js\n- /var/runtime/index.js","stack":["Runtime.ImportModuleError: Error: Cannot find module 'esm'","Require stack:","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js","- /opt/nodejs/node_modules/datadog-lambda-js/runtime/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/trace/listener.js","- /opt/nodejs/node_modules/datadog-lambda-js/trace/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/index.js","- /opt/nodejs/node_modules/datadog-lambda-js/handler.js","- /var/runtime/UserFunction.js","- /var/runtime/index.js"," at ImportModuleError.ExtendedError [as constructor] (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at new ImportModuleError (/opt/nodejs/node_modules/datadog-lambda-js/runtime/errors.js:XXX:XXX)"," at _loadUserAppSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at loadSync (/opt/nodejs/node_modules/datadog-lambda-js/runtime/user-function.js:XXX:XXX)"," at Object. (/opt/nodejs/node_modules/datadog-lambda-js/handler.js:XXX:XXX)"," at Module._compile (internal/modules/cjs/loader.js:XXX:XXX)"," at Object.Module._extensions..js (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.load (internal/modules/cjs/loader.js:XXX:XXX)"," at Function.Module._load (internal/modules/cjs/loader.js:XXX:XXX)"," at Module.require (internal/modules/cjs/loader.js:XXX:XXX)"]} { "traces": [ [ diff --git a/integration_tests/snapshots/logs/process-input-traced_node12.log b/integration_tests/snapshots/logs/process-input-traced_node12.log index 42620e57..dbd718f5 100644 --- a/integration_tests/snapshots/logs/process-input-traced_node12.log +++ b/integration_tests/snapshots/logs/process-input-traced_node12.log @@ -136,6 +136,28 @@ START "start":XXXX, "duration":XXXX, "service": "integration-tests-js-XXXX-process-input-traced_node12" + }, + { + "trace_id":"XXXX", + "span_id":"XXXX", + "parent_id":"XXXX", + "name": "aws.lambda.load", + "resource": "integration-tests-js-XXXX-process-input-traced_node12", + "error": 0, + "meta": { + "service": "aws.lambda", + "runtime-id":"XXXX", + "operation_name": "aws.lambda.require", + "resource_names": "integration-tests-js-XXXX-process-input-traced_node12", + "language": "javascript" + }, + "metrics": { + "process_id":XXXX, + "_sampling_priority_v1": 1 + }, + "start":XXXX, + "duration":XXXX, + "service": "integration-tests-js-XXXX-process-input-traced_node12" } ] ] diff --git a/integration_tests/snapshots/logs/process-input-traced_node14.log b/integration_tests/snapshots/logs/process-input-traced_node14.log index 78b7a925..50d3fbed 100644 --- a/integration_tests/snapshots/logs/process-input-traced_node14.log +++ b/integration_tests/snapshots/logs/process-input-traced_node14.log @@ -136,6 +136,28 @@ START "start":XXXX, "duration":XXXX, "service": "integration-tests-js-XXXX-process-input-traced_node14" + }, + { + "trace_id":"XXXX", + "span_id":"XXXX", + "parent_id":"XXXX", + "name": "aws.lambda.load", + "resource": "integration-tests-js-XXXX-process-input-traced_node14", + "error": 0, + "meta": { + "service": "aws.lambda", + "runtime-id":"XXXX", + "operation_name": "aws.lambda.require", + "resource_names": "integration-tests-js-XXXX-process-input-traced_node14", + "language": "javascript" + }, + "metrics": { + "process_id":XXXX, + "_sampling_priority_v1": 1 + }, + "start":XXXX, + "duration":XXXX, + "service": "integration-tests-js-XXXX-process-input-traced_node14" } ] ] diff --git a/integration_tests/snapshots/logs/process-input-traced_node16.log b/integration_tests/snapshots/logs/process-input-traced_node16.log index 90ebc44f..48ff92aa 100644 --- a/integration_tests/snapshots/logs/process-input-traced_node16.log +++ b/integration_tests/snapshots/logs/process-input-traced_node16.log @@ -140,6 +140,29 @@ START "start":XXXX, "duration":XXXX, "service": "integration-tests-js-XXXX-process-input-traced_node16" + }, + { + "trace_id":"XXXX", + "span_id":"XXXX", + "parent_id":"XXXX", + "name": "aws.lambda.load", + "resource": "integration-tests-js-XXXX-process-input-traced_node16", + "error": 0, + "meta": { + "service": "aws.lambda", + "version": "1.0.0", + "runtime-id":"XXXX", + "operation_name": "aws.lambda.require", + "resource_names": "integration-tests-js-XXXX-process-input-traced_node16", + "language": "javascript" + }, + "metrics": { + "process_id":XXXX, + "_sampling_priority_v1": 1 + }, + "start":XXXX, + "duration":XXXX, + "service": "integration-tests-js-XXXX-process-input-traced_node16" } ] ] diff --git a/integration_tests/snapshots/logs/process-input-traced_node18.log b/integration_tests/snapshots/logs/process-input-traced_node18.log index 1854ad66..b3c80d66 100644 --- a/integration_tests/snapshots/logs/process-input-traced_node18.log +++ b/integration_tests/snapshots/logs/process-input-traced_node18.log @@ -140,6 +140,29 @@ START "start":XXXX, "duration":XXXX, "service": "integration-tests-js-XXXX-process-input-traced_node18" + }, + { + "trace_id":"XXXX", + "span_id":"XXXX", + "parent_id":"XXXX", + "name": "aws.lambda.load", + "resource": "integration-tests-js-XXXX-process-input-traced_node18", + "error": 0, + "meta": { + "service": "aws.lambda", + "version": "1.0.0", + "runtime-id":"XXXX", + "operation_name": "aws.lambda.require", + "resource_names": "integration-tests-js-XXXX-process-input-traced_node18", + "language": "javascript" + }, + "metrics": { + "process_id":XXXX, + "_sampling_priority_v1": 1 + }, + "start":XXXX, + "duration":XXXX, + "service": "integration-tests-js-XXXX-process-input-traced_node18" } ] ] diff --git a/integration_tests/snapshots/logs/status-code-500s_node12.log b/integration_tests/snapshots/logs/status-code-500s_node12.log index 4cefd8e0..2afd522d 100644 --- a/integration_tests/snapshots/logs/status-code-500s_node12.log +++ b/integration_tests/snapshots/logs/status-code-500s_node12.log @@ -96,6 +96,28 @@ START "duration":XXXX, "service": "aws.lambda", "type": "serverless" + }, + { + "trace_id":"XXXX", + "span_id":"XXXX", + "parent_id":"XXXX", + "name": "aws.lambda.load", + "resource": "integration-tests-js-XXXX-status-code-500s_node12", + "error": 0, + "meta": { + "service": "aws.lambda", + "runtime-id":"XXXX", + "operation_name": "aws.lambda.require", + "resource_names": "integration-tests-js-XXXX-status-code-500s_node12", + "language": "javascript" + }, + "metrics": { + "process_id":XXXX, + "_sampling_priority_v1": 1 + }, + "start":XXXX, + "duration":XXXX, + "service": "integration-tests-js-XXXX-status-code-500s_node12" } ] ] diff --git a/integration_tests/snapshots/logs/status-code-500s_node14.log b/integration_tests/snapshots/logs/status-code-500s_node14.log index 5d7e51e5..55fbaf8b 100644 --- a/integration_tests/snapshots/logs/status-code-500s_node14.log +++ b/integration_tests/snapshots/logs/status-code-500s_node14.log @@ -96,6 +96,28 @@ START "duration":XXXX, "service": "aws.lambda", "type": "serverless" + }, + { + "trace_id":"XXXX", + "span_id":"XXXX", + "parent_id":"XXXX", + "name": "aws.lambda.load", + "resource": "integration-tests-js-XXXX-status-code-500s_node14", + "error": 0, + "meta": { + "service": "aws.lambda", + "runtime-id":"XXXX", + "operation_name": "aws.lambda.require", + "resource_names": "integration-tests-js-XXXX-status-code-500s_node14", + "language": "javascript" + }, + "metrics": { + "process_id":XXXX, + "_sampling_priority_v1": 1 + }, + "start":XXXX, + "duration":XXXX, + "service": "integration-tests-js-XXXX-status-code-500s_node14" } ] ] @@ -191,6 +213,7 @@ START } END Duration: XXXX ms Memory Used: XXXX MB START +END Duration: XXXX ms Memory Used: XXXX MB { "e": XXXX, "m": "aws.lambda.enhanced.invocations", @@ -277,4 +300,3 @@ START ] ] } -END Duration: XXXX ms Memory Used: XXXX MB diff --git a/integration_tests/snapshots/logs/status-code-500s_node16.log b/integration_tests/snapshots/logs/status-code-500s_node16.log index 0c4cb637..b1738ac3 100644 --- a/integration_tests/snapshots/logs/status-code-500s_node16.log +++ b/integration_tests/snapshots/logs/status-code-500s_node16.log @@ -98,6 +98,29 @@ START "duration":XXXX, "service": "aws.lambda", "type": "serverless" + }, + { + "trace_id":"XXXX", + "span_id":"XXXX", + "parent_id":"XXXX", + "name": "aws.lambda.load", + "resource": "integration-tests-js-XXXX-status-code-500s_node16", + "error": 0, + "meta": { + "service": "aws.lambda", + "version": "1.0.0", + "runtime-id":"XXXX", + "operation_name": "aws.lambda.require", + "resource_names": "integration-tests-js-XXXX-status-code-500s_node16", + "language": "javascript" + }, + "metrics": { + "process_id":XXXX, + "_sampling_priority_v1": 1 + }, + "start":XXXX, + "duration":XXXX, + "service": "integration-tests-js-XXXX-status-code-500s_node16" } ] ] diff --git a/integration_tests/snapshots/logs/status-code-500s_node18.log b/integration_tests/snapshots/logs/status-code-500s_node18.log index 916415c5..0ee5b706 100644 --- a/integration_tests/snapshots/logs/status-code-500s_node18.log +++ b/integration_tests/snapshots/logs/status-code-500s_node18.log @@ -98,6 +98,29 @@ START "duration":XXXX, "service": "aws.lambda", "type": "serverless" + }, + { + "trace_id":"XXXX", + "span_id":"XXXX", + "parent_id":"XXXX", + "name": "aws.lambda.load", + "resource": "integration-tests-js-XXXX-status-code-500s_node18", + "error": 0, + "meta": { + "service": "aws.lambda", + "version": "1.0.0", + "runtime-id":"XXXX", + "operation_name": "aws.lambda.require", + "resource_names": "integration-tests-js-XXXX-status-code-500s_node18", + "language": "javascript" + }, + "metrics": { + "process_id":XXXX, + "_sampling_priority_v1": 1 + }, + "start":XXXX, + "duration":XXXX, + "service": "integration-tests-js-XXXX-status-code-500s_node18" } ] ] From 16b58490f48fb677ae67f1f877a7e8aac741aa4c Mon Sep 17 00:00:00 2001 From: AJ Stuyvenberg Date: Wed, 25 Jan 2023 09:35:30 -0500 Subject: [PATCH 08/11] feat: lint --- src/index.ts | 4 ++-- src/trace/listener.spec.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index 5852f10d..0df31b71 100644 --- a/src/index.ts +++ b/src/index.ts @@ -78,7 +78,7 @@ export const defaultConfig: Config = { mergeDatadogXrayTraces: false, shouldRetryMetrics: false, siteURL: "", - minColdStartTraceDuration: 3 + minColdStartTraceDuration: 3, } as const; let currentMetricsListener: MetricsListener | undefined; @@ -295,7 +295,7 @@ function getConfig(userConfig?: Partial): Config { } if (userConfig === undefined || userConfig.minColdStartTraceDuration === undefined) { - config.minColdStartTraceDuration = Number(getEnvValue(minColdStartTraceDurationEnvVar, '3')) + config.minColdStartTraceDuration = Number(getEnvValue(minColdStartTraceDurationEnvVar, "3")); } return config; diff --git a/src/trace/listener.spec.ts b/src/trace/listener.spec.ts index a286d9d0..92768e6b 100644 --- a/src/trace/listener.spec.ts +++ b/src/trace/listener.spec.ts @@ -81,7 +81,7 @@ describe("TraceListener", () => { decodeAuthorizerContext: true, mergeDatadogXrayTraces: false, injectLogContext: false, - minColdStartTraceDuration: 3 + minColdStartTraceDuration: 3, }; const context = { invokedFunctionArn: "arn:aws:lambda:us-east-1:123456789101:function:my-lambda", From f6bb14754ab6d9dea586767950f11bbbab46b794 Mon Sep 17 00:00:00 2001 From: AJ Stuyvenberg Date: Wed, 25 Jan 2023 09:49:07 -0500 Subject: [PATCH 09/11] feat: tsconfig --- src/trace/cold-start-tracer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/trace/cold-start-tracer.ts b/src/trace/cold-start-tracer.ts index 9da718c7..d7362510 100644 --- a/src/trace/cold-start-tracer.ts +++ b/src/trace/cold-start-tracer.ts @@ -28,7 +28,7 @@ export class ColdStartTracer { trace(rootNodes: RequireNode[]) { const coldStartSpanStartTime = rootNodes[0]?.startTime; const coldStartSpan = this.createColdStartSpan(coldStartSpanStartTime, this.parentSpan); - for (let coldStartNode of rootNodes) { + for (const coldStartNode of rootNodes) { this.traceTree(coldStartNode, coldStartSpan); } } @@ -41,7 +41,7 @@ export class ColdStartTracer { resource_names: this.lambdaFunctionName, "resource.name": this.lambdaFunctionName, }, - startTime: startTime, + startTime, }; if (parentSpan) { options.childOf = parentSpan.span; @@ -86,7 +86,7 @@ export class ColdStartTracer { ); newSpan?.finish(reqNode.endTime); if (reqNode.endTime - reqNode.startTime > this.minDuration) { - for (let node of reqNode.children || []) { + for (const node of reqNode.children || []) { this.traceTree(node, newSpan); } } From ffcbf9e17d33878c0b3123067c37b4f1b477dced Mon Sep 17 00:00:00 2001 From: AJ Stuyvenberg Date: Wed, 25 Jan 2023 11:38:15 -0500 Subject: [PATCH 10/11] feat: Optionaly skip libraries from cold start spans --- src/index.ts | 6 +++ src/trace/cold-start-tracer.spec.ts | 73 +++++++++++++++++++++++++++++ src/trace/cold-start-tracer.ts | 8 ++++ src/trace/listener.spec.ts | 1 + src/trace/listener.ts | 11 +++-- 5 files changed, 96 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index 0df31b71..a19ed8dc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,6 +39,7 @@ export const encodeAuthorizerContextEnvVar = "DD_ENCODE_AUTHORIZER_CONTEXT"; export const decodeAuthorizerContextEnvVar = "DD_DECODE_AUTHORIZER_CONTEXT"; export const coldStartTracingEnvVar = "DD_COLD_START_TRACING"; export const minColdStartTraceDurationEnvVar = "DD_MIN_COLD_START_DURATION"; +export const coldStartTraceSkipLibEnvVar = "DD_COLD_START_TRACE_SKIP_LIB"; interface GlobalConfig { /** @@ -79,6 +80,7 @@ export const defaultConfig: Config = { shouldRetryMetrics: false, siteURL: "", minColdStartTraceDuration: 3, + coldStartTraceSkipLib: "", } as const; let currentMetricsListener: MetricsListener | undefined; @@ -298,6 +300,10 @@ function getConfig(userConfig?: Partial): Config { config.minColdStartTraceDuration = Number(getEnvValue(minColdStartTraceDurationEnvVar, "3")); } + if (userConfig === undefined || userConfig.minColdStartTraceDuration === undefined) { + config.coldStartTraceSkipLib = getEnvValue(coldStartTraceSkipLibEnvVar, './opentracing/tracer'); + } + return config; } diff --git a/src/trace/cold-start-tracer.spec.ts b/src/trace/cold-start-tracer.spec.ts index 7e5b892a..4ad24da5 100644 --- a/src/trace/cold-start-tracer.spec.ts +++ b/src/trace/cold-start-tracer.spec.ts @@ -71,6 +71,7 @@ describe("ColdStartTracer", () => { lambdaFunctionName: "my-function-name", coldStartSpanFinishTime: 500, minDuration: 1, + ignoreLibs: '', }; const coldStartTracer = new ColdStartTracer(coldStartConfig); coldStartTracer.trace(requireNodes); @@ -121,4 +122,76 @@ describe("ColdStartTracer", () => { service: "aws.lambda", }); }); + + it("optionally skips libraries", () => { + const requireNodes: RequireNode[] = [ + { + id: "handler", + filename: "/var/task/handler.js", + startTime: 1, + endTime: 6, + children: [ + { + id: "myChildModule", + filename: "/opt/nodejs/node_modules/my-child-module.js", + startTime: 2, + endTime: 3, + }, + { + id: "myCoreModule", + filename: "http", + startTime: 4, + endTime: 5, + }, + { + id: "aws-sdk", + filename: "/var/runtime/aws-sdk", + startTime: 4, + endTime: 5, + }, + ], + } as any as RequireNode, + ]; + const coldStartConfig: ColdStartTracerConfig = { + tracerWrapper: new TracerWrapper(), + parentSpan: { + span: {}, + name: "my-lambda-span", + } as any as SpanWrapper, + lambdaFunctionName: "my-function-name", + coldStartSpanFinishTime: 500, + minDuration: 1, + ignoreLibs: 'myChildModule,myCoreModule', + }; + const coldStartTracer = new ColdStartTracer(coldStartConfig); + coldStartTracer.trace(requireNodes); + expect(mockStartSpan).toHaveBeenCalledTimes(3); + expect(mockFinishSpan).toHaveBeenCalledTimes(3); + const span1 = mockStartSpan.mock.calls[0]; + expect(span1[0]).toEqual("aws.lambda.load"); + expect(span1[1].tags).toEqual({ + operation_name: "aws.lambda.require", + "resource.name": "my-function-name", + resource_names: "my-function-name", + service: "aws.lambda", + }); + const span2 = mockStartSpan.mock.calls[1]; + expect(span2[0]).toEqual("aws.lambda.require"); + expect(span2[1].tags).toEqual({ + operation_name: "aws.lambda.require", + "resource.name": "handler", + resource_names: "handler", + service: "aws.lambda", + filename: "/var/task/handler.js", + }); + const span3 = mockStartSpan.mock.calls[2]; + expect(span3[0]).toEqual("aws.lambda.require_runtime"); + expect(span3[1].tags).toEqual({ + filename: "/var/runtime/aws-sdk", + operation_name: "aws.lambda.require_runtime", + "resource.name": "aws-sdk", + resource_names: "aws-sdk", + service: "aws.lambda", + }); + }); }); diff --git a/src/trace/cold-start-tracer.ts b/src/trace/cold-start-tracer.ts index d7362510..bf15fc99 100644 --- a/src/trace/cold-start-tracer.ts +++ b/src/trace/cold-start-tracer.ts @@ -8,6 +8,7 @@ export interface ColdStartTracerConfig { lambdaFunctionName?: string; coldStartSpanFinishTime: number; // Equivalent to the Lambda Span Start Time minDuration: number; + ignoreLibs: string } export class ColdStartTracer { @@ -16,6 +17,7 @@ export class ColdStartTracer { private lambdaFunctionName?: string; private coldStartSpanFinishTime: number; private minDuration: number; + private ignoreLibs: string[]; constructor(coldStartTracerConfig: ColdStartTracerConfig) { this.tracerWrapper = coldStartTracerConfig.tracerWrapper; @@ -23,6 +25,7 @@ export class ColdStartTracer { this.lambdaFunctionName = coldStartTracerConfig.lambdaFunctionName; this.coldStartSpanFinishTime = coldStartTracerConfig.coldStartSpanFinishTime; this.minDuration = coldStartTracerConfig.minDuration; + this.ignoreLibs = coldStartTracerConfig.ignoreLibs.split(','); } trace(rootNodes: RequireNode[]) { @@ -67,6 +70,11 @@ export class ColdStartTracer { if (reqNode.endTime - reqNode.startTime < this.minDuration) { return; } + + if (this.ignoreLibs.includes(reqNode.id)) { + return; + } + const options: SpanOptions = { tags: { service: "aws.lambda", diff --git a/src/trace/listener.spec.ts b/src/trace/listener.spec.ts index 92768e6b..c8824c09 100644 --- a/src/trace/listener.spec.ts +++ b/src/trace/listener.spec.ts @@ -82,6 +82,7 @@ describe("TraceListener", () => { mergeDatadogXrayTraces: false, injectLogContext: false, minColdStartTraceDuration: 3, + coldStartTraceSkipLib: '' }; const context = { invokedFunctionArn: "arn:aws:lambda:us-east-1:123456789101:function:my-lambda", diff --git a/src/trace/listener.ts b/src/trace/listener.ts index f16bf18c..8d50fce8 100644 --- a/src/trace/listener.ts +++ b/src/trace/listener.ts @@ -8,7 +8,7 @@ import { } from "./context"; import { patchHttp, unpatchHttp } from "./patch-http"; import { TraceContextService } from "./trace-context-service"; -import { extractTriggerTags, extractHTTPStatusCodeTag, eventSubTypes, parseEventSourceSubType } from "./trigger"; +import { extractTriggerTags, extractHTTPStatusCodeTag } from "./trigger"; import { ColdStartTracerConfig, ColdStartTracer } from "./cold-start-tracer"; import { logDebug, tagObject } from "../utils"; @@ -16,10 +16,10 @@ import { didFunctionColdStart } from "../utils/cold-start"; import { datadogLambdaVersion } from "../constants"; import { Source, ddtraceVersion, parentSpanFinishTimeHeader, authorizingRequestIdHeader } from "./constants"; import { patchConsole } from "./patch-console"; -import { SpanContext, SpanOptions, TraceOptions, TracerWrapper } from "./tracer-wrapper"; +import { SpanContext, TraceOptions, TracerWrapper } from "./tracer-wrapper"; import { SpanInferrer } from "./span-inferrer"; import { SpanWrapper } from "./span-wrapper"; -import { RequireNode, getTraceTree } from "../runtime/index"; +import { getTraceTree } from "../runtime/index"; export type TraceExtractor = (event: any, context: Context) => TraceContext; export interface TraceConfig { @@ -61,6 +61,10 @@ export interface TraceConfig { * Minimum duration dependency to trace */ minColdStartTraceDuration: number; + /** + * Libraries to ignore from cold start traces + */ + coldStartTraceSkipLib: string; } export class TraceListener { @@ -160,6 +164,7 @@ export class TraceListener { parentSpan: this.inferredSpan || this.wrappedCurrentSpan, lambdaFunctionName: this.context?.functionName, minDuration: this.config.minColdStartTraceDuration, + ignoreLibs: this.config.coldStartTraceSkipLib, }; const coldStartTracer = new ColdStartTracer(coldStartConfig); coldStartTracer.trace(coldStartNodes); From 9741d2c256f2bff47e621aa896aa0d852d6c551d Mon Sep 17 00:00:00 2001 From: AJ Stuyvenberg Date: Wed, 25 Jan 2023 11:40:38 -0500 Subject: [PATCH 11/11] feat: lint --- src/index.ts | 2 +- src/trace/cold-start-tracer.spec.ts | 4 ++-- src/trace/cold-start-tracer.ts | 4 ++-- src/trace/listener.spec.ts | 2 +- src/trace/listener.ts | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/index.ts b/src/index.ts index a19ed8dc..1caaccb2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -301,7 +301,7 @@ function getConfig(userConfig?: Partial): Config { } if (userConfig === undefined || userConfig.minColdStartTraceDuration === undefined) { - config.coldStartTraceSkipLib = getEnvValue(coldStartTraceSkipLibEnvVar, './opentracing/tracer'); + config.coldStartTraceSkipLib = getEnvValue(coldStartTraceSkipLibEnvVar, "./opentracing/tracer"); } return config; diff --git a/src/trace/cold-start-tracer.spec.ts b/src/trace/cold-start-tracer.spec.ts index 4ad24da5..c99848a1 100644 --- a/src/trace/cold-start-tracer.spec.ts +++ b/src/trace/cold-start-tracer.spec.ts @@ -71,7 +71,7 @@ describe("ColdStartTracer", () => { lambdaFunctionName: "my-function-name", coldStartSpanFinishTime: 500, minDuration: 1, - ignoreLibs: '', + ignoreLibs: "", }; const coldStartTracer = new ColdStartTracer(coldStartConfig); coldStartTracer.trace(requireNodes); @@ -161,7 +161,7 @@ describe("ColdStartTracer", () => { lambdaFunctionName: "my-function-name", coldStartSpanFinishTime: 500, minDuration: 1, - ignoreLibs: 'myChildModule,myCoreModule', + ignoreLibs: "myChildModule,myCoreModule", }; const coldStartTracer = new ColdStartTracer(coldStartConfig); coldStartTracer.trace(requireNodes); diff --git a/src/trace/cold-start-tracer.ts b/src/trace/cold-start-tracer.ts index bf15fc99..ecfa0d4a 100644 --- a/src/trace/cold-start-tracer.ts +++ b/src/trace/cold-start-tracer.ts @@ -8,7 +8,7 @@ export interface ColdStartTracerConfig { lambdaFunctionName?: string; coldStartSpanFinishTime: number; // Equivalent to the Lambda Span Start Time minDuration: number; - ignoreLibs: string + ignoreLibs: string; } export class ColdStartTracer { @@ -25,7 +25,7 @@ export class ColdStartTracer { this.lambdaFunctionName = coldStartTracerConfig.lambdaFunctionName; this.coldStartSpanFinishTime = coldStartTracerConfig.coldStartSpanFinishTime; this.minDuration = coldStartTracerConfig.minDuration; - this.ignoreLibs = coldStartTracerConfig.ignoreLibs.split(','); + this.ignoreLibs = coldStartTracerConfig.ignoreLibs.split(","); } trace(rootNodes: RequireNode[]) { diff --git a/src/trace/listener.spec.ts b/src/trace/listener.spec.ts index c8824c09..4f89f2ac 100644 --- a/src/trace/listener.spec.ts +++ b/src/trace/listener.spec.ts @@ -82,7 +82,7 @@ describe("TraceListener", () => { mergeDatadogXrayTraces: false, injectLogContext: false, minColdStartTraceDuration: 3, - coldStartTraceSkipLib: '' + coldStartTraceSkipLib: "", }; const context = { invokedFunctionArn: "arn:aws:lambda:us-east-1:123456789101:function:my-lambda", diff --git a/src/trace/listener.ts b/src/trace/listener.ts index 8d50fce8..262cc728 100644 --- a/src/trace/listener.ts +++ b/src/trace/listener.ts @@ -62,8 +62,8 @@ export interface TraceConfig { */ minColdStartTraceDuration: number; /** - * Libraries to ignore from cold start traces - */ + * Libraries to ignore from cold start traces + */ coldStartTraceSkipLib: string; }