Skip to content

Commit

Permalink
Merge 9be5811 into 9534c00
Browse files Browse the repository at this point in the history
  • Loading branch information
joeyzhao2018 authored Nov 20, 2023
2 parents 9534c00 + 9be5811 commit 8ed18b2
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 12 deletions.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ Follow the [configuration instructions](https://docs.datadoghq.com/serverless/co

For additional tracing configuration options, check out the [official documentation for Datadog trace client](https://datadoghq.dev/dd-trace-js/).

Besides the environment variables supported by dd-trace-js, the datadog-lambda-js library added following environment variables.


| Environment Variables | Description | Default Value |
| -------------------- | ------------ | ------------- |
| DD_ENCODE_AUTHORIZER_CONTEXT | When set to `true` for Lambda authorizers, the tracing context will be encoded into the response for propagation. Supported for NodeJS and Python. | `true` |
| DD_DECODE_AUTHORIZER_CONTEXT | When set to `true` for Lambdas that are authorized via Lambda authorizers, it will parse and use the encoded tracing context (if found). Supported for NodeJS and Python. | `true` |
| DD_COLD_START_TRACING | Set to `false` to disable Cold Start Tracing. Used in NodeJS and Python. | `true` |
| DD_MIN_COLD_START_DURATION | Sets the minimum duration (in milliseconds) for a module load event to be traced via Cold Start Tracing. Number. | `3` |
| DD_COLD_START_TRACE_SKIP_LIB | optionally skip creating Cold Start Spans for a comma-separated list of libraries. Useful to limit depth or skip known libraries. | `./opentracing/tracer` |
| DD_CAPTURE_LAMBDA_PAYLOAD | [Captures incoming and outgoing AWS Lambda payloads][1] in the Datadog APM spans for Lambda invocations. | `false` |
| DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH | Determines the level of detail captured from AWS Lambda payloads, which are then assigned as tags for the `aws.lambda` span. It specifies the nesting depth of the JSON payload structure to process. Once the specified maximum depth is reached, the tag's value is set to the stringified value of any nested elements beyond this level. <br> For example, given the input payload: <pre>{<br> "lv1" : {<br> "lv2": {<br> "lv3": "val"<br> }<br> }<br>}</pre> If the depth is set to `2`, the resulting tag's key is set to `function.request.lv1.lv2` and the value is `{\"lv3\": \"val\"}`. <br> If the depth is set to `0`, the resulting tag's key is set to `function.request` and value is `{\"lv1\":{\"lv2\":{\"lv3\": \"val\"}}}` | `10` |


## Lambda Profiling Beta

Datadog's [Continuous Profiler](https://www.datadoghq.com/product/code-profiling/) is now available in beta for NodeJS in version 6.87.0 and layer version 87 and above. This optional feature is enabled by setting the `DD_PROFILING_ENABLED` environment variable to `true`. During the beta period, profiling is available at no additional cost.
Expand All @@ -32,7 +46,7 @@ The first 5.x.x version was released with Lambda Layer version `69`.

### 6.x.x

The 6.x.x release introduces support for the node 16 runtime and esm modules.
The 6.x.x release introduces support for the node 16 runtime and esm modules.

### 7.x.x

Expand Down
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export { TraceHeaders } from "./trace";
export const apiKeyEnvVar = "DD_API_KEY";
export const apiKeyKMSEnvVar = "DD_KMS_API_KEY";
export const captureLambdaPayloadEnvVar = "DD_CAPTURE_LAMBDA_PAYLOAD";
export const captureLambdaPayloadMaxDepthEnvVar = "DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH";
export const traceManagedServicesEnvVar = "DD_TRACE_MANAGED_SERVICES";
export const siteURLEnvVar = "DD_SITE";
export const logLevelEnvVar = "DD_LOG_LEVEL";
Expand Down Expand Up @@ -70,6 +71,7 @@ export const defaultConfig: Config = {
apiKeyKMS: "",
autoPatchHTTP: true,
captureLambdaPayload: false,
captureLambdaPayloadMaxDepth: 10,
createInferredSpan: true,
debugLogging: false,
encodeAuthorizerContext: true,
Expand Down Expand Up @@ -358,6 +360,10 @@ function getConfig(userConfig?: Partial<Config>): Config {
config.coldStartTraceSkipLib = getEnvValue(coldStartTraceSkipLibEnvVar, "./opentracing/tracer");
}

if (userConfig === undefined || userConfig.captureLambdaPayloadMaxDepth === undefined) {
config.captureLambdaPayloadMaxDepth = Number(getEnvValue(captureLambdaPayloadMaxDepthEnvVar, "10"));
}

return config;
}

Expand Down
1 change: 1 addition & 0 deletions src/trace/listener.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ describe("TraceListener", () => {
const defaultConfig = {
autoPatchHTTP: true,
captureLambdaPayload: false,
captureLambdaPayloadMaxDepth: 10,
createInferredSpan: true,
encodeAuthorizerContext: true,
decodeAuthorizerContext: true,
Expand Down
17 changes: 14 additions & 3 deletions src/trace/listener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { patchHttp, unpatchHttp } from "./patch-http";
import { TraceContextService } from "./trace-context-service";
import { extractTriggerTags, extractHTTPStatusCodeTag } from "./trigger";
import { ColdStartTracerConfig, ColdStartTracer } from "./cold-start-tracer";

import { logDebug, tagObject } from "../utils";
import { didFunctionColdStart, isProactiveInitialization } from "../utils/cold-start";
import { datadogLambdaVersion } from "../constants";
Expand All @@ -29,6 +28,12 @@ export interface TraceConfig {
/**
* Whether to capture the lambda payload and response in Datadog.
*/
captureLambdaPayloadMaxDepth: number;
/**
* The captured AWS Lambda payloads will become tags of the `aws.lambda` span. This sets how deep
* it fathoms the JSON structure. When the max depth reached, the tag's value will be the
* stringified value of the deeper nested items.
*/
captureLambdaPayload: boolean;
/**
* Whether to create inferred spans for managed services
Expand Down Expand Up @@ -151,8 +156,14 @@ export class TraceListener {
if (!this.tracerWrapper.currentSpan) return false;
this.wrappedCurrentSpan = new SpanWrapper(this.tracerWrapper.currentSpan, {});
if (this.config.captureLambdaPayload) {
tagObject(this.tracerWrapper.currentSpan, "function.request", event);
tagObject(this.tracerWrapper.currentSpan, "function.response", result);
tagObject(this.tracerWrapper.currentSpan, "function.request", event, 0, this.config.captureLambdaPayloadMaxDepth);
tagObject(
this.tracerWrapper.currentSpan,
"function.response",
result,
0,
this.config.captureLambdaPayloadMaxDepth,
);
}
const coldStartNodes = getTraceTree();
if (coldStartNodes.length > 0) {
Expand Down
33 changes: 33 additions & 0 deletions src/utils/tag-object.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,39 @@ describe("tagObject", () => {
["lambda_payload.request.vals.1.thingTwo", "2"],
]);
});
it("tags reach max depth", () => {
const span = {
setTag,
};

tagObject(
span,
"function.request",
{
hello: "world",
level1: {
level2_dict: {
level3: 3,
},
level2_list: [null, true, "nice", { l3: "v3" }],
level2_bool: true,
level2_int: 2,
},
vals: [{ thingOne: 1 }, { thingTwo: 2 }],
},
0,
2,
);
expect(setTag.mock.calls).toEqual([
["function.request.hello", "world"],
["function.request.level1.level2_dict", '{"level3":3}'],
["function.request.level1.level2_list", '[null,true,"nice",{"l3":"v3"}]'],
["function.request.level1.level2_bool", "true"],
["function.request.level1.level2_int", "2"],
["function.request.vals.0", '{"thingOne":1}'],
["function.request.vals.1", '{"thingTwo":2}'],
]);
});
it("redacts common secret keys", () => {
const span = {
setTag,
Expand Down
13 changes: 5 additions & 8 deletions src/utils/tag-object.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
const redactableKeys = ["authorization", "x-authorization", "password", "token"];
const maxDepth = 10;

export function tagObject(currentSpan: any, key: string, obj: any, depth = 0): any {
export function tagObject(currentSpan: any, key: string, obj: any, depth = 0, maxDepth = 10): any {
if (depth >= maxDepth) {
return;
} else {
depth += 1;
return currentSpan.setTag(key, redactVal(key, JSON.stringify(obj).substring(0, 5000)));
}
if (obj === null) {
return currentSpan.setTag(key, obj);
}
depth += 1;
if (typeof obj === "string") {
let parsed: string;
try {
Expand All @@ -18,16 +16,15 @@ export function tagObject(currentSpan: any, key: string, obj: any, depth = 0): a
const redacted = redactVal(key, obj.substring(0, 5000));
return currentSpan.setTag(key, redacted);
}
return tagObject(currentSpan, key, parsed, depth);
return tagObject(currentSpan, key, parsed, depth, maxDepth);
}
if (typeof obj === "number" || typeof obj === "boolean") {
return currentSpan.setTag(key, obj.toString());
}
if (typeof obj === "object") {
for (const [k, v] of Object.entries(obj)) {
tagObject(currentSpan, `${key}.${k}`, v, depth);
tagObject(currentSpan, `${key}.${k}`, v, depth, maxDepth);
}
return;
}
}

Expand Down

0 comments on commit 8ed18b2

Please sign in to comment.