@odigos/aws-cdk 0.0.18 → 0.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.jsii CHANGED
@@ -3826,7 +3826,7 @@
3826
3826
  },
3827
3827
  "name": "@odigos/aws-cdk",
3828
3828
  "readme": {
3829
- "markdown": "# Odigos CDK constructs for AWS\n\nInstrument AWS Lambda functions and ECS Fargate services with OpenTelemetry, from your own CDK app.\n\n```bash\nnpm install @odigos/aws-cdk # TypeScript / JavaScript\npip install odigos-aws-cdk # Python\n```\n\n| | Lambda | ECS Fargate |\n|---|---|---|\n| Construct | `OdigosLambdaInstrumentation` | `OdigosFargateInstrumentation` |\n| Mechanism | a layer, plus an exec wrapper or loader variable | agents staged into a shared task volume by a run-to-completion container |\n| Languages | python, nodejs, java, ruby | java, python, nodejs, dotnet, php, ruby |\n| Where telemetry goes | a collector in the same Lambda sandbox, which forwards | your destination, from the application process |\n| Language detection | from the function's declared runtime | none — you state the language |\n\nBoth write `ODIGOS_IAC` and never the connector's ownership marker: instrumented by Odigos, owned by you.\n\n# AWS Lambda\n\n```ts\nimport { OdigosLambdaInstrumentation } from '@odigos/aws-cdk';\n\nnew OdigosLambdaInstrumentation(this, 'Odigos', {\n destination: { endpoint: 'otlp.example.com:4317' },\n functions: [apiFn, workerFn],\n});\n```\n\n```python\nfrom odigos_aws_cdk import OdigosLambdaInstrumentation\n\nOdigosLambdaInstrumentation(self, \"Odigos\",\n destination={\"endpoint\": \"otlp.example.com:4317\"},\n functions=[api_fn, worker_fn],\n)\n```\n\nThat attaches the layer for each function's language and architecture, sets the exec wrapper that loads\nthe OpenTelemetry SDK, and hands the in-Lambda collector its whole configuration.\n\n## How the telemetry actually leaves the function\n\nYour function's SDK **does not** export to your destination. It exports to an OpenTelemetry collector\nrunning inside the same Lambda sandbox, on localhost. The collector forwards to your destination *after*\nthe handler has already returned its response.\n\nThat indirection is the whole point. A slow or unreachable destination costs billed duration, not caller\nlatency — and the collector's `decouple` processor is what holds the sandbox open until the export queue\ndrains, so a low-traffic function does not lose its telemetry to the freeze.\n\nConsequently `OTEL_EXPORTER_OTLP_ENDPOINT` is deliberately never set. If you set it yourself you put the\nexport back on the invocation's critical path.\n\n## Endpoint grammar\n\n`endpoint` is OTLP/**gRPC** as bare `host:port` (conventionally 4317). `httpEndpoint` is OTLP/**HTTP** as a\nscheme-qualified URL (conventionally 4318). Exactly one. They select different collector exporters with\ndifferent endpoint grammars, so a scheme in `endpoint` — or its absence in `httpEndpoint` — throws at synth\nrather than producing a collector that starts and silently delivers nothing. A gRPC endpoint on port 4318\n(or an HTTP one on 4317) is the likeliest version of that mistake, and is reported as a synth warning.\n\n## Choosing a layer source\n\n```ts\nnew OdigosLambdaInstrumentation(this, 'Odigos', {\n destination: { endpoint: 'otlp.example.com:4317' },\n functions: [fn],\n layerSource: OdigosLayerSource.IN_ACCOUNT,\n});\n```\n\n| | `ODIGOS_PUBLIC` (default) | `IN_ACCOUNT` |\n|---|---|---|\n| What it synthesizes | a layer ARN | an `AWS::Serverless::Application` (nested stack) per language/arch |\n| Layer owner | the Odigos AWS account | **your** account, created by your stack |\n| Resolution | synth time, from a table baked into this package | deploy time, from the Serverless Application Repository |\n| Environment-agnostic stacks | not supported — needs a concrete region | works |\n| Deploy speed | unchanged | slower on first deploy |\n\nBoth install identical bytes. `IN_ACCOUNT` exists for organisations whose policy forbids attaching a\nLambda layer owned by another AWS account. It needs the deploying principal to be allowed\n`serverlessrepo:CreateCloudFormationTemplate` and `serverlessrepo:GetApplication`.\n\nIf you mirror the layer somewhere yourself, pass `layerVersion` and neither path is used.\n\n## Versioning\n\n**The package version is the layer version.** `@odigos/aws-cdk@0.2.0` resolves to the layer built and\npublished by Odigos release `v0.2.0`, and pins the SAR application to semantic version `0.2.0` — which is\nimmutable, so it means the same bytes forever.\n\nNothing resolves at deploy time in the default path, so upgrading the layer is `npm update` and a\n`cdk diff` that shows the change. An instrumentation layer moving on its own, under a deployment that\nchanged nothing, is not a property you want.\n\n## Handler mode vs. web-server mode\n\n`OdigosInstrumentationMode.HANDLER` (the default) is for a normal Lambda handler. It sets\n`AWS_LAMBDA_EXEC_WRAPPER`.\n\n`OdigosInstrumentationMode.WEB_SERVER` is for a function running a web server behind the\n[AWS Lambda Web Adapter](https://github.com/awslabs/aws-lambda-web-adapter). The adapter owns the exec\nwrapper, so this mode does not touch it and loads instrumentation through the language's own loader\nvariable instead.\n\n```ts\nnew OdigosLambdaInstrumentation(this, 'Odigos', {\n destination: { endpoint: 'otlp.example.com:4317' },\n functions: [webFn],\n mode: OdigosInstrumentationMode.WEB_SERVER,\n // NODE_OPTIONS the function already needs — ours is appended, not substituted.\n existingLoaderValue: '--max-old-space-size=2048',\n});\n```\n\nThere is no auto-detection, deliberately. Detecting the adapter means inspecting the function's other\nlayers, which at synth time are frequently unresolved tokens; a detector that was right most of the time\nwould silently break the functions it got wrong. **Applying `HANDLER` to a web-adapter function\noverwrites the adapter's wrapper and the function stops serving.**\n\n`existingLoaderValue` has to be passed explicitly because CDK's `lambda.Function` exposes no way to read\nback an environment variable set elsewhere in your app. Omit it and the loader variable is set to the\nOdigos value alone — for `NODE_OPTIONS` that can mean losing a flag your app needs to boot.\n\nRuby has no verified web-server loader and is rejected at synth in this mode.\n\n## Java handler interfaces\n\nThe Java layer ships four wrapper scripts, one per handler interface, and this is a correctness switch\nrather than a detail level:\n\n```ts\njavaHandlerDistro: OdigosJavaHandlerDistro.OTEL_STREAM_HANDLER\n```\n\n| Distro | For |\n|---|---|\n| `OTEL_HANDLER` (default) | `RequestHandler` |\n| `OTEL_PROXY_HANDLER` | API Gateway proxy events (enriched) |\n| `OTEL_SQS_HANDLER` | SQS events (enriched) |\n| `OTEL_STREAM_HANDLER` | `RequestStreamHandler` |\n\n`OTEL_HANDLER` installs a `RequestHandler` implementation. A `RequestStreamHandler` function has a\ndifferent signature entirely, so wrapping it with the default **breaks the invocation** — it does not\nmerely produce thinner spans.\n\n## Signals\n\n**Traces only, by default.** Metrics and logs are opt-in:\n\n```ts\ndestination: {\n endpoint: 'otlp.example.com:4317',\n metrics: true, // off unless you ask\n logs: true, // off unless you ask\n}\n```\n\nThey are opt-in because the cost of a signal the destination does not implement falls on the function:\nevery batch is refused and the in-Lambda collector retries it on an exponential backoff, inside billed\nduration. Traces are what nearly every destination accepts; the other two are a decision about your\ndestination, so they are made deliberately.\n\nA disabled signal gets no collector pipeline **and** the SDK is told not to produce it\n(`OTEL_METRICS_EXPORTER=none`). Both halves matter: a signal with no pipeline is refused at the receiver on\nevery batch, so suppressing it at the source is what stops the error loop. An enabled signal's variable\nstays unset, because `otlp` is already the SDK default.\n\n`traces: false` with neither of the others on throws at synth — a collector with no pipeline fails to\nstart, which would leave the function instrumented and silent.\n\n**If Odigos also manages this function in manual mode, the signals have to match.** The connector renders\nthe collector configuration from the destination configured in Odigos and compares the whole string, so a\ntraces-only function judged against an all-three destination is reported as drifted on the\ncollector-export component. Enable the same set on both sides.\n\n## Instrumenting a whole stack\n\n```ts\nAspects.of(stack).add(new OdigosInstrumentationAspect(stack, {\n destination: { endpoint: 'otlp.example.com:4317' },\n}));\n```\n\n`skipUnsupportedRuntimes` defaults to `true` for the aspect: it visits every function in the scope,\nincluding ones whose runtime has no OpenTelemetry Lambda layer (`dotnet*`, `provided.*` for Go and custom\nruntimes). Each skip is a synth warning rather than a silent no-op. The construct defaults it to `false`,\nbecause explicitly asking to instrument a Go function is a mistake worth failing on.\n\n## Credentials in `headers`\n\n`destination.headers` is rendered into `OPENTELEMETRY_COLLECTOR_CONFIG_URI`, which is a plain Lambda\nenvironment variable — readable by anyone with `lambda:GetFunctionConfiguration` and visible in the\nconsole. For a long-lived credential, prefer a destination that authenticates by network position, or\nterminate at a collector you control.\n\n## How Odigos recognises the function\n\nThese constructs write `ODIGOS_IAC` on every function they instrument, recording the layer ARN they attached\nand the package version that attached it. That variable is what Odigos reads to attribute the function to\nitself; without it a function reports as `Absent` — \"awaiting your deployment\" — however completely the layer,\nwrapper and collector configuration are applied. There is nothing to switch on.\n\nThe recorded layer ARN matters beyond attribution: it is what the connector checks the function against, so a\nversion you pinned is respected instead of being reported as drift the day Odigos publishes a newer one. It\nworks in `IN_ACCOUNT` mode too, where the ARN is a deploy-time reference your own code has no way to obtain.\n\nWhat these constructs never write is `ODIGOS_MANAGED`. That is the connector's own record of a mutation *it*\nperformed, including the `prevEnv` snapshot it restores from on rollback, and it is written only when you hand\na function to the connector by enabling it as an automatic-mode source. A CDK stack has no such snapshot to\noffer — nothing was mutated, your stack *is* the function's definition — so writing it would claim a rollback\nthat was never recorded. Do not add it by hand.\n\n## Service names\n\nLeft unset, `OTEL_SERVICE_NAME` is the function's physical name when your stack names it explicitly, and\nthe construct id otherwise. The fallback exists because a name CloudFormation generates is a `Ref` to the\nfunction, and a function referencing itself in its own environment is a dependency cycle CloudFormation\nrejects at deploy time.\n\nThe connector compares `OTEL_SERVICE_NAME` against the workload's name in Odigos, which is the real AWS\nfunction name — so for a function whose name is generated, pass `serviceName` explicitly or it is reported\nas drifted. `serviceName` applies to **every** function a construct instruments, so use one construct per\nfunction when you want per-function names.\n\n## Do not also instrument these functions with the Odigos connector\n\nThe Odigos AWS connector instruments **live** functions by mutating them, and reconciles them back to its\nown desired state on every pass. A function instrumented here *and* enabled as an automatic-mode connector\nsource ends up owned by both: each `cdk deploy` reasserts the CDK's environment, each reconcile reasserts\nthe connector's, and every round trip publishes a new function version.\n\nPick one per function. Discovery-only (manual) mode is the combination that works: Odigos watches and\nreports, your stack owns the configuration. That is what `ODIGOS_IAC` expresses — instrumented by Odigos,\nowned by you — and it is why these constructs never write the connector's ownership marker.\n\n# ECS Fargate\n\n```ts\nimport { OdigosFargateInstrumentation, OdigosFargateLanguage } from '@odigos/aws-cdk';\n\nconst odigos = new OdigosFargateInstrumentation(this, 'Odigos', {\n destination: { httpEndpoint: 'https://otlp.example.com:4318' },\n});\n\nodigos.instrument({\n container: apiContainer, // the ecs.ContainerDefinition to instrument\n language: OdigosFargateLanguage.JAVA,\n service: apiService, // read OTEL_SERVICE_NAME off the ECS service\n});\n```\n\n```python\nfrom odigos_aws_cdk import OdigosFargateInstrumentation, OdigosFargateLanguage\n\nodigos = OdigosFargateInstrumentation(self, \"Odigos\",\n destination={\"http_endpoint\": \"https://otlp.example.com:4318\"},\n)\nodigos.instrument(\n container=api_container,\n language=OdigosFargateLanguage.JAVA,\n service=api_service,\n)\n```\n\n## What it does to the task definition\n\nFour moves, which are one mechanism and only work together:\n\n1. a bare-name task volume, `odigos-agent` — on Fargate an ephemeral-storage bind mount scoped to the task\n (there is no tmpfs);\n2. a **non-essential** `odigos-agent` container that copies the language agents out of the agents bundle\n into that volume and exits 0;\n3. the volume mounted read-only at `/var/odigos` in your container, which `DependsOn` the staging container\n with condition `SUCCESS` — legal only against a non-essential container, which is why (2) is\n non-essential;\n4. the language's activation environment, the OTLP export environment and the `ODIGOS_IAC` marker on your\n container.\n\nMarker presence alone is a false green here. A container can carry a complete, correct activation\nenvironment while the staging container is gone — the application then starts, finds nothing at\n`/var/odigos`, and exports nothing while reporting as instrumented. That is why all four are one construct.\n\n## Where the telemetry goes\n\nStraight to your destination, from inside the application process. **This is the substantive difference\nfrom Lambda**, where the SDK hands batches to a collector in the same sandbox and the export happens after\nthe response has already been returned. A Fargate task has no such collector, so a slow or unreachable\ndestination is felt by the application's own exporter.\n\n## Naming the service\n\n`OTEL_SERVICE_NAME` **must equal the workload's name in Odigos, which for a Fargate workload is the ECS\nservice name** — not the task definition family and not the container name. A different value is reported\nas instrumentation drift rather than treated as a naming preference, so there is no default: pass `service`\nto read the name off an `ecs.FargateService`, or `serviceName` when the service is defined elsewhere.\n\nThe service's physical name has to be one you set. A name CloudFormation generates is a `Ref` to the\nservice, and the task definition that service runs cannot reference it — CloudFormation rejects the cycle\nat deploy time — so a generated name throws at synth pointing you at `serviceName`.\n\n## Languages, and stating them\n\n| | Activated through | Needs `runtimeVersion` |\n|---|---|---|\n| `JAVA` | `JAVA_TOOL_OPTIONS` | no |\n| `PYTHON` | `PYTHONPATH` | no |\n| `NODEJS` | `NODE_OPTIONS` | no |\n| `DOTNET` | `CORECLR_*` / `DOTNET_*` (glibc only) | no |\n| `PHP` | `PHP_INI_SCAN_DIR` | **yes** — major.minor, e.g. `\"8.3\"` |\n| `RUBY` | `RUBYOPT` | **yes** — major.minor, e.g. `\"3.3\"` |\n\nThere is no aspect and no detection, and unlike Lambda that is not a matter of taste. A function declares\nits runtime; a container declares an image. A language guessed from an image would silently mis-instrument\nwhatever it got wrong — a Java agent staged into a Python container leaves it uninstrumented, and a\n`JAVA_TOOL_OPTIONS` on a JVM-less image is simply ignored, so nothing fails loudly enough to notice.\n\nphp and ruby need `runtimeVersion` because their agents are laid down per runtime version in the bundle,\nand the version selects the path their activation variable points at. It must be exactly `major.minor`;\nanything else names a directory that does not exist, and the container starts with no agent loaded.\n\n**Go cannot be instrumented on Fargate at all.** Its only zero-code instrumentation is eBPF, and Fargate\nforbids the privileged containers and host PID an eBPF agent needs. Use the OpenTelemetry Go SDK.\n\n## Loader variables your container already sets\n\nThe construct reads the value your container was created with and **appends** to it, joined by the\nseparator the language's own pattern specifies — a space for `JAVA_TOOL_OPTIONS` and `NODE_OPTIONS`, a\ncolon for `PYTHONPATH`:\n\n```ts\ntaskDefinition.addContainer('App', {\n image,\n environment: { JAVA_TOOL_OPTIONS: '-Xmx512m' },\n});\n// becomes: -Xmx512m -javaagent:/var/odigos/java/javaagent.jar\n```\n\nPass `existingLoaderValue` when the value is not visible at synth — set by an `environmentFiles` entry,\nbaked into the image's own `ENV`, or supplied through `secrets`.\n\n`PHP_INI_SCAN_DIR` and `RUBYOPT` are the exception: Odigos compares them **exactly**, so they are set\noutright and a value already there is reported as a synth warning rather than merged. Fold what your\napplication needs into the value Odigos sets.\n\n## Transport\n\nEach language's agent has a preferred transport, and giving the destination **both** endpoints lets each\none take it:\n\n```ts\ndestination: {\n endpoint: 'otlp.example.com:4317', // OTLP/gRPC, bare host:port\n httpEndpoint: 'https://otlp.example.com:4318', // OTLP/HTTP, scheme-qualified\n}\n```\n\nEvery agent in the bundle prefers `http/protobuf`. java, python, nodejs and dotnet honour\n`OTEL_EXPORTER_OTLP_PROTOCOL` and fall back to gRPC when that is all the destination offers; **php and\nruby cannot** — their community SDKs are `http/protobuf`-only, so instrumenting one against a gRPC-only\ndestination throws at synth rather than deploying a task that exports nothing.\n\nEndpoint and protocol are one decision, and are compared as one. Pairing an endpoint with the other\nprotocol fails at runtime as a connection reset that points nowhere near the cause, so an endpoint on the\nother protocol's standard port (4317 with `http/protobuf`, 4318 with `grpc`) is a synth warning.\n\nUnlike Lambda, `destination.headers` is rendered into `OTEL_EXPORTER_OTLP_HEADERS` on the container. The\nconnector does not set or compare that variable when it instruments a Fargate task itself, so it is\nneither reconciled nor reported as drift — it is here because a task with no collector of its own has\nnowhere else to hold a credential. Same exposure caveat as Lambda: `ecs:DescribeTaskDefinition` and the\nconsole can read it.\n\n## Signals\n\nSame defaults as Lambda — traces on, metrics and logs opt-in — and the same requirement that they match\nwhat Odigos has configured for the destination. All three `OTEL_*_EXPORTER` variables are written\nexplicitly here, unlike Lambda where an enabled signal's variable is left unset, because the connector's\nFargate check compares all three by value.\n\n## A task definition with more than one application\n\nSix applications in six languages in one task definition is a real shape. Call `instrument()` once per\ncontainer; there is **one** staging container for the whole task definition, and its command is built\nlazily from the union of every instrumented container's agents, in ascending directory order — which is\nexactly what the connector compares it against.\n\n```ts\nodigos.instrumentAll([\n { container: apiContainer, language: OdigosFargateLanguage.JAVA, service: svc },\n { container: workerContainer, language: OdigosFargateLanguage.PYTHON, service: svc },\n]);\n```\n\nAWS caps a task definition at 10 containers and the staging container needs one of them; a definition\nalready at the limit fails at synth rather than on `RegisterTaskDefinition`.\n\n## The agents image\n\nThe staging container pulls `registry.odigos.io/odigos-fargate-agents` at this package's release tag — a\nmulti-arch manifest list, so amd64 and Graviton tasks both get a matching image. **It is pulled by your\ntask's execution role at every task start**, so a task in a private subnet with no NAT and no registry\nendpoint cannot pull it. Mirror it into a registry your tasks can reach and pass `agentsImage`:\n\n```ts\nagentsImage: '111122223333.dkr.ecr.eu-west-1.amazonaws.com/odigos-fargate-agents:v0.0.13',\n```\n\nA mirror has to be the same bundle — the activation environment names exact paths inside it. The bundle is\nLinux-only and glibc-based: a Windows task definition throws at synth, and musl (Alpine) images have no\n.NET native profiler in it.\n\n**Whatever image you stage from is recorded in the marker**, as `image`, and Odigos holds the task\ndefinition to *that* rather than to the bundle the connector's own build pins. Two consequences worth\nknowing: a mirror is judged against itself instead of reading as drift, and upgrading this package ahead of\nthe connector is not drift either — the service stays conformant until you re-deploy, exactly as a Lambda\nfunction pinned to an older layer does. What *is* reported is two containers in one task definition\nrecording different images: there is one staging container and it cannot serve two bundles.\n\n## Do not also instrument these services with the connector\n\nSame rule as Lambda, with ECS's own cost. The connector instruments a live service by registering a new\ntask-definition revision and updating the service to it. A service instrumented here *and* enabled as an\nautomatic-mode source is owned by both: each `cdk deploy` reasserts your stack's definition, each\nreconcile reasserts the connector's, and every round trip registers a revision and rolls the service.\nDiscovery-only (manual) mode is the combination that works.\n\n# API\n\n| Export | |\n|---|---|\n| `OdigosLambdaInstrumentation` | the Lambda construct; `instrument(fn)`, `instrumentAll(fns)` |\n| `OdigosInstrumentationAspect` | applies it to every function in a scope |\n| `OdigosFargateInstrumentation` | the Fargate construct; `instrument(target)`, `instrumentAll(targets)` |\n| `OdigosFargateTarget` | one container to instrument: `container`, `language`, `service` / `serviceName`, `runtimeVersion`, `existingLoaderValue` |\n| `OdigosFargateLanguage` | `JAVA` \\| `PYTHON` \\| `NODEJS` \\| `DOTNET` \\| `PHP` \\| `RUBY` |\n| `OdigosDestination` | where telemetry goes; exactly one endpoint on Lambda, either or both on Fargate |\n| `OdigosLayerSource` | `ODIGOS_PUBLIC` \\| `IN_ACCOUNT` |\n| `OdigosInstrumentationMode` | `HANDLER` \\| `WEB_SERVER` |\n| `OdigosJavaHandlerDistro` | the four Java wrappers |\n| `renderCollectorConfig` | the Lambda collector config renderer, if you want to inspect what gets set |\n| `sidecarCommand`, `evaluateAppendPattern` | the Fargate staging command and loader-value rendering |\n| `LAYER_ARNS`, `ODIGOS_VERSION` | the baked layer table and the release it came from |\n| `FARGATE_AGENTS_IMAGE`, `FARGATE_DISTROS_VERSION` | the agents image this build stages, and the distros release its recipes mirror |\n\n## License\n\nApache-2.0.\n"
3829
+ "markdown": "# Odigos CDK constructs for AWS\n\nInstrument AWS Lambda functions and ECS Fargate services with OpenTelemetry, from your own CDK app.\n\n```bash\nnpm install @odigos/aws-cdk@0.0.19\n```\n\nThe npm package is published. The corrections in this checkout need a new package release before\ncustomers receive them through npm. To use this checkout locally, run `npm ci`, `npm run build`, and\n`npm run package` in `cdk/`, then install `dist/js/aws-cdk@0.0.0.jsii.tgz` in the consuming app.\n\nThe Python distribution is built by jsii but is not published on PyPI;\n`pip install odigos-aws-cdk` currently fails. Python examples below apply to a locally built wheel:\nrun `npm ci`, `npm run build`, then `npm run package` from `cdk/`, and install the wheel under `dist/python/`.\nFor customer installation until PyPI publishing is enabled, use the CloudFormation instructions instead.\n\n| | Lambda | ECS Fargate |\n|---|---|---|\n| Construct | `OdigosLambdaInstrumentation` | `OdigosFargateInstrumentation` |\n| Mechanism | a layer, plus an exec wrapper or loader variable | agents staged into a shared task volume by a run-to-completion container |\n| Languages | python, nodejs, java, ruby | java, python, nodejs, dotnet, php, ruby |\n| Where telemetry goes | a collector in the same Lambda sandbox, which forwards | your destination, from the application process |\n| Language detection | from the function's declared runtime | none — you state the language |\n\nLambda writes `odigos:iac=v1` and exact SDK/collector layer ARNs as function tags. Fargate writes an\n`ODIGOS_IAC` environment marker on each application container. Neither claims connector-managed ownership.\n\n# AWS Lambda\n\n```ts\nimport { OdigosLambdaInstrumentation } from '@odigos/aws-cdk';\n\nnew OdigosLambdaInstrumentation(this, 'Odigos', {\n destination: { endpoint: 'otlp.example.com:4317' },\n functions: [apiFn, workerFn],\n});\n```\n\n```python\nfrom odigos_aws_cdk import OdigosLambdaInstrumentation\n\nOdigosLambdaInstrumentation(self, \"Odigos\",\n destination={\"endpoint\": \"otlp.example.com:4317\"},\n functions=[api_fn, worker_fn],\n)\n```\n\nThat attaches the matching layer(s), sets `AWS_LAMBDA_EXEC_WRAPPER`, and writes the readable version-2\n`ODIGOS_LAMBDA_CONFIG` JSON. The layer launcher builds SDK settings and collector pipelines at startup;\ncustomer SDK environment variables remain saved unchanged in AWS. Public and in-account layers must be\nfrom `v0.0.19` or newer to read this configuration; a custom `layerVersion` must have the same bootstrap.\n\nApply instrumentation before creating published versions and aliases, then deploy the updated version and\npoint all traffic-serving aliases at it. Updating `$LATEST` alone does not instrument older versions.\n\n## How the telemetry actually leaves the function\n\nYour function's SDK **does not** export to your destination. It exports to an OpenTelemetry collector\nrunning inside the same Lambda sandbox, on localhost. The collector forwards to your destination *after*\nthe handler has already returned its response.\n\nThat indirection is the whole point. A slow or unreachable destination costs billed duration, not caller\nlatency — and the collector's `decouple` processor is what holds the sandbox open until the export queue\ndrains, so a low-traffic function does not lose its telemetry to the freeze.\n\nConsequently `OTEL_EXPORTER_OTLP_ENDPOINT` is deliberately never set. If you set it yourself you put the\nexport back on the invocation's critical path.\n\n## Endpoint grammar\n\n`endpoint` is OTLP/**gRPC** as bare `host:port` (conventionally 4317). `httpEndpoint` is OTLP/**HTTP** as a\nscheme-qualified URL (conventionally 4318). Exactly one. They select different collector exporters with\ndifferent endpoint grammars, so a scheme in `endpoint` — or its absence in `httpEndpoint` — throws at synth\nrather than producing a collector that starts and silently delivers nothing. A gRPC endpoint on port 4318\n(or an HTTP one on 4317) is the likeliest version of that mistake, and is reported as a synth warning.\n\n## Choosing a layer source\n\n```ts\nnew OdigosLambdaInstrumentation(this, 'Odigos', {\n destination: { endpoint: 'otlp.example.com:4317' },\n functions: [fn],\n layerSource: OdigosLayerSource.IN_ACCOUNT,\n});\n```\n\n| | `ODIGOS_PUBLIC` (default) | `IN_ACCOUNT` |\n|---|---|---|\n| What it synthesizes | a layer ARN | an `AWS::Serverless::Application` (nested stack) per language/arch |\n| Layer owner | the Odigos AWS account | **your** account, created by your stack |\n| Resolution | synth time, from a table baked into this package | deploy time, from the Serverless Application Repository |\n| Environment-agnostic stacks | not supported — needs a concrete region | works |\n| Deploy speed | unchanged | slower on first deploy |\n\nBoth install identical bytes. `IN_ACCOUNT` exists for organisations whose policy forbids attaching a\nLambda layer owned by another AWS account. It needs the deploying principal to be allowed\n`serverlessrepo:CreateCloudFormationTemplate` and `serverlessrepo:GetApplication`.\n\nIf you mirror the layer somewhere yourself, pass `layerVersion` and neither path is used.\n\n## Versioning\n\n**The package version is the artifact release version.** `@odigos/aws-cdk@0.0.19` resolves to layers built and\npublished by Odigos release `v0.0.19`, and pins the SAR application to semantic version `0.0.19` — which is\nimmutable, so it means the same bytes forever.\n\nNothing resolves at deploy time in the default path, so upgrading the layer is `npm update` and a\n`cdk diff` that shows the change. An instrumentation layer moving on its own, under a deployment that\nchanged nothing, is not a property you want.\n\n## Handler mode vs. web-server mode\n\n`OdigosInstrumentationMode.HANDLER` (the default) is for a normal Lambda handler. It sets\n`AWS_LAMBDA_EXEC_WRAPPER`.\n\n`OdigosInstrumentationMode.WEB_SERVER` is for a function running a web server behind the\n[AWS Lambda Web Adapter](https://github.com/awslabs/aws-lambda-web-adapter). The adapter owns the exec\nwrapper. This mode sets `AWS_LAMBDA_EXEC_WRAPPER=/opt/odigos-webserver`, records the previous wrapper in\n`ODIGOS_LAMBDA_CONFIG.webWrapper`, loads the agent through its language loader, then invokes the saved\nwrapper. Define the adapter's wrapper before applying instrumentation so it is preserved.\n\n```ts\nnew OdigosLambdaInstrumentation(this, 'Odigos', {\n destination: { endpoint: 'otlp.example.com:4317' },\n functions: [webFn],\n mode: OdigosInstrumentationMode.WEB_SERVER,\n // NODE_OPTIONS the function already needs — ours is appended, not substituted.\n existingLoaderValue: '--max-old-space-size=2048',\n});\n```\n\nThere is no auto-detection, deliberately. Detecting the adapter means inspecting the function's other\nlayers, which at synth time are frequently unresolved tokens; a detector that was right most of the time\nwould silently break the functions it got wrong. **Applying `HANDLER` to a web-adapter function\noverwrites the adapter's wrapper and the function stops serving.**\n\nThe launcher preserves and joins the function's existing loader value at startup. `existingLoaderValue`\nis an optional override: when supplied, CDK saves it unchanged as the customer's loader variable, and the\nlauncher adds instrumentation in its child process. Omit it to preserve the loader already in the function.\n\nRuby has no verified web-server loader and is rejected at synth in this mode.\n\n## Java handler interfaces\n\nThe Java layer ships four wrapper scripts, one per handler interface, and this is a correctness switch\nrather than a detail level:\n\n```ts\njavaHandlerDistro: OdigosJavaHandlerDistro.OTEL_STREAM_HANDLER\n```\n\n| Distro | For |\n|---|---|\n| `OTEL_HANDLER` | `RequestHandler` |\n| `OTEL_PROXY_HANDLER` | API Gateway proxy events (enriched) |\n| `OTEL_SQS_HANDLER` | SQS events (enriched) |\n| `OTEL_STREAM_HANDLER` | `RequestStreamHandler` |\n\nJava defaults to the full `java-lambda-otel-agent`, which supports handler and stream interfaces. The\nlegacy `javaHandlerDistro` option explicitly selects a lightweight wrapper. `OTEL_HANDLER` implements\n`RequestHandler`; use `OTEL_STREAM_HANDLER` for `RequestStreamHandler` when selecting a lightweight wrapper.\nPrefer the equivalent `distro` IDs below. Do not set both options.\n\n## Distributions and ADOT\n\nSet `distro` to the exact connector catalog ID. A fixed distro must match every function on that construct.\n\n| Language | Default | Alternatives |\n|---|---|---|\n| Java | `java-lambda-otel-agent` | `java-lambda-otel-wrapper`, `java-lambda-otel-proxy-wrapper`, `java-lambda-otel-sqs-wrapper`, `java-lambda-otel-stream-wrapper`, `java-lambda-adot`, `java-lambda-adot-application-signals` |\n| Node.js | `nodejs-lambda-otel` | `nodejs-lambda-adot`, `nodejs-lambda-adot-application-signals` |\n| Python | `python-lambda-otel` | `python-lambda-adot`, `python-lambda-adot-application-signals` |\n| Ruby | `ruby-lambda-otel` | none |\n\nADOT uses two architecture-matched layers: the language SDK and `adot-collector`. If mirroring the SDK\nwith `layerVersion`, also supply `collectorLayerVersion` or let `layerSource` resolve the collector.\nADOT supports Java 11/17/21, Node.js 18/20/22/24, and Python 3.10–3.13. ADOT and lightweight Java wrappers\ncannot use `WEB_SERVER` mode. Plain ADOT disables Application Signals. The Application Signals presets\nrequire traces **and** metrics, add `CloudWatchLambdaApplicationSignalsExecutionRolePolicy` to the Lambda\nrole, and require one-time CloudWatch Application Signals account/region setup. For an immutable imported\nrole, attach the policy separately. These presets also send enriched spans to Lambda's embedded AWS agent;\nNode.js/Python's AWS-derived service metrics do not become ordinary OTLP metrics at a custom destination.\n\n## Signals\n\n**Traces only, by default.** Metrics and logs are opt-in:\n\n```ts\ndestination: {\n endpoint: 'otlp.example.com:4317',\n metrics: true, // off unless you ask\n logs: true, // off unless you ask\n}\n```\n\nThey are opt-in because the cost of a signal the destination does not implement falls on the function:\nevery batch is refused and the in-Lambda collector retries it on an exponential backoff, inside billed\nduration. Traces are what nearly every destination accepts; the other two are a decision about your\ndestination, so they are made deliberately.\n\nA disabled signal gets no collector pipeline **and** the SDK is told not to produce it\n(`OTEL_METRICS_EXPORTER=none`) inside the launcher's child process. Enabled signals explicitly select\n`otlp` there. Both halves matter: a signal with no pipeline would otherwise be refused on every batch.\n\n`traces: false` with neither of the others on throws at synth — a collector with no pipeline fails to\nstart, which would leave the function instrumented and silent.\n\n**If Odigos also manages this function in manual mode, the signals have to match.** The connector renders\nthe collector configuration from the destination configured in Odigos and compares the whole string, so a\ntraces-only function judged against an all-three destination is reported as drifted on the\ncollector-export component. Enable the same set on both sides.\n\n## Instrumenting a whole stack\n\n```ts\nAspects.of(stack).add(new OdigosInstrumentationAspect(stack, {\n destination: { endpoint: 'otlp.example.com:4317' },\n}));\n```\n\n`skipUnsupportedRuntimes` defaults to `true` for the aspect: it visits every function in the scope,\nincluding ones whose runtime has no OpenTelemetry Lambda layer (`dotnet*`, `provided.*` for Go and custom\nruntimes). Each skip is a synth warning rather than a silent no-op. The construct defaults it to `false`,\nbecause explicitly asking to instrument a Go function is a mistake worth failing on.\n\n## Credentials in `headers`\n\n`destination.headers` is rendered into `ODIGOS_LAMBDA_CONFIG`, which is a plain Lambda\nenvironment variable — readable by anyone with `lambda:GetFunctionConfiguration` and visible in the\nconsole. For a long-lived credential, prefer a destination that authenticates by network position, or\nterminate at a collector you control.\n\n## How Odigos recognises the function\n\nLambda attribution uses `odigos:iac=v1`, `odigos:sdk-layer`, and (for ADOT) `odigos:collector-layer`\nfunction tags. The recorded ARNs identify the exact attached layer versions, including SAR output tokens.\nThere is no Lambda `ODIGOS_IAC` environment marker. Fargate uses its per-container environment marker.\n\nThe connector's `odigos:managed` Lambda tag and `ODIGOS_MANAGED` Fargate marker describe mutations and\nrollback snapshots owned by the connector. Do not add them to an IaC-managed workload.\n\n## Service names\n\nLeft unset, `OTEL_SERVICE_NAME` is the function's physical name when your stack names it explicitly, and\nthe construct id otherwise. The fallback exists because a name CloudFormation generates is a `Ref` to the\nfunction, and a function referencing itself in its own environment is a dependency cycle CloudFormation\nrejects at deploy time.\n\nThe connector compares `OTEL_SERVICE_NAME` against the workload's name in Odigos, which is the real AWS\nfunction name — so for a function whose name is generated, pass `serviceName` explicitly or it is reported\nas drifted. `serviceName` applies to **every** function a construct instruments, so use one construct per\nfunction when you want per-function names.\n\n## Do not also instrument these functions with the Odigos connector\n\nThe Odigos AWS connector instruments **live** functions by mutating them, and reconciles them back to its\nown desired state on every pass. A function instrumented here *and* enabled as an automatic-mode connector\nsource ends up owned by both: each `cdk deploy` reasserts the CDK's environment, each reconcile reasserts\nthe connector's, and every round trip publishes a new function version.\n\nPick one per function. Discovery-only (manual) mode is the combination that works: Odigos watches and\nreports, your stack owns the configuration. The IaC markers express that ownership.\n\n# ECS Fargate\n\n```ts\nimport { OdigosFargateInstrumentation, OdigosFargateLanguage } from '@odigos/aws-cdk';\n\nconst odigos = new OdigosFargateInstrumentation(this, 'Odigos', {\n destination: { httpEndpoint: 'https://otlp.example.com:4318' },\n});\n\nodigos.instrument({\n container: apiContainer, // the ecs.ContainerDefinition to instrument\n language: OdigosFargateLanguage.JAVA,\n service: apiService, // read OTEL_SERVICE_NAME off the ECS service\n});\n```\n\n```python\nfrom odigos_aws_cdk import OdigosFargateInstrumentation, OdigosFargateLanguage\n\nodigos = OdigosFargateInstrumentation(self, \"Odigos\",\n destination={\"http_endpoint\": \"https://otlp.example.com:4318\"},\n)\nodigos.instrument(\n container=api_container,\n language=OdigosFargateLanguage.JAVA,\n service=api_service,\n)\n```\n\n## What it does to the task definition\n\nFour moves, which are one mechanism and only work together:\n\n1. a bare-name task volume, `odigos-agent` — on Fargate an ephemeral-storage bind mount scoped to the task\n (there is no tmpfs);\n2. a **non-essential** `odigos-agent` container that copies the language agents out of the agents bundle\n into that volume and exits 0;\n3. the volume mounted read-only at `/var/odigos` in your container, which `DependsOn` the staging container\n with condition `SUCCESS` — legal only against a non-essential container, which is why (2) is\n non-essential;\n4. the language's activation environment, the OTLP export environment and the `ODIGOS_IAC` marker on your\n container.\n\nMarker presence alone is a false green here. A container can carry a complete, correct activation\nenvironment while the staging container is gone — the application then starts, finds nothing at\n`/var/odigos`, and exports nothing while reporting as instrumented. That is why all four are one construct.\n\n## Where the telemetry goes\n\nStraight to your destination, from inside the application process. **This is the substantive difference\nfrom Lambda**, where the SDK hands batches to a collector in the same sandbox and the export happens after\nthe response has already been returned. A Fargate task has no such collector, so a slow or unreachable\ndestination is felt by the application's own exporter.\n\n## Naming the service\n\n`OTEL_SERVICE_NAME` **must equal the workload's name in Odigos, which for a Fargate workload is the ECS\nservice name** — not the task definition family and not the container name. A different value is reported\nas instrumentation drift rather than treated as a naming preference, so there is no default: pass `service`\nto read the name off an `ecs.FargateService`, or `serviceName` when the service is defined elsewhere.\n\nThe service's physical name has to be one you set. A name CloudFormation generates is a `Ref` to the\nservice, and the task definition that service runs cannot reference it — CloudFormation rejects the cycle\nat deploy time — so a generated name throws at synth pointing you at `serviceName`.\n\n## Languages, and stating them\n\n| | Activated through | Needs `runtimeVersion` |\n|---|---|---|\n| `JAVA` | `JAVA_TOOL_OPTIONS` | no |\n| `PYTHON` | `PYTHONPATH` | no |\n| `NODEJS` | `NODE_OPTIONS` | no |\n| `DOTNET` | `CORECLR_*` / `DOTNET_*` (glibc only) | no |\n| `PHP` | `PHP_INI_SCAN_DIR` | **yes** — major.minor, e.g. `\"8.3\"` |\n| `RUBY` | `RUBYOPT` | **yes** — major.minor, e.g. `\"3.3\"` |\n\nThere is no aspect and no detection, and unlike Lambda that is not a matter of taste. A function declares\nits runtime; a container declares an image. A language guessed from an image would silently mis-instrument\nwhatever it got wrong — a Java agent staged into a Python container leaves it uninstrumented, and a\n`JAVA_TOOL_OPTIONS` on a JVM-less image is simply ignored, so nothing fails loudly enough to notice.\n\nphp and ruby need `runtimeVersion` because their agents are laid down per runtime version in the bundle,\nand the version selects the path their activation variable points at. It must be exactly `major.minor`;\nanything else names a directory that does not exist, and the container starts with no agent loaded.\n\n**Go cannot be instrumented on Fargate at all.** Its only zero-code instrumentation is eBPF, and Fargate\nforbids the privileged containers and host PID an eBPF agent needs. Use the OpenTelemetry Go SDK.\n\n## Loader variables your container already sets\n\nThe construct reads the value your container was created with and **appends** to it, joined by the\nseparator the language's own pattern specifies — a space for `JAVA_TOOL_OPTIONS` and `NODE_OPTIONS`, a\ncolon for `PYTHONPATH`:\n\n```ts\ntaskDefinition.addContainer('App', {\n image,\n environment: { JAVA_TOOL_OPTIONS: '-Xmx512m' },\n});\n// becomes: -Xmx512m -javaagent:/var/odigos/java/javaagent.jar\n```\n\nPass `existingLoaderValue` when the value is not visible at synth — set by an `environmentFiles` entry\nor baked into the image's own `ENV`. Do not supply a generated instrumentation variable through ECS\n`secrets`: it conflicts with the ordinary environment setting and is rejected. Move loader options into\nordinary application configuration before applying instrumentation.\n\n`PHP_INI_SCAN_DIR` and `RUBYOPT` are the exception: Odigos compares them **exactly**, so they are set\noutright and a value already there is reported as a synth warning rather than merged. Fold what your\napplication needs into the value Odigos sets.\n\n## Transport\n\nEach language's agent has a preferred transport, and giving the destination **both** endpoints lets each\none take it:\n\n```ts\ndestination: {\n endpoint: 'otlp.example.com:4317', // OTLP/gRPC, bare host:port\n httpEndpoint: 'https://otlp.example.com:4318', // OTLP/HTTP, scheme-qualified\n}\n```\n\nEvery agent in the bundle prefers `http/protobuf`. java, python, nodejs and dotnet honour\n`OTEL_EXPORTER_OTLP_PROTOCOL` and fall back to gRPC when that is all the destination offers; **php and\nruby cannot** — their community SDKs are `http/protobuf`-only, so instrumenting one against a gRPC-only\ndestination throws at synth rather than deploying a task that exports nothing.\n\nEndpoint and protocol are one decision, and are compared as one. Pairing an endpoint with the other\nprotocol fails at runtime as a connection reset that points nowhere near the cause, so an endpoint on the\nother protocol's standard port (4317 with `http/protobuf`, 4318 with `grpc`) is a synth warning.\n\nOn Fargate, `destination.headers` is rendered into `OTEL_EXPORTER_OTLP_HEADERS` as a sorted, URL-encoded\nheader list matching the connector. Pass raw header values, such as `Bearer token`, and let the construct\nencode them. The connector compares the transport, endpoint and headers against the selected destination.\n`ecs:DescribeTaskDefinition` and the console can read these values; task revisions can retain them.\n\n## Signals\n\nSame defaults as Lambda — traces on, metrics and logs opt-in — and the same requirement that they match\nwhat Odigos has configured for the destination. All three `OTEL_*_EXPORTER` variables are written\nexplicitly here. Lambda sets the same selectors inside the launcher rather than storing them in AWS.\nRuby Fargate supports traces only; Ruby Lambda also supports function stdout/stderr logs, but no metrics.\nUnsupported Ruby signals and profiles generate synth warnings; they do not activate missing exporters.\n\n## A task definition with more than one application\n\nSix applications in six languages in one task definition is a real shape. Call `instrument()` once per\ncontainer; there is **one** staging container for the whole task definition, and its command is built\nlazily from the union of every instrumented container's agents, in ascending directory order — which is\nexactly what the connector compares it against.\n\n```ts\nodigos.instrumentAll([\n { container: apiContainer, language: OdigosFargateLanguage.JAVA, service: svc },\n { container: workerContainer, language: OdigosFargateLanguage.PYTHON, service: svc },\n]);\n```\n\nAWS caps a task definition at 10 containers and the staging container needs one of them; a definition\nalready at the limit fails at synth rather than on `RegisterTaskDefinition`. Several constructs can share\nthe staging container only when they use the same agents image. Instrument each application container once.\nExisting manual `odigos-agent` containers/volumes and conflicting mounts are rejected; remove those manual\nresources before adopting the construct. Use Linux Fargate platform version `1.3.0` or newer (`LATEST` is\nrecommended) so the `SUCCESS` startup dependency is supported.\n\n## The agents image\n\nThe staging container pulls `registry.odigos.io/odigos-fargate-agents` at this package's release tag — a\nmulti-arch manifest list, so amd64 and Graviton tasks both get a matching image. **It is pulled by your\ntask's execution role at every task start**, so a task in a private subnet with no NAT and no registry\nendpoint cannot pull it. Mirror it into a registry your tasks can reach and pass `agentsImage`:\n\n```ts\nagentsImage: '111122223333.dkr.ecr.eu-west-1.amazonaws.com/odigos-fargate-agents:v0.0.19',\n```\n\nA mirror has to be the same bundle — the activation environment names exact paths inside it. The bundle is\nLinux-only and glibc-based: a Windows task definition throws at synth, and musl (Alpine) images have no\n.NET native profiler in it.\n\n**Whatever image you stage from is recorded in the marker**, as `image`, and Odigos holds the task\ndefinition to *that* rather than to the bundle the connector's own build pins. Two consequences worth\nknowing: a mirror is judged against itself instead of reading as drift, and upgrading this package ahead of\nthe connector is not drift either — the service stays conformant until you re-deploy, exactly as a Lambda\nfunction pinned to an older layer does. What *is* reported is two containers in one task definition\nrecording different images: there is one staging container and it cannot serve two bundles.\n\n## Do not also instrument these services with the connector\n\nSame rule as Lambda, with ECS's own cost. The connector instruments a live service by registering a new\ntask-definition revision and updating the service to it. A service instrumented here *and* enabled as an\nautomatic-mode source is owned by both: each `cdk deploy` reasserts your stack's definition, each\nreconcile reasserts the connector's, and every round trip registers a revision and rolls the service.\nDiscovery-only (manual) mode is the combination that works.\n\n# Bill of materials and removal\n\n| Flow | Resources and configuration left by deployment |\n|---|---|\n| Lambda public/custom layer | Layer reference(s), wrapper and compact config environment variables, IaC layer tags, new published versions/aliases if your app creates them |\n| Lambda `IN_ACCOUNT` | The above plus one SAR nested stack/layer per artifact family and architecture; ADOT adds the shared collector application |\n| Application Signals | The above plus a managed-policy attachment on the Lambda role; account-wide discovery is configured separately |\n| Fargate | A task-definition revision with one non-essential copier, one shared volume, mounts/dependencies, activation/export environment and per-container IaC marker; an ECS deployment when the service references that revision |\n\nThe constructs do not create a regional Collector, network routes, telemetry destination, or customer\ncredentials. Provision and remove those using their separate guides. Registry access and destination\nreachability are deployment prerequisites.\n\nTo uninstall instrumentation while keeping the application:\n\n1. Disable automatic connector ownership for the workload, then remove the instrumentation construct,\n `instrument()` calls or aspect from your app. Restore any explicit loader override you no longer want.\n2. Run `cdk diff`, review removal of the instrumentation resources/settings, then run `cdk deploy`.\n For Lambda, publish the uninstrumented version and move every serving alias to it. For Fargate,\n ensure the service references the new task revision and wait for the rollout to finish.\n3. Verify the application is healthy and sends no new Odigos telemetry. Old published Lambda versions\n and ECS revisions remain instrumented until retired; review all traffic and rollback references first.\n4. SAR layers use retention policies and can remain after stack removal. After no function or published\n version references one, remove that exact retained layer version with\n `aws lambda delete-layer-version --layer-name \"<LAYER_NAME>\" --version-number \"<LAYER_VERSION>\"`.\n Never delete a shared layer still in use. Retire unused task revisions through your existing ECS lifecycle.\n5. Remove dedicated, unused role policy attachments and telemetry credentials after confirming no other\n consumer needs them. Configurations retained in versions, task revisions and IaC history may still hold\n headers. Account-wide Application Signals discovery and its service-linked role are separate resources.\n\n`cdk destroy` removes the entire application stack and is only appropriate when retiring that application.\n\n# API\n\n| Export | |\n|---|---|\n| `OdigosLambdaInstrumentation` | the Lambda construct; `instrument(fn)`, `instrumentAll(fns)` |\n| `OdigosInstrumentationAspect` | applies it to every function in a scope |\n| `OdigosFargateInstrumentation` | the Fargate construct; `instrument(target)`, `instrumentAll(targets)` |\n| `OdigosFargateTarget` | one container to instrument: `container`, `language`, `service` / `serviceName`, `runtimeVersion`, `existingLoaderValue` |\n| `OdigosFargateLanguage` | `JAVA` \\| `PYTHON` \\| `NODEJS` \\| `DOTNET` \\| `PHP` \\| `RUBY` |\n| `OdigosDestination` | where telemetry goes; exactly one endpoint on Lambda, either or both on Fargate |\n| `OdigosLayerSource` | `ODIGOS_PUBLIC` \\| `IN_ACCOUNT` |\n| `OdigosInstrumentationMode` | `HANDLER` \\| `WEB_SERVER` |\n| `OdigosJavaHandlerDistro` | the four Java wrappers |\n| `renderCollectorConfig` | legacy inline collector config renderer for validation; the compact launcher generates the deployed config |\n| `sidecarCommand`, `evaluateAppendPattern` | the Fargate staging command and loader-value rendering |\n| `LAYER_ARNS`, `ODIGOS_VERSION` | the baked layer table and the release it came from |\n| `FARGATE_AGENTS_IMAGE`, `FARGATE_DISTROS_VERSION` | the agents image this build stages, and the distros release its recipes mirror |\n\n## License\n\nApache-2.0.\n"
3830
3830
  },
