diff --git a/README.md b/README.md
index b325a9119..b0a3f8afc 100644
--- a/README.md
+++ b/README.md
@@ -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.
For example, given the input payload:
{
"lv1" : {
"lv2": {
"lv3": "val"
}
}
}
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\"}`.
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.
@@ -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
diff --git a/src/index.ts b/src/index.ts
index e8aa40ef3..b3be30a39 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -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";
@@ -70,6 +71,7 @@ export const defaultConfig: Config = {
apiKeyKMS: "",
autoPatchHTTP: true,
captureLambdaPayload: false,
+ captureLambdaPayloadMaxDepth: 10,
createInferredSpan: true,
debugLogging: false,
encodeAuthorizerContext: true,
@@ -358,6 +360,10 @@ function getConfig(userConfig?: Partial): Config {
config.coldStartTraceSkipLib = getEnvValue(coldStartTraceSkipLibEnvVar, "./opentracing/tracer");
}
+ if (userConfig === undefined || userConfig.captureLambdaPayloadMaxDepth === undefined) {
+ config.captureLambdaPayloadMaxDepth = Number(getEnvValue(captureLambdaPayloadMaxDepthEnvVar, "10"));
+ }
+
return config;
}
diff --git a/src/trace/listener.spec.ts b/src/trace/listener.spec.ts
index 9dcd888e4..9c0da874d 100644
--- a/src/trace/listener.spec.ts
+++ b/src/trace/listener.spec.ts
@@ -69,6 +69,7 @@ describe("TraceListener", () => {
const defaultConfig = {
autoPatchHTTP: true,
captureLambdaPayload: false,
+ captureLambdaPayloadMaxDepth: 10,
createInferredSpan: true,
encodeAuthorizerContext: true,
decodeAuthorizerContext: true,
diff --git a/src/trace/listener.ts b/src/trace/listener.ts
index 3f9e5fd70..07eb41606 100644
--- a/src/trace/listener.ts
+++ b/src/trace/listener.ts
@@ -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";
@@ -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
@@ -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) {
diff --git a/src/utils/tag-object.spec.ts b/src/utils/tag-object.spec.ts
index 43b3387fd..df9de4859 100644
--- a/src/utils/tag-object.spec.ts
+++ b/src/utils/tag-object.spec.ts
@@ -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,
diff --git a/src/utils/tag-object.ts b/src/utils/tag-object.ts
index bd9f4417d..2b63a0479 100644
--- a/src/utils/tag-object.ts
+++ b/src/utils/tag-object.ts
@@ -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 {
@@ -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;
}
}