[SVLS-9189] feat(logs): add config param DD_LAMBDA_DURABLE_FUNCTION_LOG_BUFFER_SIZE - #1239
Conversation
Adds `durable_function_log_buffer_size` (env: `DD_DURABLE_FUNCTION_LOG_BUFFER_SIZE`, default: 50) to control the max number of request ID keys held in `held_logs` waiting for durable execution context. Setting it to 0 disables holding entirely, so logs are flushed immediately without durable context enrichment — useful when the tracer is not installed and the buffer would otherwise delay log delivery until full or until extension shutdown. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
Default to 0 (disabled) until tracer-side durable execution support is released. Customers can set DD_DURABLE_FUNCTION_LOG_BUFFER_SIZE=50 to opt into context enrichment once the tracer is available. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a new DD_DURABLE_FUNCTION_LOG_BUFFER_SIZE configuration option that controls the maximum number of request IDs whose logs are held in the durable-function log buffer. The previous hardcoded constant (HELD_LOGS_MAX_KEYS = 50) is replaced by this configurable value, defaulted to 0, which bypasses the hold mechanism entirely so logs are flushed immediately without durable execution context enrichment. This avoids missing-log issues when the tracer is not installed on durable functions.
Changes:
- New
durable_function_log_buffer_sizeconfig field (env + yaml + default), defaulting to 0. LambdaProcessorreads this value, replacing theHELD_LOGS_MAX_KEYSconstant, and short-circuits the hold path when set to 0.- Existing durable-arn tests explicitly set
held_logs_max_keys = 50to preserve prior behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| bottlecap/src/config/mod.rs | Adds durable_function_log_buffer_size: usize field with doc and default 0. |
| bottlecap/src/config/env.rs | Adds env var deserialization, merge call, and updates default-config test. |
| bottlecap/src/config/yaml.rs | Updates yaml test expected struct with new field. |
| bottlecap/src/logs/lambda/processor.rs | Replaces constant with configurable cap; adds zero-disables fast path; updates tests. |
|
|
||
| let processing_rules = &datadog_config.logs_config_processing_rules; | ||
| let logs_enabled = datadog_config.serverless_logs_enabled; | ||
| let held_logs_max_keys = datadog_config.durable_function_log_buffer_size; |
There was a problem hiding this comment.
nit: rename held_logs_max_keys to durable_function_log_buffer_size or sth. similar for better readability.
There was a problem hiding this comment.
Renamed it as lambda_durable_function_log_buffer_size
| /// durable execution context enrichment. Useful when the tracer is not installed. | ||
| /// Default is `0`. | ||
| #[serde(deserialize_with = "deserialize_option_lossless")] | ||
| pub durable_function_log_buffer_size: Option<usize>, |
There was a problem hiding this comment.
Wondering if this should include the LAMBDA prefix, since we're gonna migrate to the config package from serverless-components eventually,
that way it makes it more descriptive that its for the lambda product
There was a problem hiding this comment.
Renamed it as lambda_durable_function_log_buffer_size
There was a problem hiding this comment.
I see it only in the processor, not the config
…log_buffer_size Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lambda_durable_function_log_buffer_size Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
litianningdatadog
left a comment
There was a problem hiding this comment.
left some comments
…e loop Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…m 0 to 5 (#1324) ## Background `DD_LAMBDA_DURABLE_FUNCTION_LOG_BUFFER_SIZE` means how many logs (in terms of number of invocations) the extension buffers to wait for enrichment. If it's N (which is 5 right now), then the extension holds all logs for up to N invocations, waits for traces for these invocations to be sent from the tracer, then uses the metadata from the traces to enrich the logs. If the function is not a durable function, then the extension holds all logs (for up to N invocations) at cold start. Then, as soon as it receives the `platform.InitStart` event and learns that the function is not a durable function, it releases all held logs and no longer holds logs. Ideally, its default value should be a non-zero value, so the behavior is correct for both durable functions and non-durable functions, i.e.: - logs for durable functions are held, and - logs for non-durable functions are not held. Actually, the default was 5 at the beginning. However, when the log holding logic was released, tracer-side changes that adds the necessary metadata to traces had not been released. As a result, the extension never gets the metadata it's waiting for, and some logs are somehow dropped. Therefore, #1239 changes the default back to 0 as a temporary mitigation of this issue. Now, all the tracer-side changes (`datadog-lambda-js`, `datadog-lambda-python`) have been released, so we can change the default back to 5. The dropped-logs problem won't happen as long as the user uses the latest version for the tracer. ## Overview Changes the default of `lambda_durable_function_log_buffer_size` (`DD_LAMBDA_DURABLE_FUNCTION_LOG_BUFFER_SIZE`) from `0` to `5`, so instrumenting a durable function no longer requires setting this env var to get logs enriched with durable execution context. ## Testing - `cargo test --lib` — 541 passed, 0 failed - `cargo clippy --all-targets -- -D warnings` — clean - `cargo fmt --check` — clean No manual test on a live durable function yet. ### Existing integration tests **Non-durable functions — covered.** These suites ensures logs for non-durable functions are flushed correctly: - `integration-tests/tests/on-demand.test.ts` — node/python/java/dotnet, 2 invocations each; asserts the `Hello world!` log is retrievable by request ID for both the cold and warm invocation. - `integration-tests/tests/lmi.test.ts` — Managed Instance mode; asserts logs exist and that the `Hello world!` log is present. - `integration-tests/tests/auth.test.ts` — asserts `logs.length > 0` under delegated auth. **Durable functions — not covered**. However, we have done lots of manual tests using `DD_LAMBDA_DURABLE_FUNCTION_LOG_BUFFER_SIZE == 5` and ensured this works well for durable functions. ## Next steps: Update onboarding doc saying if extension v100+ is used, then the user doesn't need to set `DD_LAMBDA_DURABLE_FUNCTION_LOG_BUFFER_SIZE` to 5 since it's 5 by default. 🤖 Partially generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Background
For durable functions, the extension holds logs and waits to get the invocation context from the tracer so the extension can enrich the logs.
Problem
When tracing is off (which is not recommended for durable functions), logs are flushed when:
This PR
lambda_durable_function_log_buffer_sizeconfig field (env varDD_LAMBDA_DURABLE_FUNCTION_LOG_BUFFER_SIZE, default:0) controlling the max number of request ID keys held inheld_logs.0, logs bypass the hold mechanism and are sent immediately without durable execution context enrichment.Test plan
Steps
Result
Before:
After: