@foam-ai/node 0.1.0-alpha.4 → 0.1.0-alpha.6
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/README.md +393 -214
- package/dist/before-send.d.ts +16 -0
- package/dist/before-send.js +41 -0
- package/dist/constants.d.ts +10 -8
- package/dist/constants.js +19 -6
- package/dist/endpoint.d.ts +2 -0
- package/dist/endpoint.js +11 -0
- package/dist/exporters.d.ts +11 -3
- package/dist/exporters.js +127 -53
- package/dist/index.d.ts +7 -2
- package/dist/index.js +8 -1
- package/dist/ingest.d.ts +8 -2
- package/dist/ingest.js +28 -16
- package/dist/init.d.ts +7 -3
- package/dist/init.js +85 -37
- package/dist/instrumentations.js +25 -3
- package/dist/logs.d.ts +10 -0
- package/dist/logs.js +61 -0
- package/dist/network-capture/collector.js +168 -69
- package/dist/network-capture/http.d.ts +0 -1
- package/dist/network-capture/http.js +23 -25
- package/dist/network-capture/index.js +10 -0
- package/dist/network-capture/redact.d.ts +9 -0
- package/dist/network-capture/redact.js +401 -0
- package/dist/network-capture/undici.d.ts +0 -4
- package/dist/network-capture/undici.js +55 -22
- package/dist/otlp.d.ts +8 -0
- package/dist/otlp.js +46 -0
- package/dist/propagation.d.ts +1 -1
- package/dist/propagation.js +6 -4
- package/dist/redaction-keys.d.ts +3 -0
- package/dist/redaction-keys.js +421 -0
- package/dist/redaction.d.ts +24 -0
- package/dist/redaction.js +270 -0
- package/dist/report.d.ts +9 -0
- package/dist/report.js +56 -0
- package/dist/state.d.ts +11 -4
- package/dist/state.js +26 -9
- package/dist/traces.d.ts +1 -0
- package/dist/traces.js +21 -0
- package/dist/utils.js +1 -1
- package/package.json +1 -1
- package/dist/diagnostics.d.ts +0 -13
- package/dist/diagnostics.js +0 -83
package/README.md
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
# Foam OpenTelemetry SDK for Node.js
|
|
2
2
|
|
|
3
|
-
`@foam-ai/node` is Foam's OpenTelemetry distribution for Node.js.
|
|
4
|
-
the official OpenTelemetry JavaScript SDK, node automatic instrumentations, and
|
|
5
|
-
OTLP/HTTP exporters with one `init()` call.
|
|
3
|
+
`@foam-ai/node` is Foam's OpenTelemetry distribution for Node.js.
|
|
6
4
|
|
|
7
5
|
## Requirements
|
|
8
6
|
|
|
9
7
|
- Node.js `^18.19.0 || >=20.6.0`, matching the supported engine range of
|
|
10
|
-
|
|
11
|
-
-
|
|
8
|
+
`@opentelemetry/auto-instrumentations-node`
|
|
9
|
+
- Foam ingest token, stored as `FOAM_OTEL_TOKEN`
|
|
12
10
|
|
|
13
11
|
## Install
|
|
14
12
|
|
|
@@ -18,11 +16,14 @@ npm install @foam-ai/node
|
|
|
18
16
|
|
|
19
17
|
## Initialize
|
|
20
18
|
|
|
21
|
-
Call `init()` before
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
Call `init()` before your app loads so OpenTelemetry can patch libraries as
|
|
20
|
+
they import. Put `init()` in its own file and preload it with `--import` or
|
|
21
|
+
`--require` (below).
|
|
22
|
+
|
|
23
|
+
Set `enabled: false` to turn Foam off.
|
|
24
|
+
|
|
25
|
+
Store the ingest token as `FOAM_OTEL_TOKEN` and pass
|
|
26
|
+
`process.env.FOAM_OTEL_TOKEN` to `init()`.
|
|
26
27
|
|
|
27
28
|
### TypeScript
|
|
28
29
|
|
|
@@ -40,13 +41,14 @@ init({
|
|
|
40
41
|
});
|
|
41
42
|
```
|
|
42
43
|
|
|
43
|
-
Compile it with the app. If TypeScript emits ESM
|
|
44
|
+
Compile it with the app. If TypeScript emits ESM, preload with `--import`
|
|
45
|
+
(same rule as ESM below):
|
|
44
46
|
|
|
45
47
|
```sh
|
|
46
48
|
node --import ./dist/instrumentation.js ./dist/app.js
|
|
47
49
|
```
|
|
48
50
|
|
|
49
|
-
If TypeScript emits CommonJS
|
|
51
|
+
If TypeScript emits CommonJS, preload with `--require`:
|
|
50
52
|
|
|
51
53
|
```sh
|
|
52
54
|
node --require ./dist/instrumentation.js ./dist/app.js
|
|
@@ -60,7 +62,11 @@ node --import tsx --import ./instrumentation.ts ./src/app.ts
|
|
|
60
62
|
|
|
61
63
|
### ESM
|
|
62
64
|
|
|
63
|
-
Create `instrumentation.mjs` with the same `init()` call
|
|
65
|
+
Create `instrumentation.mjs` with the same `init()` call.
|
|
66
|
+
|
|
67
|
+
`import` lines always run first, even if you write `init()` above them.
|
|
68
|
+
Don't import your app from that file. Use `--import`, or
|
|
69
|
+
`await import("./app.js")` after `init()`:
|
|
64
70
|
|
|
65
71
|
```sh
|
|
66
72
|
node --import ./instrumentation.mjs ./app.js
|
|
@@ -68,7 +74,9 @@ node --import ./instrumentation.mjs ./app.js
|
|
|
68
74
|
|
|
69
75
|
### CommonJS
|
|
70
76
|
|
|
71
|
-
Create `instrumentation.cjs
|
|
77
|
+
Create `instrumentation.cjs`. `require()` runs in order, so `init()` then
|
|
78
|
+
`require("./app")` in this file is fine. `--require` is still the better
|
|
79
|
+
preload:
|
|
72
80
|
|
|
73
81
|
```js
|
|
74
82
|
const { init } = require("@foam-ai/node");
|
|
@@ -90,10 +98,10 @@ node --require ./instrumentation.cjs ./app.js
|
|
|
90
98
|
|
|
91
99
|
### `init(options)`
|
|
92
100
|
|
|
93
|
-
|
|
94
|
-
and
|
|
101
|
+
Sets up tracing, metrics, logs, auto-instrumentation, OTLP export to Foam,
|
|
102
|
+
and W3C context propagation.
|
|
95
103
|
|
|
96
|
-
Call this once when Foam
|
|
104
|
+
Call this once. Use it when Foam should run OpenTelemetry for the process.
|
|
97
105
|
|
|
98
106
|
```js
|
|
99
107
|
import { init } from "@foam-ai/node";
|
|
@@ -113,6 +121,7 @@ init({
|
|
|
113
121
|
environment,
|
|
114
122
|
enabled,
|
|
115
123
|
token,
|
|
124
|
+
endpoint?,
|
|
116
125
|
version?,
|
|
117
126
|
sampleRate?,
|
|
118
127
|
additionalInstrumentations?,
|
|
@@ -123,51 +132,95 @@ init({
|
|
|
123
132
|
disableLogSending?,
|
|
124
133
|
networkCapture?,
|
|
125
134
|
ignoredOutboundHosts?,
|
|
126
|
-
|
|
135
|
+
redact?,
|
|
136
|
+
beforeSend?,
|
|
127
137
|
}): void
|
|
128
138
|
```
|
|
129
139
|
|
|
130
140
|
`name`, `environment`, and `enabled` are required.
|
|
131
|
-
`token` is required only when enabled; store it as `FOAM_OTEL_TOKEN`.
|
|
132
|
-
|
|
133
|
-
`
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
`
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
Foam
|
|
145
|
-
|
|
146
|
-
`
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
`
|
|
154
|
-
|
|
155
|
-
(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
### `createFoamIngestSpanProcessor(name, environment, token)`
|
|
163
|
-
|
|
164
|
-
Returns a span processor that exports traces to Foam without taking over the
|
|
165
|
-
global TracerProvider.
|
|
166
|
-
|
|
167
|
-
Use this when another OpenTelemetry SDK already owns traces and you only need
|
|
168
|
-
Foam as an extra export destination.
|
|
141
|
+
`token` is required only when Foam is enabled; store it as `FOAM_OTEL_TOKEN`.
|
|
142
|
+
|
|
143
|
+
`endpoint` defaults to Foam's OTLP endpoint and is where all telemetry is
|
|
144
|
+
delivered. Override it only to route through a Foam-compatible OTLP
|
|
145
|
+
gateway, such as an egress proxy or a local collector. It must be an
|
|
146
|
+
`http(s)` URL; its host is always excluded from outbound tracing so
|
|
147
|
+
export calls do not become client spans.
|
|
148
|
+
|
|
149
|
+
`version` sets `service.version`.
|
|
150
|
+
|
|
151
|
+
`sampleRate` is a number from `0` to `1` and defaults to `1`. Rarely if ever updated. Only change it if you are sending telemetry in the petabytes range.
|
|
152
|
+
|
|
153
|
+
`additionalInstrumentations` adds extra OpenTelemetry instrumentations next
|
|
154
|
+
to Foam's Node auto bundle and `ConsoleInstrumentation`.
|
|
155
|
+
|
|
156
|
+
`additionalSpanProcessors`, `additionalLogRecordProcessors`, and
|
|
157
|
+
`additionalMetricReaders` add extra processors/readers from other vendors next to Foam's
|
|
158
|
+
exporters. Note: Never add Foam's own processors/readers helpers.
|
|
159
|
+
|
|
160
|
+
`additionalResourceAttributes` adds extra resource attributes (for example
|
|
161
|
+
`team` or `cloud.region`) to all telemetry.
|
|
162
|
+
|
|
163
|
+
`disableLogSending` defaults to `false`. Set `true` to stop Foam from
|
|
164
|
+
adding its own sending path to supported loggers; trace correlation in
|
|
165
|
+
log records is unaffected (see Loggers).
|
|
166
|
+
|
|
167
|
+
`networkCapture` defaults to `"basic"`. Upgrade to `"advanced"` unless you
|
|
168
|
+
cannot; it gives much more powerful HTTP and Undici capture. See Network capture.
|
|
169
|
+
|
|
170
|
+
`redact` adds keys to Foam's built-in redaction. `secrets` preserves the last
|
|
171
|
+
four characters of longer values; `pii` fully masks values.
|
|
169
172
|
|
|
170
173
|
```js
|
|
174
|
+
init({
|
|
175
|
+
// ...
|
|
176
|
+
redact: {
|
|
177
|
+
secrets: ["internalId", "internalReference"],
|
|
178
|
+
pii: ["customerEmail", "homeAddress"],
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`beforeSend` can edit or drop spans and logs before Foam exports them. Return
|
|
184
|
+
the event to send it or `null` to drop it.
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
init({
|
|
188
|
+
// ...
|
|
189
|
+
beforeSend: (event) => {
|
|
190
|
+
if (event.type === "span" && event.attributes["url.path"] === "/health") {
|
|
191
|
+
return null; // drop health checks
|
|
192
|
+
}
|
|
193
|
+
delete event.attributes["user.email"];
|
|
194
|
+
return event;
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Metrics are not passed through `beforeSend`. See Redaction for coverage.
|
|
200
|
+
|
|
201
|
+
`ignoredOutboundHosts` skips outbound HTTP tracing for those hostnames.
|
|
202
|
+
The configured `endpoint` host is always skipped so export calls do not
|
|
203
|
+
become client spans. Add extra hosts for high-volume clients that would drown traces,
|
|
204
|
+
for example a sidecar health-check host (`localhost`) or another vendor's
|
|
205
|
+
OTLP ingest host you already export to.
|
|
206
|
+
|
|
207
|
+
TODO(pcga11): Add other common Gen AI providers.
|
|
208
|
+
|
|
209
|
+
If another SDK already registered traces, metrics, logs, or the propagator,
|
|
210
|
+
`init()` leaves the signal's slot untouched and only takes what is available. When a slot is taken, use its ingest helper instead.
|
|
211
|
+
|
|
212
|
+
### `createFoamIngestSpanProcessor(name, environment, token, options?)`
|
|
213
|
+
|
|
214
|
+
The helper returns a span processor but does not attach it to the
|
|
215
|
+
application's provider. Include it in the `spanProcessors` option when
|
|
216
|
+
constructing `BasicTracerProvider`. It cannot be added after provider
|
|
217
|
+
construction in `@opentelemetry/sdk-trace-base` 2.10; the old
|
|
218
|
+
`addSpanProcessor()` API is obsolete. If another SDK creates the provider,
|
|
219
|
+
configure the processor through that SDK's startup options.
|
|
220
|
+
|
|
221
|
+
```js
|
|
222
|
+
import { trace } from "@opentelemetry/api";
|
|
223
|
+
import { BasicTracerProvider } from "@opentelemetry/sdk-trace-base";
|
|
171
224
|
import { createFoamIngestSpanProcessor } from "@foam-ai/node";
|
|
172
225
|
|
|
173
226
|
const spanProcessor = createFoamIngestSpanProcessor(
|
|
@@ -175,17 +228,24 @@ const spanProcessor = createFoamIngestSpanProcessor(
|
|
|
175
228
|
"production",
|
|
176
229
|
process.env.FOAM_OTEL_TOKEN,
|
|
177
230
|
);
|
|
178
|
-
tracerProvider
|
|
231
|
+
const tracerProvider = new BasicTracerProvider({
|
|
232
|
+
spanProcessors: [spanProcessor],
|
|
233
|
+
});
|
|
234
|
+
trace.setGlobalTracerProvider(tracerProvider);
|
|
179
235
|
```
|
|
180
236
|
|
|
181
|
-
### `createFoamIngestLogRecordProcessor(name, environment, token)`
|
|
182
|
-
|
|
183
|
-
Returns a log-record processor that exports logs to Foam without taking over
|
|
184
|
-
the global LoggerProvider.
|
|
237
|
+
### `createFoamIngestLogRecordProcessor(name, environment, token, options?)`
|
|
185
238
|
|
|
186
|
-
|
|
239
|
+
The helper returns a log-record processor but does not attach it to the
|
|
240
|
+
application's provider. Include it in the `processors` option when constructing
|
|
241
|
+
`LoggerProvider`. It cannot be added after provider construction in
|
|
242
|
+
`@opentelemetry/sdk-logs` 0.221; the old `addLogRecordProcessor()` API is
|
|
243
|
+
obsolete. If another SDK creates the provider, configure the processor through
|
|
244
|
+
that SDK's startup options.
|
|
187
245
|
|
|
188
246
|
```js
|
|
247
|
+
import { logs } from "@opentelemetry/api-logs";
|
|
248
|
+
import { LoggerProvider } from "@opentelemetry/sdk-logs";
|
|
189
249
|
import { createFoamIngestLogRecordProcessor } from "@foam-ai/node";
|
|
190
250
|
|
|
191
251
|
const logRecordProcessor = createFoamIngestLogRecordProcessor(
|
|
@@ -193,18 +253,27 @@ const logRecordProcessor = createFoamIngestLogRecordProcessor(
|
|
|
193
253
|
"production",
|
|
194
254
|
process.env.FOAM_OTEL_TOKEN,
|
|
195
255
|
);
|
|
196
|
-
loggerProvider
|
|
256
|
+
const loggerProvider = new LoggerProvider({
|
|
257
|
+
processors: [logRecordProcessor],
|
|
258
|
+
});
|
|
259
|
+
logs.setGlobalLoggerProvider(loggerProvider);
|
|
197
260
|
```
|
|
198
261
|
|
|
199
|
-
|
|
262
|
+
Both ingest helpers accept the same `redact` and `beforeSend` options as
|
|
263
|
+
`init()`.
|
|
200
264
|
|
|
201
|
-
|
|
202
|
-
global MeterProvider.
|
|
265
|
+
### `createFoamIngestMetricReader(name, environment, token)`
|
|
203
266
|
|
|
204
|
-
|
|
205
|
-
|
|
267
|
+
The helper returns a metric reader but does not attach it to the application's
|
|
268
|
+
provider. Include it in the `readers` option when constructing `MeterProvider`.
|
|
269
|
+
It cannot be added after provider construction in
|
|
270
|
+
`@opentelemetry/sdk-metrics` 2.10; the old `addMetricReader()` API is obsolete.
|
|
271
|
+
If another SDK creates the provider, configure the reader through that SDK's
|
|
272
|
+
startup options.
|
|
206
273
|
|
|
207
274
|
```js
|
|
275
|
+
import { metrics } from "@opentelemetry/api";
|
|
276
|
+
import { MeterProvider } from "@opentelemetry/sdk-metrics";
|
|
208
277
|
import { createFoamIngestMetricReader } from "@foam-ai/node";
|
|
209
278
|
|
|
210
279
|
const metricReader = createFoamIngestMetricReader(
|
|
@@ -212,42 +281,65 @@ const metricReader = createFoamIngestMetricReader(
|
|
|
212
281
|
"production",
|
|
213
282
|
process.env.FOAM_OTEL_TOKEN,
|
|
214
283
|
);
|
|
215
|
-
meterProvider
|
|
284
|
+
const meterProvider = new MeterProvider({
|
|
285
|
+
readers: [metricReader],
|
|
286
|
+
});
|
|
287
|
+
metrics.setGlobalMeterProvider(meterProvider);
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
If a vendor SDK does not expose a processor or reader startup option, add Foam
|
|
291
|
+
at the collector instead.
|
|
292
|
+
|
|
293
|
+
If the other SDK already set `service.name` or environment,
|
|
294
|
+
Foam's values overwrite them on that copy only. The original spans, logs,
|
|
295
|
+
and metrics are left unchanged. For the Foam SDK, ensure all of the signals (spans, logs and metrics)
|
|
296
|
+
carry identical `service.name`.
|
|
297
|
+
|
|
298
|
+
Don't use `init()` and an ingest helper for the same signal. If another
|
|
299
|
+
SDK already owns traces (or another signal), call `init()` for the rest
|
|
300
|
+
and attach ingest only to the provider you don't own. Don't pass ingest
|
|
301
|
+
helpers as `additionalSpanProcessors` or extra readers: `init()` already
|
|
302
|
+
installs Foam exporters.
|
|
303
|
+
|
|
304
|
+
Ingest helpers do not register instrumentations. If you cannot call
|
|
305
|
+
`init()`, register them with the official OpenTelemetry API
|
|
306
|
+
before the app imports those libraries:
|
|
307
|
+
|
|
308
|
+
```js
|
|
309
|
+
import { registerInstrumentations } from "@opentelemetry/instrumentation";
|
|
310
|
+
import { RedisInstrumentation } from "@opentelemetry/instrumentation-redis";
|
|
311
|
+
import { createFoamIngestSpanProcessor } from "@foam-ai/node";
|
|
312
|
+
|
|
313
|
+
registerInstrumentations({
|
|
314
|
+
instrumentations: [new RedisInstrumentation()],
|
|
315
|
+
});
|
|
216
316
|
```
|
|
217
317
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
(
|
|
234
|
-
AWS X-Ray. If Foam wins the slot, auto-instrumented HTTP is W3C-only. If
|
|
235
|
-
another SDK already registered a propagator, Foam keeps it,
|
|
236
|
-
`getState().signals.baggage` is `false`, and HTTP speaks that winner's format.
|
|
237
|
-
A B3-only (or Jaeger/X-Ray-only) winner will not send or honor `traceparent`,
|
|
238
|
-
so a Foam peer that only understands W3C starts a new trace. Foam does not
|
|
239
|
-
wrap inject/extract to dual-read those formats or add W3C headers on top.
|
|
240
|
-
Initialize Foam first, or have the other SDK register a composite that
|
|
241
|
-
includes W3C, if those headers must be on HTTP.
|
|
318
|
+
Attach the ingest helper to the other SDK's provider as in the examples
|
|
319
|
+
above. If that SDK already instruments the same library, don't register a
|
|
320
|
+
second copy.
|
|
321
|
+
|
|
322
|
+
OpenTelemetry allows only one global propagator per signal. Foam registers W3C Trace
|
|
323
|
+
Context and W3C Baggage (`traceparent`, `tracestate`, `baggage`), not B3,
|
|
324
|
+
Jaeger, or AWS X-Ray. Auto-instrumented HTTP uses whichever propagator
|
|
325
|
+
won. If another SDK already registered a propagator, Foam keeps it and
|
|
326
|
+
`getState().signals.baggage` is `"none"`. A non-W3C propagator will not
|
|
327
|
+
send `traceparent`, so a Foam peer starts a new trace. Foam does not add
|
|
328
|
+
W3C headers on top.
|
|
329
|
+
|
|
330
|
+
A work around is to initialize Foam first, or include W3C in the other
|
|
331
|
+
SDK's composite, if those headers must be on HTTP.
|
|
332
|
+
|
|
333
|
+
TODO(pcga11): Handle traceparent conflict^
|
|
242
334
|
|
|
243
335
|
### `injectTraceContext(headers)`
|
|
244
336
|
|
|
245
337
|
Writes W3C `traceparent`, `tracestate`, and `baggage` onto a header map.
|
|
246
338
|
|
|
247
|
-
Use this when you send a message yourself (Kafka, a queue, a custom
|
|
248
|
-
Auto-instrumented HTTP, Express, Undici, and Fetch already inject
|
|
249
|
-
headers;
|
|
250
|
-
propagator even if Foam
|
|
339
|
+
Use this when you send a message yourself (Kafka, a queue, a custom
|
|
340
|
+
socket). Auto-instrumented HTTP, Express, Undici, and Fetch already inject
|
|
341
|
+
these headers; don't call this on ordinary HTTP. This uses Foam's local
|
|
342
|
+
W3C propagator so it works even if Foam did not get the global one.
|
|
251
343
|
|
|
252
344
|
```js
|
|
253
345
|
const headers = {};
|
|
@@ -257,10 +349,11 @@ await kafka.send({ value: payload, headers });
|
|
|
257
349
|
|
|
258
350
|
### `extractTraceContext(headers)`
|
|
259
351
|
|
|
260
|
-
Reads W3C trace and baggage headers and returns a parent OpenTelemetry
|
|
352
|
+
Reads W3C trace and baggage headers and returns a parent OpenTelemetry
|
|
353
|
+
context.
|
|
261
354
|
|
|
262
|
-
Use this when
|
|
263
|
-
the
|
|
355
|
+
Use this when receiving a message on a custom transport. Without extraction,
|
|
356
|
+
the handler loses the propagated parent context.
|
|
264
357
|
|
|
265
358
|
```js
|
|
266
359
|
import { context } from "@opentelemetry/api";
|
|
@@ -269,25 +362,22 @@ const parent = extractTraceContext(message.headers);
|
|
|
269
362
|
await context.with(parent, () => handle(message));
|
|
270
363
|
```
|
|
271
364
|
|
|
272
|
-
### `setBaggage(key, value)`
|
|
273
|
-
|
|
274
|
-
Stores a small string on the current request context.
|
|
365
|
+
### `setBaggage(key, value, callback)`
|
|
275
366
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
these keys until you call `injectTraceContext`.
|
|
367
|
+
Runs a callback with a request-scoped string such as a tenant or user ID.
|
|
368
|
+
The value follows work started by the callback across `await`s without leaking
|
|
369
|
+
to later handlers. Call `injectTraceContext` to send it on custom transports.
|
|
280
370
|
|
|
281
371
|
```js
|
|
282
|
-
setBaggage("tenant.id", "acme");
|
|
372
|
+
await setBaggage("tenant.id", "acme", () => handleRequest());
|
|
283
373
|
```
|
|
284
374
|
|
|
285
375
|
### `getBaggage(key)`
|
|
286
376
|
|
|
287
377
|
Returns a baggage value from the current request, or `undefined`.
|
|
288
378
|
|
|
289
|
-
Use this later in the same request to read a value set with `setBaggage`,
|
|
290
|
-
key that arrived on an inbound `baggage` header.
|
|
379
|
+
Use this later in the same request to read a value set with `setBaggage`,
|
|
380
|
+
or a key that arrived on an inbound `baggage` header.
|
|
291
381
|
|
|
292
382
|
```js
|
|
293
383
|
const tenant = getBaggage("tenant.id"); // "acme"
|
|
@@ -295,9 +385,9 @@ const tenant = getBaggage("tenant.id"); // "acme"
|
|
|
295
385
|
|
|
296
386
|
### `incrementCounter(name, value?, attributes?, options?)`
|
|
297
387
|
|
|
298
|
-
|
|
388
|
+
Creates the counter if it does not exist, then increments it.
|
|
299
389
|
|
|
300
|
-
Use this for event counts
|
|
390
|
+
This counter can only increases. Use this for event counts.
|
|
301
391
|
|
|
302
392
|
```js
|
|
303
393
|
incrementCounter("orders.created", 1, { "cloud.region": "us-west-1" });
|
|
@@ -305,10 +395,9 @@ incrementCounter("orders.created", 1, { "cloud.region": "us-west-1" });
|
|
|
305
395
|
|
|
306
396
|
### `recordHistogram(name, value, attributes?, options?)`
|
|
307
397
|
|
|
308
|
-
|
|
398
|
+
Creates the histogram if it does not exist, then records a measurement.
|
|
309
399
|
|
|
310
|
-
Use this for values you want percentiles or averages of: latency, duration,
|
|
311
|
-
payload size.
|
|
400
|
+
Use this for values you want percentiles or averages of: latency, duration, payload size.
|
|
312
401
|
|
|
313
402
|
```js
|
|
314
403
|
recordHistogram("checkout.duration", 0.142, undefined, { unit: "s" });
|
|
@@ -316,17 +405,20 @@ recordHistogram("checkout.duration", 0.142, undefined, { unit: "s" });
|
|
|
316
405
|
|
|
317
406
|
### `addUpDownCounter(name, value, attributes?, options?)`
|
|
318
407
|
|
|
319
|
-
|
|
408
|
+
Creates the up-down counter if it does not exist, then adds a signed
|
|
409
|
+
delta. The value can go up or down.
|
|
320
410
|
|
|
321
411
|
Use this for occupancy: active jobs, open connections, items in a pool.
|
|
322
412
|
|
|
413
|
+
In other terms, pass +value when something starts occupying a slot, -value when it leaves. The metric is the current state of occupancy.
|
|
414
|
+
|
|
323
415
|
```js
|
|
324
416
|
addUpDownCounter("jobs.active", -1);
|
|
325
417
|
```
|
|
326
418
|
|
|
327
419
|
### `setMetric(name, value, attributes?, options?)`
|
|
328
420
|
|
|
329
|
-
|
|
421
|
+
Creates the gauge if it does not exist, then sets its current value.
|
|
330
422
|
|
|
331
423
|
Use this for a point-in-time level: queue depth, heap used, cache size.
|
|
332
424
|
|
|
@@ -334,17 +426,51 @@ Use this for a point-in-time level: queue depth, heap used, cache size.
|
|
|
334
426
|
setMetric("queue.depth", 27);
|
|
335
427
|
```
|
|
336
428
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
429
|
+
TODO(pcga11): Investigate if it's worth support observable (pull) instruments API.
|
|
430
|
+
|
|
431
|
+
### `log(body, severity?, attributes?)`
|
|
432
|
+
|
|
433
|
+
Sends a log record to Foam on Foam's own export path. It does not write to the app's logger, and it does not emit on a LoggerProvider Foam does
|
|
434
|
+
not own.
|
|
435
|
+
|
|
436
|
+
Use this to send logs directly. Typically used for Pino 5–6, Winston 1–2 as they do not support sending logs.
|
|
437
|
+
|
|
438
|
+
```js
|
|
439
|
+
import { log, SeverityNumber } from "@foam-ai/node";
|
|
440
|
+
|
|
441
|
+
log("checkout failed");
|
|
442
|
+
log("checkout failed", SeverityNumber.ERROR);
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### `recordException(error)`
|
|
446
|
+
|
|
447
|
+
Records an exception on the current active span and sets the span status
|
|
448
|
+
to `ERROR`. Accepts an `Error` or any value (stringified). Never throws;
|
|
449
|
+
does nothing when no span is active or the span is not recording.
|
|
450
|
+
|
|
451
|
+
Use this in `catch` blocks inside auto-instrumented handlers (HTTP
|
|
452
|
+
routes, message consumers) where you did not create the span yourself.
|
|
453
|
+
The exception lands on whichever SDK owns the active span, so it works
|
|
454
|
+
even when another SDK owns tracing.
|
|
455
|
+
|
|
456
|
+
```js
|
|
457
|
+
import { recordException } from "@foam-ai/node";
|
|
458
|
+
|
|
459
|
+
try {
|
|
460
|
+
await chargeCard(order);
|
|
461
|
+
} catch (err) {
|
|
462
|
+
recordException(err);
|
|
463
|
+
throw err;
|
|
464
|
+
}
|
|
465
|
+
```
|
|
341
466
|
|
|
342
467
|
### `getState()`
|
|
343
468
|
|
|
344
|
-
Returns whether Foam initialized, which instrumentations registered, and
|
|
345
|
-
signals have an export path.
|
|
469
|
+
Returns whether Foam initialized, which instrumentations registered, and
|
|
470
|
+
which signals have an export path.
|
|
346
471
|
|
|
347
|
-
Use this in health checks, tests, or diagnostics to confirm Foam is
|
|
472
|
+
Use this in health checks, tests, or diagnostics to confirm Foam is
|
|
473
|
+
running.
|
|
348
474
|
|
|
349
475
|
```js
|
|
350
476
|
import { getState } from "@foam-ai/node";
|
|
@@ -354,117 +480,170 @@ getState();
|
|
|
354
480
|
// initialized: true,
|
|
355
481
|
// instrumentations: ["http", "express", "pg", ...],
|
|
356
482
|
// signals: {
|
|
357
|
-
// traces:
|
|
358
|
-
// metrics:
|
|
359
|
-
// logs:
|
|
360
|
-
// baggage:
|
|
361
|
-
// profile:
|
|
483
|
+
// traces: "global",
|
|
484
|
+
// metrics: "global",
|
|
485
|
+
// logs: "global",
|
|
486
|
+
// baggage: "global",
|
|
487
|
+
// profile: "none",
|
|
362
488
|
// },
|
|
363
489
|
// }
|
|
364
490
|
```
|
|
365
491
|
|
|
366
|
-
`instrumentations`
|
|
492
|
+
`instrumentations` lists only the instrumentations registered with a
|
|
367
493
|
successfully started Foam SDK, after upstream defaults and
|
|
368
494
|
`OTEL_NODE_ENABLED_INSTRUMENTATIONS` /
|
|
369
|
-
`OTEL_NODE_DISABLED_INSTRUMENTATIONS` filtering.
|
|
370
|
-
instrumentation is ready to patch a supported module when that module
|
|
371
|
-
|
|
495
|
+
`OTEL_NODE_DISABLED_INSTRUMENTATIONS` filtering. Registered means the
|
|
496
|
+
instrumentation is ready to patch a supported module when that module
|
|
497
|
+
loads.
|
|
498
|
+
|
|
499
|
+
Each `signals` value names the source of the Foam export path for that
|
|
500
|
+
signal:
|
|
501
|
+
|
|
502
|
+
- `"global"` — `init()` registered Foam's provider in the global
|
|
503
|
+
OpenTelemetry slot.
|
|
504
|
+
- `"ingest"` — a `createFoamIngest*` processor/reader was constructed for
|
|
505
|
+
another SDK's pipeline. Foam cannot tell whether you actually registered
|
|
506
|
+
the returned object.
|
|
507
|
+
- `"local"` — logs only: another SDK owns the global LoggerProvider, but
|
|
508
|
+
Foam keeps its own local provider so `log()` still delivers to Foam.
|
|
509
|
+
- `"none"` — no Foam export path.
|
|
510
|
+
|
|
511
|
+
A value other than `"none"` means telemetry is expected, not that the app
|
|
512
|
+
has already produced or exported it.
|
|
513
|
+
|
|
514
|
+
## Custom spans and other OpenTelemetry APIs
|
|
515
|
+
|
|
516
|
+
Foam does not wrap span creation. To start your own spans, read the
|
|
517
|
+
current span, or work with context, import `@opentelemetry/api` directly.
|
|
518
|
+
`init()` registers Foam's providers in the global OpenTelemetry slots, so
|
|
519
|
+
the official API routes to Foam automatically.
|
|
372
520
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
521
|
+
Install the API in your app so it resolves one compatible `1.x` copy:
|
|
522
|
+
|
|
523
|
+
```sh
|
|
524
|
+
npm install @opentelemetry/api
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
```js
|
|
528
|
+
import { trace, SpanStatusCode } from "@opentelemetry/api";
|
|
529
|
+
|
|
530
|
+
const tracer = trace.getTracer("checkout");
|
|
531
|
+
|
|
532
|
+
await tracer.startActiveSpan("charge-card", async (span) => {
|
|
533
|
+
try {
|
|
534
|
+
await chargeCard(order);
|
|
535
|
+
} catch (err) {
|
|
536
|
+
span.recordException(err);
|
|
537
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
538
|
+
throw err;
|
|
539
|
+
} finally {
|
|
540
|
+
span.end();
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
Prefer `startActiveSpan` over `startSpan`: it makes the span current, so
|
|
546
|
+
auto-instrumented HTTP/database spans and nested custom spans parent under
|
|
547
|
+
it. Always call `span.end()` (the `finally` above), or the span never
|
|
548
|
+
exports. When you did not create the span yourself, Foam's
|
|
549
|
+
`recordException(err)` does the exception-plus-ERROR-status pair on the
|
|
550
|
+
current active span for you.
|
|
551
|
+
|
|
552
|
+
The same applies to the rest of the API: `trace.getActiveSpan()` to tag
|
|
553
|
+
the current request (`span.setAttribute(...)`), and `context.with(...)`
|
|
554
|
+
to run code under a specific context.
|
|
555
|
+
|
|
556
|
+
This is safe in every state: if another SDK owns the global tracer slot,
|
|
557
|
+
these spans go to that SDK; before `init()` (or when Foam is disabled)
|
|
558
|
+
the API returns a no-op tracer and nothing is recorded. Custom spans
|
|
559
|
+
export wherever `getState().signals.traces` points.
|
|
379
560
|
|
|
380
561
|
## Network capture
|
|
381
562
|
|
|
382
|
-
`networkCapture` defaults to `"basic"` and controls HTTP
|
|
383
|
-
keeps standard OpenTelemetry HTTP/Undici telemetry with no extra capture,
|
|
384
|
-
`"basic"` adds allowlisted `content-type`, `content-length`, and
|
|
385
|
-
`content-encoding` headers, and `"advanced"` adds those headers plus complete
|
|
386
|
-
Node `http`/`https` (and Fetch/Undici, when diagnostics exist) request and
|
|
387
|
-
response bodies as raw wire bytes up to 1 MiB. Bodies are emitted as
|
|
388
|
-
correlated `foam.http.body.chunk` log events; the HTTP span records semantic
|
|
389
|
-
`http.*.body.size` and, for textual bodies, `http.*.body.content`, while
|
|
390
|
-
`authorization`, `cookie`, HTTP/2, and gRPC are never captured. Advanced
|
|
391
|
-
capture requires Foam to own the logger provider, closes incomplete bodies
|
|
392
|
-
after 60 seconds of inactivity, and is replaced if you override the bundled
|
|
393
|
-
HTTP instrumentation through `additionalInstrumentations`. Foam partitions
|
|
394
|
-
serialized trace and log export batches below 19 MiB to stay under the
|
|
395
|
-
ClickStack collector's 20 MiB OTLP/HTTP limit.
|
|
396
|
-
|
|
397
|
-
## Logger support and ownership
|
|
398
|
-
|
|
399
|
-
Foam follows the compatibility ranges of its OpenTelemetry logger
|
|
400
|
-
instrumentations:
|
|
401
|
-
|
|
402
|
-
- Bunyan `>=1 <2`
|
|
403
|
-
- Pino `>=5.14 <11`
|
|
404
|
-
- Winston `>=1 <4`
|
|
405
|
-
|
|
406
|
-
The init log line that lists registered instrumentations also names each
|
|
407
|
-
detected logger with its installed version. Initialize Foam before importing or
|
|
408
|
-
constructing the logger; registration alone does not prove that a logger was
|
|
409
|
-
loaded, patched, or produced records.
|
|
410
|
-
|
|
411
|
-
Logger trace correlation remains enabled by default. With
|
|
412
|
-
`disableLogSending: false` (the default), OpenTelemetry automatically sends:
|
|
413
|
-
|
|
414
|
-
- Bunyan records through its added OTel stream
|
|
415
|
-
- Pino 7 and newer records through an added main-thread multistream
|
|
416
|
-
- Winston 3 records through the bundled `OpenTelemetryTransportV3`
|
|
417
|
-
|
|
418
|
-
Pino 5 and 6 and Winston 1 and 2 support correlation only. Set
|
|
419
|
-
`disableLogSending: true` when the application manually installs a Bunyan OTel
|
|
420
|
-
stream, configures `pino-opentelemetry-transport`, installs a Winston OTel
|
|
421
|
-
transport, or otherwise gives another pipeline ownership of logger records.
|
|
422
|
-
This disables automatic sending for Bunyan, Pino, and Winston while preserving
|
|
423
|
-
trace correlation.
|
|
424
|
-
|
|
425
|
-
OpenTelemetry cannot reliably deduplicate manually configured streams and
|
|
426
|
-
transports; explicit `disableLogSending` ownership is the correctness
|
|
427
|
-
mechanism.
|
|
428
|
-
|
|
429
|
-
`ConsoleInstrumentation` separately turns calls to `console.*` into OTel log
|
|
430
|
-
records. It does not make arbitrary stdout/stderr writes observable.
|
|
563
|
+
`networkCapture` defaults to `"basic"` and controls extra HTTP detail.
|
|
431
564
|
|
|
432
|
-
|
|
565
|
+
- `"off"` keeps standard OpenTelemetry HTTP/Undici telemetry, with no extra
|
|
566
|
+
capture.
|
|
567
|
+
- `"basic"` adds allowlisted `content-type`, `content-length`, and
|
|
568
|
+
`content-encoding` headers.
|
|
569
|
+
- `"advanced"` adds those headers plus full Node `http`/`https` and
|
|
570
|
+
Fetch/Undici request and response bodies as raw wire bytes, up to 1 MiB. Always set to `"advanced"` when security and compliance allows.
|
|
433
571
|
|
|
434
|
-
|
|
435
|
-
propagation, instrumentation, and OTLP exporter packages. It does not fork
|
|
436
|
-
their data model or redefine spans, metrics, logs, resources, or context.
|
|
572
|
+
## Redaction
|
|
437
573
|
|
|
438
|
-
Foam
|
|
574
|
+
Foam always masks common credentials in captured request and response data,
|
|
575
|
+
URLs, and exported span and log fields. Add application-specific keys with the
|
|
576
|
+
`redact` option shown under `init(options)`.
|
|
439
577
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
- `OTEL_NODE_ENABLED_INSTRUMENTATIONS` and
|
|
443
|
-
`OTEL_NODE_DISABLED_INSTRUMENTATIONS`
|
|
444
|
-
- `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT`
|
|
445
|
-
- `OTEL_TRACES_SAMPLER`
|
|
578
|
+
Redaction applies to Foam's exports, including ingest helpers. It does not
|
|
579
|
+
modify telemetry sent through other exporters.
|
|
446
580
|
|
|
447
|
-
|
|
448
|
-
describes `opentelemetry-js`, not this distro. It uses `+` (implemented),
|
|
449
|
-
`-` (not implemented), `?` (unknown), and `N/A`. Foam does not reimplement
|
|
450
|
-
those SDK features and does not inherit every `+` row: `init()` only
|
|
451
|
-
constructs the packages and options this distro actually wires (W3C
|
|
452
|
-
propagation, OTLP/HTTP to Foam, a parent-based ratio sampler). Rows the
|
|
453
|
-
matrix marks `+` for JavaScript but Foam never installs stay unavailable
|
|
454
|
-
here, including OTLP/gRPC, Zipkin, Prometheus, Jaeger remote sampling,
|
|
455
|
-
B3/Jaeger propagators, metric views, and `OTEL_TRACES_SAMPLER`. Foam also
|
|
456
|
-
does not implement rows marked `-` or `?`.
|
|
581
|
+
## Loggers
|
|
457
582
|
|
|
458
|
-
|
|
583
|
+
Initialize Foam before you import or construct the logger.
|
|
584
|
+
|
|
585
|
+
What Foam does depends on the logger version:
|
|
586
|
+
|
|
587
|
+
- Bunyan 1, Pino 7–10, and Winston 3: Foam adds trace IDs to every
|
|
588
|
+
record and automatically sends a copy to Foam.
|
|
589
|
+
- Pino 5.14–6 and Winston 1–2: Foam adds trace IDs, but these versions
|
|
590
|
+
have no way to attach a sender, so no copy reaches Foam. Upgrade the
|
|
591
|
+
logger, or call `log()` yourself.
|
|
592
|
+
- Everything else: Foam does nothing. Use `console.*` (captured
|
|
593
|
+
below), call `log()`, or upgrade the logger.
|
|
594
|
+
|
|
595
|
+
### If the app already ships logs somewhere
|
|
596
|
+
|
|
597
|
+
- To a non-OTel destination (files, another vendor's transport): no
|
|
598
|
+
conflict. Keep the defaults; Foam sends its own copy alongside.
|
|
599
|
+
- Through its own OTel stream/transport (a Bunyan OTel stream,
|
|
600
|
+
Winston `OpenTelemetryTransportV3`): those already deliver to Foam
|
|
601
|
+
via the global LoggerProvider. Set `disableLogSending: true` so
|
|
602
|
+
records don't arrive twice.
|
|
603
|
+
- Through another OTel SDK's LoggerProvider: set
|
|
604
|
+
`disableLogSending: true` and attach
|
|
605
|
+
`createFoamIngestLogRecordProcessor` to that provider (see Public
|
|
606
|
+
API) so Foam gets a copy.
|
|
607
|
+
- Through `pino-opentelemetry-transport`: it runs in a worker
|
|
608
|
+
thread and never touches the in-process LoggerProvider, so nothing
|
|
609
|
+
conflicts. Keep `disableLogSending: false`, or Foam gets no Pino
|
|
610
|
+
records at all.
|
|
611
|
+
- Through a collector or agent reading stdout/files: no app changes;
|
|
612
|
+
add a Foam OTLP exporter to the collector config.
|
|
613
|
+
|
|
614
|
+
`log()` sends a single record straight to Foam in any setup. It is a
|
|
615
|
+
manual per-record call, not a mirror of the logger. Use it for
|
|
616
|
+
targeted, high-value records.
|
|
459
617
|
|
|
460
|
-
|
|
461
|
-
|
|
618
|
+
`ConsoleInstrumentation` turns calls to `console.*` into OTel log records.
|
|
619
|
+
It does not capture arbitrary stdout/stderr writes.
|
|
462
620
|
|
|
463
|
-
|
|
621
|
+
```js
|
|
622
|
+
console.log("hi"); // captured
|
|
623
|
+
process.stdout.write("hi\n"); // not captured
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
## OpenTelemetry configuration and compliance
|
|
627
|
+
|
|
628
|
+
Foam uses the official OpenTelemetry JavaScript API, SDK, resource,
|
|
629
|
+
propagation, instrumentation, and OTLP exporter packages.
|
|
630
|
+
|
|
631
|
+
The [OpenTelemetry JavaScript compliance matrix](https://github.com/open-telemetry/opentelemetry-specification/blob/main/spec-compliance-matrix/js.yaml)
|
|
632
|
+
|
|
633
|
+
## TODO(pcga11): OpenTelemetry profiling
|
|
464
634
|
|
|
465
|
-
Track upstream progress:
|
|
635
|
+
The official OpenTelemetry JavaScript SDK has no in-process Profiles provider, processor, or exporter yet. Track upstream progress:
|
|
466
636
|
|
|
467
637
|
- [OpenTelemetry JS profiling implementation issue](https://github.com/open-telemetry/opentelemetry-js/issues/6500)
|
|
468
638
|
- [Profiling SIG language SDK support tracker](https://github.com/open-telemetry/sig-profiling/issues/106)
|
|
469
639
|
- [OpenTelemetry Profiles public alpha announcement](https://opentelemetry.io/blog/2026/profiles-alpha/)
|
|
470
640
|
- [OpenTelemetry eBPF profiler](https://github.com/open-telemetry/opentelemetry-ebpf-profiler)
|
|
641
|
+
|
|
642
|
+
## TODO(pcga11): Anthropic instrumentation
|
|
643
|
+
|
|
644
|
+
There is no released official OpenTelemetry JavaScript instrumentation for the Anthropic SDK yet, but one is actively in progress in js-contrib, based on the OpenInference donation from Arize. Once `@opentelemetry/instrumentation-anthropic` is released (and picked up by `auto-instrumentations-node`), bundle it here like the OpenAI and aws-sdk ones. Until then, Anthropic coverage is handled case by case with the FDE (see FDE.md). Track upstream progress:
|
|
645
|
+
|
|
646
|
+
- [feat(instrumentation-anthropic): add basic messages instrumentation](https://github.com/open-telemetry/opentelemetry-js-contrib/pull/3664)
|
|
647
|
+
- [refactor(instrumentation-anthropic): use genai-util library](https://github.com/open-telemetry/opentelemetry-js-contrib/pull/3698)
|
|
648
|
+
- [Tracking: Migrate OpenInference JS instrumentations into opentelemetry-js-contrib](https://github.com/open-telemetry/opentelemetry-js-contrib/issues/3668)
|
|
649
|
+
- [feat: Add @opentelemetry/genai-util package for GenAI instrumentations](https://github.com/open-telemetry/opentelemetry-js-contrib/issues/3681)
|