@odigos/aws-cdk 0.0.10 → 0.0.12

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 Lambda\n\nInstrument AWS Lambda functions with the Odigos OpenTelemetry layer, from your own CDK app.\n\n```bash\nnpm install @odigos/aws-cdk # TypeScript / JavaScript\npip install odigos-aws-cdk # Python\n```\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## 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```ts\ndestination: {\n endpoint: 'otlp.example.com:4317',\n metrics: false, // traces and logs stay on\n}\n```\n\nA disabled signal gets no collector pipeline **and** the SDK is told not to produce it. Both halves\nmatter: a signal with no pipeline is refused at the receiver on every batch, so suppressing it at the\nsource is what stops the error loop.\n\nTurning all three off throws at synth — a collector with no pipeline fails to start, which would leave\nthe function instrumented and silent.\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## 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 a connector source ends up\nowned by both: each `cdk deploy` reasserts the CDK's environment, each reconcile reasserts the\nconnector's, and every round trip publishes a new function version.\n\nPick one per function. These constructs never set the connector's `ODIGOS_MANAGED` ownership marker — so\nan IaC-instrumented function is not one the connector will roll back — but the connector will take it\nover if you enable it as a source.\n\n## API\n\n| Export | |\n|---|---|\n| `OdigosLambdaInstrumentation` | the construct; `instrument(fn)`, `instrumentAll(fns)` |\n| `OdigosInstrumentationAspect` | applies it to every function in a scope |\n| `OdigosDestination` | where the collector exports; exactly one of `endpoint` / `httpEndpoint` |\n| `OdigosLayerSource` | `ODIGOS_PUBLIC` \\| `IN_ACCOUNT` |\n| `OdigosInstrumentationMode` | `HANDLER` \\| `WEB_SERVER` |\n| `OdigosJavaHandlerDistro` | the four Java wrappers |\n| `renderCollectorConfig` | the collector config renderer, if you want to inspect what gets set |\n| `LAYER_ARNS`, `ODIGOS_VERSION` | the baked layer table and the release it came from |\n\n## License\n\nApache-2.0.\n"
3829
+ "markdown": "# Odigos CDK constructs for AWS Lambda\n\nInstrument AWS Lambda functions with the Odigos OpenTelemetry layer, from your own CDK app.\n\n```bash\nnpm install @odigos/aws-cdk # TypeScript / JavaScript\npip install odigos-aws-cdk # Python\n```\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## Making Odigos report the function as instrumented\n\nBy default these constructs do **not** write `ODIGOS_MANAGED`, and the consequence is easy to hit: Odigos\nreports the function as `Absent` — \"awaiting your deployment\" — even though the layer is attached, the\nwrapper is set and the collector is configured. The connector derives ownership from that one variable, and\nchecks for it *before* comparing anything else, so nothing about a correctly instrumented function\nsubstitutes for it.\n\nFor a function Odigos discovers in **manual (IaC) mode** — which is the mode a workload is in unless you\nhave explicitly handed instrumentation to the connector — turn it on:\n\n```ts\nnew OdigosLambdaInstrumentation(this, 'Odigos', {\n destination: { endpoint: 'otlp.example.com:4317' },\n functions: [fn],\n recordOwnershipMarker: true,\n});\n```\n\nThe marker records the exact layer ARN the construct attached, which is also what the connector reads back\nto decide which layer version the function is *supposed* to carry — so a version you pinned is respected\ninstead of being reported as drift. It works in `IN_ACCOUNT` mode too, where the ARN is a deploy-time\nreference your own code has no way to obtain.\n\nIt also records a rollback snapshot (`prevEnv`) naming every variable the construct set. That matters: a\nmarker without one makes an uninstrument remove the **layer** while leaving `AWS_LAMBDA_EXEC_WRAPPER`\npointing into it, which breaks the function at its next cold start. In web-server mode the snapshot holds\nyour `existingLoaderValue`, so a rollback restores your own `NODE_OPTIONS` rather than deleting it.\n\nLeave it `false` for a function you have enabled as an **automatic-mode** connector source: there the\nconnector owns the marker, including a snapshot it wrote itself, and two writers of one ownership record is\nthe combination that genuinely misbehaves.\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 `recordOwnershipMarker` is for.\n\n## API\n\n| Export | |\n|---|---|\n| `OdigosLambdaInstrumentation` | the construct; `instrument(fn)`, `instrumentAll(fns)` |\n| `OdigosInstrumentationAspect` | applies it to every function in a scope |\n| `OdigosDestination` | where the collector exports; exactly one of `endpoint` / `httpEndpoint` |\n| `OdigosLayerSource` | `ODIGOS_PUBLIC` \\| `IN_ACCOUNT` |\n| `OdigosInstrumentationMode` | `HANDLER` \\| `WEB_SERVER` |\n| `OdigosJavaHandlerDistro` | the four Java wrappers |\n| `recordOwnershipMarker` | the option that makes Odigos recognise the function as instrumented |\n| `renderCollectorConfig` | the collector config renderer, if you want to inspect what gets set |\n| `LAYER_ARNS`, `ODIGOS_VERSION` | the baked layer table and the release it came from |\n\n## License\n\nApache-2.0.\n"
3830
3830
  },