3831
3831
  "repository": {
3832
3832
  "directory": "cdk",
@@ -3887,14 +3887,14 @@
3887
3887
  "abstract": true,
3888
3888
  "docs": {
3889
3889
  "default": "- no headers",
3890
- "remarks": "These are included in compact `ODIGOS_LAMBDA_CONFIG`, which is a plain Lambda environment\nvariable: readable by anyone with `lambda:GetFunctionConfiguration` and visible in the console. For a\nlong-lived credential prefer a destination that authenticates by network position, or terminate at a\ncollector you control.\n\nOn ECS Fargate they are rendered into `OTEL_EXPORTER_OTLP_HEADERS` on the instrumented container,\nwith the same exposure — `ecs:DescribeTaskDefinition` and the console. Note that the connector does\nNOT set that variable when it instruments a Fargate task itself, and does not compare it, so this is\nneither reconciled nor reported as drift either way: it is here because an authenticated destination\nis otherwise unreachable from a task, which has no collector of its own to hold the credential.",
3890
+ "remarks": "These are included in compact `ODIGOS_LAMBDA_CONFIG`, which is a plain Lambda environment\nvariable: readable by anyone with `lambda:GetFunctionConfiguration` and visible in the console. For a\nlong-lived credential prefer a destination that authenticates by network position, or terminate at a\ncollector you control.\n\nOn ECS Fargate they are rendered into `OTEL_EXPORTER_OTLP_HEADERS` on the instrumented container,\nwith the same exposure — `ecs:DescribeTaskDefinition` and the console. Values are URL-encoded by the construct; pass raw header values. The connector compares the\nheaders together with the endpoint and protocol against the selected destination.",
3891
3891
  "stability": "stable",
3892
3892
  "summary": "Per-request headers, in practice the destination's credential."
3893
3893
  },
3894
3894
  "immutable": true,
3895
3895
  "locationInModule": {
3896
3896
  "filename": "src/types.ts",
3897
- "line": 200
3897
+ "line": 198
3898
3898
  },
3899
3899
  "name": "headers",
3900
3900
  "optional": true,
@@ -3931,7 +3931,7 @@
3931
3931
  "default": "false",
3932
3932
  "remarks": "Applies to `endpoint` only — the HTTP exporter takes its transport from the URL scheme.",
3933
3933
  "stability": "stable",
3934
- "summary": "Disable TLS verification on the gRPC exporter."
3934
+ "summary": "Connect to the gRPC exporter without TLS (plaintext)."
3935
3935
  },
