diff --git a/src/index.ts b/src/index.ts index 8fca98c7..3e3a25d6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -35,6 +35,7 @@ export const mergeXrayTracesEnvVar = "DD_MERGE_XRAY_TRACES"; 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"; interface GlobalConfig { /** @@ -66,6 +67,7 @@ export const defaultConfig: Config = { createInferredSpan: true, debugLogging: false, encodeAuthorizerContext: true, + decodeAuthorizerContext: true, enhancedMetrics: true, forceWrap: false, injectLogContext: true, @@ -280,6 +282,11 @@ function getConfig(userConfig?: Partial): Config { config.encodeAuthorizerContext = result === "true"; } + if (userConfig === undefined || userConfig.decodeAuthorizerContext === undefined) { + const result = getEnvValue(decodeAuthorizerContextEnvVar, "true").toLowerCase(); + config.decodeAuthorizerContext = result === "true"; + } + return config; } diff --git a/src/trace/context.ts b/src/trace/context.ts index 28bfe784..1396ca0d 100644 --- a/src/trace/context.ts +++ b/src/trace/context.ts @@ -26,9 +26,8 @@ import { xrayTraceEnvVar, } from "./constants"; import { TraceExtractor } from "./listener"; -import { eventTypes, parseEventSource, parseEventSourceSubType, eventSubTypes } from "./trigger"; +import { parseEventSourceSubType, eventSubTypes } from "./trigger"; import { authorizingRequestIdHeader } from "./constants"; -import { datadog } from "../index"; export interface XRayTraceHeader { traceID: string; @@ -59,6 +58,7 @@ export function extractTraceContext( event: any, context: Context, extractor?: TraceExtractor, + decodeAuthorizerContext: boolean = true, ): TraceContext | undefined { let trace; @@ -74,7 +74,7 @@ export function extractTraceContext( } if (!trace) { - trace = readTraceFromEvent(event); + trace = readTraceFromEvent(event, decodeAuthorizerContext); } if (!trace) { @@ -202,7 +202,7 @@ export function sendXraySubsegment(segment: string) { export function readTraceFromAppSyncEvent(event: any): TraceContext | undefined { event.headers = event.request.headers; - return readTraceFromHTTPEvent(event); + return readTraceFromHTTPEvent(event, false); } export function readTraceFromSQSEvent(event: SQSEvent): TraceContext | undefined { @@ -365,16 +365,18 @@ export function getInjectedAuthorizerData(event: any, eventSourceSubType: eventS } } -export function readTraceFromHTTPEvent(event: any): TraceContext | undefined { - // need to set the trace context if using authorizer lambda in authorizing (non-cached) cases - try { - const eventSourceSubType: eventSubTypes = parseEventSourceSubType(event); - const injectedAuthorizerData = getInjectedAuthorizerData(event, eventSourceSubType); - if (injectedAuthorizerData !== null) { - return exportTraceData(injectedAuthorizerData); +export function readTraceFromHTTPEvent(event: any, decodeAuthorizerContext: boolean = true): TraceContext | undefined { + if (decodeAuthorizerContext) { + // need to set the trace context if using authorizer lambda in authorizing (non-cached) cases + try { + const eventSourceSubType: eventSubTypes = parseEventSourceSubType(event); + const injectedAuthorizerData = getInjectedAuthorizerData(event, eventSourceSubType); + if (injectedAuthorizerData !== null) { + return exportTraceData(injectedAuthorizerData); + } + } catch (error) { + logDebug(`unable to extract trace context from authorizer event.`, { error }); } - } catch (error) { - logDebug(`unable to extract trace context from authorizer event.`, { error }); } const headers = event.headers; @@ -390,13 +392,13 @@ export function readTraceFromHTTPEvent(event: any): TraceContext | undefined { return trace; } -export function readTraceFromEvent(event: any): TraceContext | undefined { +export function readTraceFromEvent(event: any, decodeAuthorizerContext: boolean = true): TraceContext | undefined { if (!event || typeof event !== "object") { return; } if (event.headers !== null && typeof event.headers === "object") { - return readTraceFromHTTPEvent(event); + return readTraceFromHTTPEvent(event, decodeAuthorizerContext); } if (isSNSEvent(event)) { diff --git a/src/trace/listener.spec.ts b/src/trace/listener.spec.ts index f592b88a..b6f30cc1 100644 --- a/src/trace/listener.spec.ts +++ b/src/trace/listener.spec.ts @@ -78,6 +78,7 @@ describe("TraceListener", () => { captureLambdaPayload: false, createInferredSpan: true, encodeAuthorizerContext: true, + decodeAuthorizerContext: true, mergeDatadogXrayTraces: false, injectLogContext: false, }; diff --git a/src/trace/listener.ts b/src/trace/listener.ts index fdad16b5..a335e181 100644 --- a/src/trace/listener.ts +++ b/src/trace/listener.ts @@ -38,6 +38,10 @@ export interface TraceConfig { * Whether to encode trace context in authorizer metadata */ encodeAuthorizerContext: boolean; + /** + * Whether to decode trace context in authorizer metadata + */ + decodeAuthorizerContext: boolean; /** * Whether to automatically patch console.log with Datadog's tracing ids. */ @@ -90,7 +94,12 @@ export class TraceListener { } else { logDebug("Not patching HTTP libraries", { autoPatchHTTP: this.config.autoPatchHTTP, tracerInitialized }); } - const rootTraceHeaders = this.contextService.extractHeadersFromContext(event, context, this.config.traceExtractor); + const rootTraceHeaders = this.contextService.extractHeadersFromContext( + event, + context, + this.config.traceExtractor, + this.config.decodeAuthorizerContext, + ); // The aws.lambda span needs to have a parented to the Datadog trace context from the // incoming event if available or the X-Ray trace context if hybrid tracing is enabled let parentSpanContext: SpanContext | undefined; @@ -104,7 +113,12 @@ export class TraceListener { }); } if (this.config.createInferredSpan) { - this.inferredSpan = this.inferrer.createInferredSpan(event, context, parentSpanContext); + this.inferredSpan = this.inferrer.createInferredSpan( + event, + context, + parentSpanContext, + this.config.encodeAuthorizerContext, + ); } this.lambdaSpanParentContext = this.inferredSpan?.span || parentSpanContext; this.context = context; diff --git a/src/trace/span-inferrer.spec.ts b/src/trace/span-inferrer.spec.ts index b3abbdb4..656d52c5 100644 --- a/src/trace/span-inferrer.spec.ts +++ b/src/trace/span-inferrer.spec.ts @@ -510,36 +510,13 @@ describe("Authorizer Spans", () => { ]); }); - it("creates an inferred span for API Gateway V2 event with traced authorizers [Request Type]", () => { + it("connects the inferred span for API Gateway V2 event with traced authorizers [Request Type]", () => { const inferrer = new SpanInferrer(mockWrapperWithFinish as unknown as TracerWrapper); inferrer.createInferredSpan(apiGatewayV2RequestAuthorizer, {} as any, {} as SpanContext); expect(mockWrapperWithFinish.startSpan.mock.calls[0]).toEqual([ - "aws.apigateway.authorizer", - { - childOf: {}, - startTime: 1665596771812, - tags: { - _inferred_span: { synchronicity: "sync", tag_source: "self" }, - apiid: "l9flvsey83", - domain_name: "l9flvsey83.execute-api.sa-east-1.amazonaws.com", - endpoint: "/hello", - "http.method": "GET", - "http.url": "l9flvsey83.execute-api.sa-east-1.amazonaws.com/hello", - operation_name: "aws.apigateway", - request_id: undefined, - "resource.name": "GET /hello", - resource_names: "GET /hello", - service: "l9flvsey83.execute-api.sa-east-1.amazonaws.com", - "service.name": "l9flvsey83.execute-api.sa-east-1.amazonaws.com", - "span.type": "http", - stage: "$default", - }, - }, - ]); - expect(mockWrapperWithFinish.startSpan.mock.calls[1]).toEqual([ "aws.apigateway", { - childOf: { finish: mockFinish }, // Hack around jest mocks + childOf: {}, startTime: 1665596771812, tags: { _inferred_span: { synchronicity: "sync", tag_source: "self" }, @@ -548,7 +525,7 @@ describe("Authorizer Spans", () => { endpoint: "/hello", "http.method": "GET", "http.url": "l9flvsey83.execute-api.sa-east-1.amazonaws.com/hello", - operation_name: "aws.apigateway", + operation_name: "aws.httpapi", request_id: undefined, "resource.name": "GET /hello", resource_names: "GET /hello", diff --git a/src/trace/span-inferrer.ts b/src/trace/span-inferrer.ts index 0b823c1a..31c3b8f0 100644 --- a/src/trace/span-inferrer.ts +++ b/src/trace/span-inferrer.ts @@ -14,6 +14,7 @@ import { SpanWrapper } from "./span-wrapper"; import { parentSpanFinishTimeHeader } from "./constants"; import { logDebug } from "../utils"; import { getInjectedAuthorizerData } from "./context"; +import { decodeAuthorizerContextEnvVar } from "../index"; export class SpanInferrer { traceWrapper: TracerWrapper; @@ -21,13 +22,18 @@ export class SpanInferrer { this.traceWrapper = traceWrapper; } - public createInferredSpan(event: any, context: Context | undefined, parentSpanContext: SpanContext | undefined): any { + public createInferredSpan( + event: any, + context: Context | undefined, + parentSpanContext: SpanContext | undefined, + decodeAuthorizerContext: boolean = true, + ): any { const eventSource = parseEventSource(event); if (eventSource === eventTypes.lambdaUrl) { return this.createInferredSpanForLambdaUrl(event, context); } if (eventSource === eventTypes.apiGateway) { - return this.createInferredSpanForApiGateway(event, context, parentSpanContext); + return this.createInferredSpanForApiGateway(event, context, parentSpanContext, decodeAuthorizerContext); } if (eventSource === eventTypes.sns) { return this.createInferredSpanForSns(event, context, parentSpanContext); @@ -60,6 +66,7 @@ export class SpanInferrer { event: any, context: Context | undefined, parentSpanContext: SpanContext | undefined, + decodeAuthorizerContext: boolean = true, ): SpanWrapper { const options: SpanOptions = {}; const domain = event.requestContext.domainName; @@ -102,47 +109,47 @@ export class SpanInferrer { options.tags.event_type = event.requestContext.eventType; } let upstreamAuthorizerSpan: SpanWrapper | undefined; - try { - const eventSourceSubType: eventSubTypes = parseEventSourceSubType(event); - const parsedUpstreamContext = getInjectedAuthorizerData(event, eventSourceSubType); - - if (parsedUpstreamContext) { - let upstreamSpanOptions: SpanOptions = {}; - const startTime = parsedUpstreamContext[parentSpanFinishTimeHeader] / 1e6; - upstreamSpanOptions = { - startTime, - tags: { operation_name: "aws.apigateway.authorizer", ...options.tags }, - }; - - let endTime: number; - // getting an approximated endTime - if (eventSourceSubType === eventSubTypes.apiGatewayV2) { - endTime = startTime; - } else { - endTime = event.requestContext.requestTimeEpoch + event.requestContext.authorizer.integrationLatency; + const eventSourceSubType: eventSubTypes = parseEventSourceSubType(event); + if (decodeAuthorizerContext) { + try { + const parsedUpstreamContext = getInjectedAuthorizerData(event, eventSourceSubType); + if (parsedUpstreamContext) { + let upstreamSpanOptions: SpanOptions = {}; + const startTime = parsedUpstreamContext[parentSpanFinishTimeHeader] / 1e6; + // getting an approximated endTime + if (eventSourceSubType === eventSubTypes.apiGatewayV2) { + options.startTime = startTime; // not inserting authorizer span + options.tags.operation_name = "aws.httpapi"; + } else { + upstreamSpanOptions = { + startTime, + childOf: parentSpanContext, + tags: { operation_name: "aws.apigateway.authorizer", ...options.tags }, + }; + upstreamAuthorizerSpan = new SpanWrapper( + this.traceWrapper.startSpan("aws.apigateway.authorizer", upstreamSpanOptions), + { isAsync: false }, + ); + const endTime = event.requestContext.requestTimeEpoch + event.requestContext.authorizer.integrationLatency; + upstreamAuthorizerSpan.finish(endTime); + options.startTime = endTime; // For the main function's inferred span + } } + } catch (error) { + logDebug("Error decoding authorizer span", error as Error); + } + } - upstreamSpanOptions.childOf = parentSpanContext; - upstreamAuthorizerSpan = new SpanWrapper( - this.traceWrapper.startSpan("aws.apigateway.authorizer", upstreamSpanOptions), - { isAsync: false }, - ); - upstreamAuthorizerSpan.finish(endTime); - options.startTime = endTime; + if (!options.startTime) { + if ( + eventSourceSubType === eventSubTypes.apiGatewayV1 || + eventSourceSubType === eventSubTypes.apiGatewayWebsocket + ) { + options.startTime = event.requestContext.requestTimeEpoch; } else { - if ( - eventSourceSubType === eventSubTypes.apiGatewayV1 || - eventSourceSubType === eventSubTypes.apiGatewayWebsocket - ) { - options.startTime = event.requestContext.requestTimeEpoch; - } else { - options.startTime = event.requestContext.timeEpoch; - } + options.startTime = event.requestContext.timeEpoch; } - } catch (error) { - logDebug("Error decoding authorizer span", error as Error); } - options.childOf = upstreamAuthorizerSpan ? upstreamAuthorizerSpan.span : parentSpanContext; const spanWrapperOptions = { diff --git a/src/trace/trace-context-service.ts b/src/trace/trace-context-service.ts index bdec7989..72088582 100644 --- a/src/trace/trace-context-service.ts +++ b/src/trace/trace-context-service.ts @@ -25,8 +25,9 @@ export class TraceContextService { event: any, context: Context, extractor?: TraceExtractor, + decodeAuthorizerContext: boolean = true, ): Partial | undefined { - this.rootTraceContext = extractTraceContext(event, context, extractor); + this.rootTraceContext = extractTraceContext(event, context, extractor, decodeAuthorizerContext); return this.currentTraceHeaders; }