3831
3831
  "repository": {
3832
3832
  "directory": "cdk",
@@ -3947,14 +3947,15 @@
3947
3947
  {
3948
3948
  "abstract": true,
3949
3949
  "docs": {
3950
- "default": "true",
3950
+ "default": "false",
3951
+ "remarks": "Opt-in for the same reason as `metrics`. Note that a Lambda function's stdout already reaches\nCloudWatch Logs regardless of this setting; this controls only what the OpenTelemetry SDK emits to\nthe in-Lambda collector and the collector forwards to your destination.",
3951
3952
  "stability": "stable",
3952
- "summary": "Send logs."
3953
+ "summary": "Send logs. OFF unless you turn it on."
3953
3954
  },
3954
3955
  "immutable": true,
3955
3956
  "locationInModule": {
3956
3957
  "filename": "src/types.ts",
3957
- "line": 164
3958
+ "line": 176
3958
3959
  },
3959
3960
  "name": "logs",
3960
3961
  "optional": true,
@@ -3965,14 +3966,15 @@
3965
3966
  {
3966
3967
  "abstract": true,
3967
3968
  "docs": {
3968
- "default": "true",
3969
+ "default": "false",
3970
+ "remarks": "Opt-in rather than on-by-default because the cost of a signal the destination does not implement is\npaid by the function: every batch is refused and the in-Lambda collector retries it on an exponential\nbackoff, inside billed duration. Traces are what nearly every destination accepts; metrics are a\nchoice about the destination, so they are made explicitly.",
3969
3971
  "stability": "stable",
3970
- "summary": "Send metrics."
3972
+ "summary": "Send metrics. OFF unless you turn it on."
3971
3973
  },
3972
3974
  "immutable": true,
3973
3975
  "locationInModule": {
3974
3976
  "filename": "src/types.ts",
3975
- "line": 157
3977
+ "line": 165
3976
3978
  },
3977
3979
  "name": "metrics",
3978
3980
  "optional": true,
@@ -3984,14 +3986,14 @@
3984
3986
  "abstract": true,
3985
3987
  "docs": {
3986
3988
  "default": "true",
3987
- "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.",
3989
+ "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.",
3988
3990
  "stability": "stable",
3989
3991
  "summary": "Send traces."
3990
3992
  },
3991
3993
  "immutable": true,
3992
3994
  "locationInModule": {
3993
3995
  "filename": "src/types.ts",
3994
- "line": 150
3996
+ "line": 153
3995
3997
  },
3996
3998
  "name": "traces",
3997
3999
  "optional": true,
@@ -4016,7 +4018,7 @@
4016
4018
  },
4017
4019
  "locationInModule": {
4018
4020
  "filename": "src/instrumentation.ts",
4019
- "line": 339
4021
+ "line": 448
4020
4022
  },
4021
4023
  "parameters": [
4022
4024
  {
@@ -4043,7 +4045,7 @@
4043
4045
  "kind": "class",
4044
4046
  "locationInModule": {
4045
4047
  "filename": "src/instrumentation.ts",
4046
- "line": 331
4048
+ "line": 440
4047
4049
  },
4048
4050
  "methods": [
4049
4051
  {
@@ -4053,7 +4055,7 @@
4053
4055
  },
4054
4056
  "locationInModule": {
4055
4057
  "filename": "src/instrumentation.ts",
4056
- "line": 346
4058
+ "line": 455
4057
4059
  },
4058
4060
  "name": "visit",
4059
4061
  "overrides": "aws-cdk-lib.IAspect",
@@ -4084,7 +4086,7 @@
4084
4086
  "kind": "interface",
4085
4087
  "locationInModule": {
4086
4088
  "filename": "src/instrumentation.ts",
4087
- "line": 312
4089
+ "line": 421
4088
4090
  },
4089
4091
  "name": "OdigosInstrumentationAspectProps",
4090
4092
  "symbolId": "src/instrumentation:OdigosInstrumentationAspectProps"
@@ -4134,7 +4136,7 @@
4134
4136
  "kind": "interface",
4135
4137
  "locationInModule": {
4136
4138
  "filename": "src/instrumentation.ts",
4137
- "line": 67
4139
+ "line": 85
4138
4140
  },
4139
4141
  "name": "OdigosInstrumentationOptions",
4140
4142
  "properties": [
@@ -4147,7 +4149,7 @@
4147
4149
  "immutable": true,
4148
4150
  "locationInModule": {
4149
4151
  "filename": "src/instrumentation.ts",
4150
- "line": 71
4152
+ "line": 89
4151
4153
  },
4152
4154
  "name": "destination",
4153
4155
  "type": {
@@ -4165,7 +4167,7 @@
4165
4167
  "immutable": true,
4166
4168
  "locationInModule": {
4167
4169
  "filename": "src/instrumentation.ts",
4168
- "line": 123
4170
+ "line": 141
4169
4171
  },
4170
4172
  "name": "debug",
4171
4173
  "optional": true,
@@ -4184,7 +4186,7 @@
4184
4186
  "immutable": true,
4185
4187
  "locationInModule": {
4186
4188
  "filename": "src/instrumentation.ts",
4187
- "line": 145
4189
+ "line": 163
4188
4190
  },
4189
4191
  "name": "existingLoaderValue",
4190
4192
  "optional": true,
@@ -4203,7 +4205,7 @@
4203
4205
  "immutable": true,
4204
4206
  "locationInModule": {
4205
4207
  "filename": "src/instrumentation.ts",
4206
- "line": 96
4208
+ "line": 114
4207
4209
  },
4208
4210
  "name": "javaHandlerDistro",
4209
4211
  "optional": true,
@@ -4221,7 +4223,7 @@
4221
4223
  "immutable": true,
4222
4224
  "locationInModule": {
4223
4225
  "filename": "src/instrumentation.ts",
4224
- "line": 78
4226
+ "line": 96
4225
4227
  },
4226
4228
  "name": "layerSource",
4227
4229
  "optional": true,
@@ -4240,7 +4242,7 @@
4240
4242
  "immutable": true,
4241
4243
  "locationInModule": {
4242
4244
  "filename": "src/instrumentation.ts",
4243
- "line": 133
4245
+ "line": 151
4244
4246
  },
4245
4247
  "name": "layerVersion",
4246
4248
  "optional": true,
@@ -4259,7 +4261,7 @@
4259
4261
  "immutable": true,
4260
4262
  "locationInModule": {
4261
4263
  "filename": "src/instrumentation.ts",
4262
- "line": 89
4264
+ "line": 107
4263
4265
  },
4264
4266
  "name": "mode",
4265
4267
  "optional": true,
@@ -4267,6 +4269,25 @@
4267
4269
  "fqn": "@odigos/aws-cdk.OdigosInstrumentationMode"
4268
4270
  }
4269
4271
  },
4272
+ {
4273
+ "abstract": true,
4274
+ "docs": {
4275
+ "default": "false",
4276
+ "remarks": "WITHOUT IT THE FUNCTION IS REPORTED AS UNINSTRUMENTED. The connector derives ownership from this\nvariable alone: no marker means `Absent` — \"awaiting your deployment\" — even though the layer is\nattached, the wrapper is set and the collector is configured. Set this to `true` for a function that\nOdigos discovers in MANUAL (IaC) mode, which is the mode a workload is in unless you explicitly hand\ninstrumentation to the connector.\n\nLeave it `false` (the default) for a function you have enabled as an AUTOMATIC-mode connector source.\nThere, the connector instruments the function itself and owns this variable, including the `prevEnv`\nsnapshot it restores on rollback; two writers of one ownership record is the one combination that\ngenuinely misbehaves.\n\nThe marker records the exact layer ARN this construct attached, which is also what the connector reads\nback to decide which layer version the function is supposed to carry — so a version you pinned is\nrespected rather than reported as drift. It works in every layer mode, including `IN_ACCOUNT`, where the\nARN is a deploy-time reference your own code cannot obtain.",
4277
+ "stability": "stable",
4278
+ "summary": "Write `ODIGOS_MANAGED`, the marker that makes the Odigos connector recognise this function as instrumented."
4279
+ },
4280
+ "immutable": true,
4281
+ "locationInModule": {
4282
+ "filename": "src/instrumentation.ts",
4283
+ "line": 187
4284
+ },
4285
+ "name": "recordOwnershipMarker",
4286
+ "optional": true,
4287
+ "type": {
4288
+ "primitive": "boolean"
4289
+ }
4290
+ },
4270
4291
  {
4271
4292
  "abstract": true,
4272
4293
  "docs": {
@@ -4278,7 +4299,7 @@
4278
4299
  "immutable": true,
4279
4300
  "locationInModule": {
4280
4301
  "filename": "src/instrumentation.ts",
4281
- "line": 111
4302
+ "line": 129
4282
4303
  },
4283
4304
  "name": "serviceName",
4284
4305
  "optional": true,
@@ -4297,7 +4318,7 @@
4297
4318
  "immutable": true,
4298
4319
  "locationInModule": {
4299
4320
  "filename": "src/instrumentation.ts",
4300
- "line": 156
4321
+ "line": 198
4301
4322
  },
4302
4323
  "name": "skipUnsupportedRuntimes",
4303
4324
  "optional": true,
@@ -4370,7 +4391,7 @@
4370
4391
  },
4371
4392
  "locationInModule": {
4372
4393
  "filename": "src/instrumentation.ts",
4373
- "line": 207
4394
+ "line": 249
4374
4395
  },
4375
4396
  "parameters": [
4376
4397
  {
@@ -4396,7 +4417,7 @@
4396
4417
  "kind": "class",
4397
4418
  "locationInModule": {
4398
4419
  "filename": "src/instrumentation.ts",
4399
- "line": 201
4420
+ "line": 243
4400
4421
  },
4401
4422
  "methods": [
4402
4423
  {
@@ -4407,7 +4428,7 @@
4407
4428
  },
4408
4429
  "locationInModule": {
4409
4430
  "filename": "src/instrumentation.ts",
4410
- "line": 226
4431
+ "line": 289
4411
4432
  },
4412
4433
  "name": "instrument",
4413
4434
  "parameters": [
@@ -4426,7 +4447,7 @@
4426
4447
  },
4427
4448
  "locationInModule": {
4428
4449
  "filename": "src/instrumentation.ts",
4429
- "line": 257
4450
+ "line": 322
4430
4451
  },
4431
4452
  "name": "instrumentAll",
4432
4453
  "parameters": [
@@ -4461,7 +4482,7 @@
4461
4482
  "kind": "interface",
4462
4483
  "locationInModule": {
4463
4484
  "filename": "src/instrumentation.ts",
4464
- "line": 162
4485
+ "line": 204
4465
4486
  },
4466
4487
  "name": "OdigosLambdaInstrumentationProps",
4467
4488
  "properties": [
@@ -4476,7 +4497,7 @@
4476
4497
  "immutable": true,
4477
4498
  "locationInModule": {
4478
4499
  "filename": "src/instrumentation.ts",
4479
- "line": 168
4500
+ "line": 210
4480
4501
  },
4481
4502
  "name": "functions",
4482
4503
  "optional": true,
@@ -4526,6 +4547,6 @@
4526
4547
  "symbolId": "src/types:OdigosLayerSource"
4527
4548
  }
4528
4549
  },
4529
- "version": "0.0.10",
4530
- "fingerprint": "fx+B66BFFPxbnPyQDWebGpotmbtitQSQDn87AebqIa8="
4550
+ "version": "0.0.12",
4551
+ "fingerprint": "76adPuc8StHhgh9x1dH8rP9GBtLmzzNKY1d5Dvhv+hM="
4531
4552
  }
package/README.md CHANGED
@@ -41,6 +41,14 @@ drains, so a low-traffic function does not lose its telemetry to the freeze.
41
41
  Consequently `OTEL_EXPORTER_OTLP_ENDPOINT` is deliberately never set. If you set it yourself you put the
42
42
  export back on the invocation's critical path.
43
43
 
44
+ ## Endpoint grammar
45
+
46
+ `endpoint` is OTLP/**gRPC** as bare `host:port` (conventionally 4317). `httpEndpoint` is OTLP/**HTTP** as a
47
+ scheme-qualified URL (conventionally 4318). Exactly one. They select different collector exporters with
48
+ different endpoint grammars, so a scheme in `endpoint` — or its absence in `httpEndpoint` — throws at synth
49
+ rather than producing a collector that starts and silently delivers nothing. A gRPC endpoint on port 4318
50
+ (or an HTTP one on 4317) is the likeliest version of that mistake, and is reported as a synth warning.
51
+
44
52
  ## Choosing a layer source
45
53
 
46
54
  ```ts
@@ -128,19 +136,33 @@ merely produce thinner spans.
128
136
 
129
137
  ## Signals
130
138
 
139
+ **Traces only, by default.** Metrics and logs are opt-in:
140
+
131
141
  ```ts
132
142
  destination: {
133
143
  endpoint: 'otlp.example.com:4317',
134
- metrics: false, // traces and logs stay on
144
+ metrics: true, // off unless you ask
145
+ logs: true, // off unless you ask
135
146
  }
136
147
  ```
137
148
 
138
- A disabled signal gets no collector pipeline **and** the SDK is told not to produce it. Both halves
139
- matter: a signal with no pipeline is refused at the receiver on every batch, so suppressing it at the
140
- source is what stops the error loop.
149
+ They are opt-in because the cost of a signal the destination does not implement falls on the function:
150
+ every batch is refused and the in-Lambda collector retries it on an exponential backoff, inside billed
151
+ duration. Traces are what nearly every destination accepts; the other two are a decision about your
152
+ destination, so they are made deliberately.
141
153
 
142
- Turning all three off throws at synth — a collector with no pipeline fails to start, which would leave
143
- the function instrumented and silent.
154
+ A disabled signal gets no collector pipeline **and** the SDK is told not to produce it
155
+ (`OTEL_METRICS_EXPORTER=none`). Both halves matter: a signal with no pipeline is refused at the receiver on
156
+ every batch, so suppressing it at the source is what stops the error loop. An enabled signal's variable
157
+ stays unset, because `otlp` is already the SDK default.
158
+
159
+ `traces: false` with neither of the others on throws at synth — a collector with no pipeline fails to
160
+ start, which would leave the function instrumented and silent.
161
+
162
+ **If Odigos also manages this function in manual mode, the signals have to match.** The connector renders
163
+ the collector configuration from the destination configured in Odigos and compares the whole string, so a
164
+ traces-only function judged against an all-three destination is reported as drifted on the
165
+ collector-export component. Enable the same set on both sides.
144
166
 
145
167
  ## Instrumenting a whole stack
146
168
 
@@ -162,16 +184,60 @@ environment variable — readable by anyone with `lambda:GetFunctionConfiguratio
162
184
  console. For a long-lived credential, prefer a destination that authenticates by network position, or
163
185
  terminate at a collector you control.
164
186
 
187
+ ## Making Odigos report the function as instrumented
188
+
189
+ By default these constructs do **not** write `ODIGOS_MANAGED`, and the consequence is easy to hit: Odigos
190
+ reports the function as `Absent` — "awaiting your deployment" — even though the layer is attached, the
191
+ wrapper is set and the collector is configured. The connector derives ownership from that one variable, and
192
+ checks for it *before* comparing anything else, so nothing about a correctly instrumented function
193
+ substitutes for it.
194
+
195
+ For a function Odigos discovers in **manual (IaC) mode** — which is the mode a workload is in unless you
196
+ have explicitly handed instrumentation to the connector — turn it on:
197
+
198
+ ```ts
199
+ new OdigosLambdaInstrumentation(this, 'Odigos', {
200
+ destination: { endpoint: 'otlp.example.com:4317' },
201
+ functions: [fn],
202
+ recordOwnershipMarker: true,
203
+ });
204
+ ```
205
+
206
+ The marker records the exact layer ARN the construct attached, which is also what the connector reads back
207
+ to decide which layer version the function is *supposed* to carry — so a version you pinned is respected
208
+ instead of being reported as drift. It works in `IN_ACCOUNT` mode too, where the ARN is a deploy-time
209
+ reference your own code has no way to obtain.
210
+
211
+ It also records a rollback snapshot (`prevEnv`) naming every variable the construct set. That matters: a
212
+ marker without one makes an uninstrument remove the **layer** while leaving `AWS_LAMBDA_EXEC_WRAPPER`
213
+ pointing into it, which breaks the function at its next cold start. In web-server mode the snapshot holds
214
+ your `existingLoaderValue`, so a rollback restores your own `NODE_OPTIONS` rather than deleting it.
215
+
216
+ Leave it `false` for a function you have enabled as an **automatic-mode** connector source: there the
217
+ connector owns the marker, including a snapshot it wrote itself, and two writers of one ownership record is
218
+ the combination that genuinely misbehaves.
219
+
220
+ ## Service names
221
+
222
+ Left unset, `OTEL_SERVICE_NAME` is the function's physical name when your stack names it explicitly, and
223
+ the construct id otherwise. The fallback exists because a name CloudFormation generates is a `Ref` to the
224
+ function, and a function referencing itself in its own environment is a dependency cycle CloudFormation
225
+ rejects at deploy time.
226
+
227
+ The connector compares `OTEL_SERVICE_NAME` against the workload's name in Odigos, which is the real AWS
228
+ function name — so for a function whose name is generated, pass `serviceName` explicitly or it is reported
229
+ as drifted. `serviceName` applies to **every** function a construct instruments, so use one construct per
230
+ function when you want per-function names.
231
+
165
232
  ## Do not also instrument these functions with the Odigos connector
166
233
 
167
234
  The Odigos AWS connector instruments **live** functions by mutating them, and reconciles them back to its
168
- own desired state on every pass. A function instrumented here *and* enabled as a connector source ends up
169
- owned by both: each `cdk deploy` reasserts the CDK's environment, each reconcile reasserts the
170
- connector's, and every round trip publishes a new function version.
235
+ own desired state on every pass. A function instrumented here *and* enabled as an automatic-mode connector
236
+ source ends up owned by both: each `cdk deploy` reasserts the CDK's environment, each reconcile reasserts
237
+ the connector's, and every round trip publishes a new function version.
171
238
 
172
- Pick one per function. These constructs never set the connector's `ODIGOS_MANAGED` ownership marker — so
173
- an IaC-instrumented function is not one the connector will roll back — but the connector will take it
174
- over if you enable it as a source.
239
+ Pick one per function. Discovery-only (manual) mode is the combination that works: Odigos watches and
240
+ reports, your stack owns the configuration — that is what `recordOwnershipMarker` is for.
175
241
 
176
242
  ## API
177
243
 
@@ -183,6 +249,7 @@ over if you enable it as a source.
183
249
  | `OdigosLayerSource` | `ODIGOS_PUBLIC` \| `IN_ACCOUNT` |
184
250
  | `OdigosInstrumentationMode` | `HANDLER` \| `WEB_SERVER` |
185
251
  | `OdigosJavaHandlerDistro` | the four Java wrappers |
252
+ | `recordOwnershipMarker` | the option that makes Odigos recognise the function as instrumented |
186
253
  | `renderCollectorConfig` | the collector config renderer, if you want to inspect what gets set |
187
254
  | `LAYER_ARNS`, `ODIGOS_VERSION` | the baked layer table and the release it came from |
188
255
 
@@ -43,8 +43,36 @@ export declare const EXPORTER_NONE = "none";
43
43
  export declare const ENV_TRACES_EXPORTER = "OTEL_TRACES_EXPORTER";
44
44
  export declare const ENV_METRICS_EXPORTER = "OTEL_METRICS_EXPORTER";
45
45
  export declare const ENV_LOGS_EXPORTER = "OTEL_LOGS_EXPORTER";
46
- /** Whether a signal is enabled, defaulting to on. */
47
- export declare function signalEnabled(value: boolean | undefined): boolean;
46
+ /**
47
+ * Whether a signal is enabled, given the caller's value and the signal's own default.
48
+ *
49
+ * The defaults are NOT uniform: traces are on, metrics and logs are off (see `SIGNAL_DEFAULTS`). So the
50
+ * default has to travel with the signal rather than being folded into one "unset means on" rule.
51
+ */
52
+ export declare function signalEnabled(value: boolean | undefined, defaultEnabled: boolean): boolean;
53
+ /**
54
+ * Which signals a destination carries when the caller says nothing.
55
+ *
56
+ * TRACES ONLY. Metrics and logs are opt-in because the cost of having them on by default falls on people
57
+ * who never asked for them: a destination that does not implement a signal refuses every batch of it, and
58
+ * the in-Lambda collector then retries on an exponential backoff inside billed duration. Traces are what
59
+ * almost every destination accepts and what almost everyone instruments for; the other two are a
60
+ * deliberate choice about the destination, so they are made deliberately.
61
+ *
62
+ * Turning one on is `metrics: true`. Enabling a signal also drops the `OTEL_*_EXPORTER=none` override, so
63
+ * the SDK starts producing it again.
64
+ */
65
+ export declare const SIGNAL_DEFAULTS: {
66
+ traces: boolean;
67
+ metrics: boolean;
68
+ logs: boolean;
69
+ };
70
+ /** The three signals resolved against their defaults — the single place that pairing is decided. */
71
+ export declare function resolvedSignals(dest: OdigosDestination): {
72
+ traces: boolean;
73
+ metrics: boolean;
74
+ logs: boolean;
75
+ };
48
76
  /**
49
77
  * Picks the exporter by the transport the destination configured. A destination carries a gRPC endpoint
50
78
  * (type `otlp`) or an OTLP/HTTP one (type `otlphttp`); the two are different collector components with