3936
3936
  "immutable": true,
3937
3937
  "locationInModule": {
@@ -3955,7 +3955,7 @@
3955
3955
  "immutable": true,
3956
3956
  "locationInModule": {
3957
3957
  "filename": "src/types.ts",
3958
- "line": 237
3958
+ "line": 235
3959
3959
  },
3960
3960
  "name": "logs",
3961
3961
  "optional": true,
@@ -3974,7 +3974,7 @@
3974
3974
  "immutable": true,
3975
3975
  "locationInModule": {
3976
3976
  "filename": "src/types.ts",
3977
- "line": 226
3977
+ "line": 224
3978
3978
  },
3979
3979
  "name": "metrics",
3980
3980
  "optional": true,
@@ -3993,7 +3993,7 @@
3993
3993
  "immutable": true,
3994
3994
  "locationInModule": {
3995
3995
  "filename": "src/types.ts",
3996
- "line": 244
3996
+ "line": 242
3997
3997
  },
3998
3998
  "name": "profiles",
3999
3999
  "optional": true,
@@ -4005,14 +4005,14 @@
4005
4005
  "abstract": true,
4006
4006
  "docs": {
4007
4007
  "default": "true",
4008
- "remarks": "A signal that is off gets no collector pipeline AND the SDK is told not to produce it\n(`OTEL_TRACES_EXPORTER=none`). Both halves matter: a signal with no pipeline is refused at the\nreceiver on every batch, so suppressing it at the source is what stops the error loop.\n\nTurning this off without turning on `metrics` or `logs` leaves the destination with no signal at all,\nwhich throws at synth — a collector with no pipeline fails to start.",
4008
+ "remarks": "A signal that is off gets no collector pipeline AND the SDK is told not to produce it\n(`OTEL_TRACES_EXPORTER=none` in the startup process). Both halves matter: a signal with no pipeline is refused at the\nreceiver on every batch, so suppressing it at the source is what stops the error loop.\n\nTurning this off without turning on `metrics` or `logs` leaves the destination with no signal at all,\nwhich throws at synth — a collector with no pipeline fails to start.",
4009
4009
  "stability": "stable",
4010
4010
  "summary": "Send traces."
4011
4011
  },
4012
4012
  "immutable": true,
4013
4013
  "locationInModule": {
4014
4014
  "filename": "src/types.ts",
4015
- "line": 214
4015
+ "line": 212
4016
4016
  },
4017
4017
  "name": "traces",
4018
4018
  "optional": true,
@@ -4038,7 +4038,7 @@
4038
4038
  },
4039
4039
  "locationInModule": {
4040
4040
  "filename": "src/fargate.ts",
4041
- "line": 274
4041
+ "line": 277
4042
4042
  },
4043
4043
  "parameters": [
4044
4044
  {
@@ -4064,7 +4064,7 @@
4064
4064
  "kind": "class",
4065
4065
  "locationInModule": {
4066
4066
  "filename": "src/fargate.ts",
4067
- "line": 268
4067
+ "line": 269
4068
4068
  },
4069
4069
  "methods": [
4070
4070
  {
@@ -4075,7 +4075,7 @@
4075
4075
  },
4076
4076
  "locationInModule": {
4077
4077
  "filename": "src/fargate.ts",
4078
- "line": 300
4078
+ "line": 303
4079
4079
  },
4080
4080
  "name": "instrument",
4081
4081
  "parameters": [
@@ -4094,7 +4094,7 @@
4094
4094
  },
4095
4095
  "locationInModule": {
4096
4096
  "filename": "src/fargate.ts",
4097
- "line": 338
4097
+ "line": 360
4098
4098
  },
4099
4099
  "name": "instrumentAll",
4100
4100
  "parameters": [
@@ -4350,7 +4350,7 @@
4350
4350
  "abstract": true,
4351
4351
  "docs": {
4352
4352
  "default": "- read from the container's own environment",
4353
- "remarks": "Only needed as an OVERRIDE. The construct reads the value the container was created with and appends\nto it, so a container that sets its own `JAVA_TOOL_OPTIONS` in `addContainer` keeps it. Pass this when\nthe value is not visible at synth — set by an `environmentFiles` entry, injected by the image's own\n`ENV`, or supplied through `secrets`.",
4353
+ "remarks": "Only needed as an OVERRIDE. The construct reads the value the container was created with and appends\nto it, so a container that sets its own `JAVA_TOOL_OPTIONS` in `addContainer` keeps it. Pass this when\nthe value is not visible at synth — set by an `environmentFiles` entry, injected by the image's own\n`ENV`, or move secret-supplied loader settings to ordinary configuration first.",
4354
4354
  "stability": "stable",
4355
4355
  "summary": "The value this container already needs in its loader variable (`JAVA_TOOL_OPTIONS`, `NODE_OPTIONS` or `PYTHONPATH`), which the Odigos fragment is joined to rather than replacing."
4356
4356
  },
@@ -4439,7 +4439,7 @@
4439
4439
  },
4440
4440
  "locationInModule": {
4441
4441
  "filename": "src/instrumentation.ts",
4442
- "line": 385
4442
+ "line": 400
4443
4443
  },
4444
4444
  "parameters": [
4445
4445
  {
@@ -4466,7 +4466,7 @@
4466
4466
  "kind": "class",
4467
4467
  "locationInModule": {
4468
4468
  "filename": "src/instrumentation.ts",
4469
- "line": 377
4469
+ "line": 392
4470
4470
  },
4471
4471
  "methods": [
4472
4472
  {
@@ -4476,7 +4476,7 @@
4476
4476
  },
4477
4477
  "locationInModule": {
4478
4478
  "filename": "src/instrumentation.ts",
4479
- "line": 392
4479
+ "line": 407
4480
4480
  },
4481
4481
  "name": "visit",
4482
4482
  "overrides": "aws-cdk-lib.IAspect",
@@ -4507,7 +4507,7 @@
4507
4507
  "kind": "interface",
4508
4508
  "locationInModule": {
4509
4509
  "filename": "src/instrumentation.ts",
4510
- "line": 358
4510
+ "line": 373
4511
4511
  },
4512
4512
  "name": "OdigosInstrumentationAspectProps",
4513
4513
  "symbolId": "src/instrumentation:OdigosInstrumentationAspectProps"
@@ -4536,7 +4536,7 @@
4536
4536
  },
4537
4537
  {
4538
4538
  "docs": {
4539
- "remarks": "The adapter owns `AWS_LAMBDA_EXEC_WRAPPER`, so this mode does not touch it. Instrumentation is loaded\nthrough the language's own loader variable instead — `JAVA_TOOL_OPTIONS`, `NODE_OPTIONS` or\n`PYTHONPATH` — appended to (never replacing) whatever the function already sets.\n\nSetting `HANDLER` on a web-adapter function overwrites the adapter's wrapper and the function stops\nserving. Ruby has no verified web-server loader and is rejected at synth in this mode.",
4539
+ "remarks": "The adapter's existing wrapper is saved in `ODIGOS_LAMBDA_CONFIG.webWrapper`. This mode sets\n`/opt/odigos-webserver`, which loads the language agent and then invokes the saved wrapper.\nDefine the adapter wrapper before applying instrumentation. Existing loader options are preserved.\n\nSetting `HANDLER` on a web-adapter function overwrites the adapter's wrapper and the function stops\nserving. Ruby has no verified web-server loader and is rejected at synth in this mode.",
4540
4540
  "stability": "stable",
4541
4541
  "summary": "A function running a web server behind the AWS Lambda Web Adapter."
4542
4542
  },
@@ -4600,7 +4600,7 @@
4600
4600
  "abstract": true,
4601
4601
  "docs": {
4602
4602
  "default": "false",
4603
- "remarks": "The layer, SDK and collector then emit span and OTLP-export diagnostics into the function's CloudWatch\nlog group. Useful while wiring up a destination, expensive to leave on.\n\nHas no effect in web-server mode, matching the connector, which never sets a log level there.",
4603
+ "remarks": "The SDK and collector can then emit additional OTLP-export diagnostics into the function's CloudWatch\nlog group. Useful while wiring up a destination, expensive to leave on.\n\nHas no effect in web-server mode, matching the connector, which never sets a log level there.",
4604
4604
  "stability": "stable",
4605
4605
  "summary": "Raise in-function OpenTelemetry verbosity (`OTEL_LOG_LEVEL=debug`)."
4606
4606
  },
@@ -4636,15 +4636,15 @@
4636
4636
  {
4637
4637
  "abstract": true,
4638
4638
  "docs": {
4639
- "default": "- the loader variable is set to the Odigos value alone",
4640
- "remarks": "Only used in web-server mode. It has to be passed explicitly because CDK's `lambda.Function` exposes\nno way to read back an environment variable set elsewhere in the app — an omitted value here is\nsilently overwritten, which for `NODE_OPTIONS` can mean losing a flag the app needs to boot.",
4639
+ "default": "- preserve the function's existing loader variable",
4640
+ "remarks": "Only used in web-server mode. The launcher preserves the function's existing loader at startup;\nthis optional value overrides the customer's saved loader setting before instrumentation is added.",
4641
4641
  "stability": "stable",
4642
4642
  "summary": "The value the function already needs in its language loader variable (`NODE_OPTIONS`, `JAVA_TOOL_OPTIONS` or `PYTHONPATH`), which the Odigos loader is joined to rather than replacing."
4643
4643
  },
4644
4644
  "immutable": true,
4645
4645
  "locationInModule": {
4646
4646
  "filename": "src/instrumentation.ts",
4647
- "line": 144
4647
+ "line": 143
4648
4648
  },
4649
4649
  "name": "existingLoaderValue",
4650
4650
  "optional": true,
@@ -4756,7 +4756,7 @@
4756
4756
  "immutable": true,
4757
4757
  "locationInModule": {
4758
4758
  "filename": "src/instrumentation.ts",
4759
- "line": 155
4759
+ "line": 154
4760
4760
  },
4761
4761
  "name": "skipUnsupportedRuntimes",
4762
4762
  "optional": true,
@@ -4783,9 +4783,8 @@
4783
4783
  "members": [
4784
4784
  {
4785
4785
  "docs": {
4786
- "remarks": "The default.",
4787
4786
  "stability": "stable",
4788
- "summary": "`/opt/otel-handler` — a `RequestHandler` function."
4787
+ "summary": "`/opt/otel-handler` — a `RequestHandler` function when explicitly selecting this legacy adapter."
4789
4788
  },
4790
4789
  "name": "OTEL_HANDLER"
4791
4790
  },
@@ -4829,7 +4828,7 @@
4829
4828
  },
4830
4829
  "locationInModule": {
4831
4830
  "filename": "src/instrumentation.ts",
4832
- "line": 205
4831
+ "line": 204
4833
4832
  },
4834
4833
  "parameters": [
4835
4834
  {
@@ -4855,7 +4854,7 @@
4855
4854
  "kind": "class",
4856
4855
  "locationInModule": {
4857
4856
  "filename": "src/instrumentation.ts",
4858
- "line": 200
4857
+ "line": 199
4859
4858
  },
4860
4859
  "methods": [
4861
4860
  {
@@ -4866,7 +4865,7 @@
4866
4865
  },
4867
4866
  "locationInModule": {
4868
4867
  "filename": "src/instrumentation.ts",
4869
- "line": 245
4868
+ "line": 243
4870
4869
  },
4871
4870
  "name": "instrument",
4872
4871
  "parameters": [
@@ -4885,7 +4884,7 @@
4885
4884
  },
4886
4885
  "locationInModule": {
4887
4886
  "filename": "src/instrumentation.ts",
4888
- "line": 293
4887
+ "line": 304
4889
4888
  },
4890
4889
  "name": "instrumentAll",
4891
4890
  "parameters": [
@@ -4920,7 +4919,7 @@
4920
4919
  "kind": "interface",
4921
4920
  "locationInModule": {
4922
4921
  "filename": "src/instrumentation.ts",
4923
- "line": 161
4922
+ "line": 160
4924
4923
  },
4925
4924
  "name": "OdigosLambdaInstrumentationProps",
4926
4925
  "properties": [
@@ -4935,7 +4934,7 @@
4935
4934
  "immutable": true,
4936
4935
  "locationInModule": {
4937
4936
  "filename": "src/instrumentation.ts",
4938
- "line": 167
4937
+ "line": 166
4939
4938
  },
4940
4939
  "name": "functions",
4941
4940
  "optional": true,
@@ -4985,6 +4984,6 @@
4985
4984
  "symbolId": "src/types:OdigosLayerSource"
4986
4985
  }
4987
4986
  },
4988
- "version": "0.0.18",
4989
- "fingerprint": "5/xKYOxxDpKywcwlm/8NYunPMm8n0M9yKWyb2VKFn3Q="
4987
+ "version": "0.0.20",
4988
+ "fingerprint": "WRP2fthDVXOckrP5fNFi/uoko5EqmyyV6JB7k2HJSF0="
4990
4989
  }