@foam-ai/node 0.1.0-alpha.2 → 0.1.0-alpha.4
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 +424 -77
- package/dist/constants.d.ts +42 -0
- package/dist/constants.js +54 -0
- package/dist/diagnostics.d.ts +13 -0
- package/dist/diagnostics.js +83 -0
- package/dist/exporters.d.ts +25 -0
- package/dist/exporters.js +105 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +21 -0
- package/dist/ingest.d.ts +6 -0
- package/dist/ingest.js +58 -0
- package/dist/init.d.ts +22 -0
- package/dist/init.js +121 -0
- package/dist/instrumentations.d.ts +3 -0
- package/dist/instrumentations.js +79 -0
- package/dist/metrics.d.ts +5 -0
- package/dist/metrics.js +29 -0
- package/dist/network-capture/collector.d.ts +27 -0
- package/dist/network-capture/collector.js +312 -0
- package/dist/network-capture/http.d.ts +6 -0
- package/dist/network-capture/http.js +223 -0
- package/dist/network-capture/index.d.ts +2 -0
- package/dist/network-capture/index.js +7 -0
- package/dist/network-capture/undici.d.ts +9 -0
- package/dist/network-capture/undici.js +144 -0
- package/dist/propagation.d.ts +5 -0
- package/dist/propagation.js +45 -0
- package/dist/resource.d.ts +3 -0
- package/dist/resource.js +24 -0
- package/dist/state.d.ts +19 -0
- package/dist/state.js +44 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +20 -0
- package/package.json +47 -19
- package/dist/node/src/capture-exception.d.ts +0 -9
- package/dist/node/src/capture-exception.js +0 -31
- package/dist/node/src/index.d.ts +0 -9
- package/dist/node/src/index.js +0 -12
- package/dist/node/src/init.d.ts +0 -27
- package/dist/node/src/init.js +0 -213
- package/dist/shared/constants.d.ts +0 -1
- package/dist/shared/constants.js +0 -4
- package/dist/shared/util.d.ts +0 -9
- package/dist/shared/util.js +0 -21
package/README.md
CHANGED
|
@@ -1,123 +1,470 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Foam OpenTelemetry SDK for Node.js
|
|
2
2
|
|
|
3
|
-
Foam
|
|
3
|
+
`@foam-ai/node` is Foam's OpenTelemetry distribution for Node.js. It configures
|
|
4
|
+
the official OpenTelemetry JavaScript SDK, node automatic instrumentations, and
|
|
5
|
+
OTLP/HTTP exporters with one `init()` call.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- Node.js `^18.19.0 || >=20.6.0`, matching the supported engine range of
|
|
10
|
+
`@opentelemetry/auto-instrumentations-node`
|
|
11
|
+
- A Foam ingest token, stored as `FOAM_OTEL_TOKEN`
|
|
4
12
|
|
|
5
13
|
## Install
|
|
6
14
|
|
|
7
|
-
```
|
|
15
|
+
```sh
|
|
8
16
|
npm install @foam-ai/node
|
|
9
17
|
```
|
|
10
18
|
|
|
11
|
-
##
|
|
19
|
+
## Initialize
|
|
20
|
+
|
|
21
|
+
Call `init()` before any application code loads so OpenTelemetry can patch
|
|
22
|
+
libraries as they import. Don't import the app from the same file: ESM runs
|
|
23
|
+
static imports before `init()`. Set `enabled: false` to disable Foam. Store
|
|
24
|
+
the ingest token as `FOAM_OTEL_TOKEN` and pass `process.env.FOAM_OTEL_TOKEN`;
|
|
25
|
+
do not hardcode it.
|
|
26
|
+
|
|
27
|
+
### TypeScript
|
|
12
28
|
|
|
13
|
-
|
|
29
|
+
Create `instrumentation.ts`:
|
|
14
30
|
|
|
15
|
-
```
|
|
16
|
-
import
|
|
31
|
+
```ts
|
|
32
|
+
import { init } from "@foam-ai/node";
|
|
17
33
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
34
|
+
init({
|
|
35
|
+
name: "checkout-api",
|
|
36
|
+
environment: "production",
|
|
37
|
+
enabled: true,
|
|
38
|
+
token: process.env.FOAM_OTEL_TOKEN,
|
|
39
|
+
version: "1.0.0",
|
|
22
40
|
});
|
|
23
41
|
```
|
|
24
42
|
|
|
25
|
-
|
|
43
|
+
Compile it with the app. If TypeScript emits ESM:
|
|
26
44
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
45
|
+
```sh
|
|
46
|
+
node --import ./dist/instrumentation.js ./dist/app.js
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
If TypeScript emits CommonJS:
|
|
31
50
|
|
|
32
|
-
|
|
51
|
+
```sh
|
|
52
|
+
node --require ./dist/instrumentation.js ./dist/app.js
|
|
53
|
+
```
|
|
33
54
|
|
|
34
|
-
|
|
55
|
+
To run TypeScript without compiling first:
|
|
35
56
|
|
|
36
|
-
|
|
57
|
+
```sh
|
|
58
|
+
node --import tsx --import ./instrumentation.ts ./src/app.ts
|
|
59
|
+
```
|
|
37
60
|
|
|
38
|
-
|
|
39
|
-
import { captureException } from '@foam-ai/node';
|
|
61
|
+
### ESM
|
|
40
62
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return res.status(500).json({ error: 'Payment failed' });
|
|
46
|
-
}
|
|
63
|
+
Create `instrumentation.mjs` with the same `init()` call, then:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
node --import ./instrumentation.mjs ./app.js
|
|
47
67
|
```
|
|
48
68
|
|
|
49
|
-
|
|
69
|
+
### CommonJS
|
|
50
70
|
|
|
51
|
-
|
|
71
|
+
Create `instrumentation.cjs`:
|
|
52
72
|
|
|
53
|
-
|
|
73
|
+
```js
|
|
74
|
+
const { init } = require("@foam-ai/node");
|
|
54
75
|
|
|
55
|
-
|
|
76
|
+
init({
|
|
77
|
+
name: "checkout-api",
|
|
78
|
+
environment: "production",
|
|
79
|
+
enabled: true,
|
|
80
|
+
token: process.env.FOAM_OTEL_TOKEN,
|
|
81
|
+
version: "1.0.0",
|
|
82
|
+
});
|
|
83
|
+
```
|
|
56
84
|
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
|
|
85
|
+
```sh
|
|
86
|
+
node --require ./instrumentation.cjs ./app.js
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Public API
|
|
90
|
+
|
|
91
|
+
### `init(options)`
|
|
60
92
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
93
|
+
Starts Foam: resource, sampler, OTLP exporters, automatic instrumentations,
|
|
94
|
+
and the W3C propagator.
|
|
95
|
+
|
|
96
|
+
Call this once when Foam can own all of one of OTEL slots.
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
import { init } from "@foam-ai/node";
|
|
100
|
+
|
|
101
|
+
init({
|
|
102
|
+
name: "checkout-api",
|
|
103
|
+
environment: "production",
|
|
104
|
+
enabled: true,
|
|
105
|
+
token: process.env.FOAM_OTEL_TOKEN,
|
|
106
|
+
version: "1.0.0",
|
|
66
107
|
});
|
|
67
108
|
```
|
|
68
109
|
|
|
69
|
-
```
|
|
70
|
-
|
|
110
|
+
```ts
|
|
111
|
+
init({
|
|
112
|
+
name,
|
|
113
|
+
environment,
|
|
114
|
+
enabled,
|
|
115
|
+
token,
|
|
116
|
+
version?,
|
|
117
|
+
sampleRate?,
|
|
118
|
+
additionalInstrumentations?,
|
|
119
|
+
additionalSpanProcessors?,
|
|
120
|
+
additionalLogRecordProcessors?,
|
|
121
|
+
additionalMetricReaders?,
|
|
122
|
+
additionalResourceAttributes?,
|
|
123
|
+
disableLogSending?,
|
|
124
|
+
networkCapture?,
|
|
125
|
+
ignoredOutboundHosts?,
|
|
126
|
+
diagnostics?,
|
|
127
|
+
}): void
|
|
71
128
|
```
|
|
72
129
|
|
|
73
|
-
|
|
130
|
+
`name`, `environment`, and `enabled` are required.
|
|
131
|
+
`token` is required only when enabled; store it as `FOAM_OTEL_TOKEN`.
|
|
132
|
+
`sampleRate` accepts values from `0` through `1` and defaults to
|
|
133
|
+
`1`.
|
|
134
|
+
|
|
135
|
+
Foam always installs this sampler rather than reading `OTEL_TRACES_SAMPLER`.
|
|
136
|
+
Diagnostics default to enabled.
|
|
137
|
+
|
|
138
|
+
`additionalResourceAttributes` adds custom resource attributes (for example
|
|
139
|
+
`team` or `cloud.region`) to all telemetry. The Basic API's `name`,
|
|
140
|
+
`environment`, and `version` options take precedence over matching keys.
|
|
141
|
+
Foam also sets `telemetry.sdk.*` from the OpenTelemetry SDK and
|
|
142
|
+
`telemetry.distro.name` / `telemetry.distro.version` for this distribution.
|
|
143
|
+
|
|
144
|
+
Foam detects process, host, and OS attributes from this machine. It does not
|
|
145
|
+
run cloud or container resource detectors (AWS, GCP, Azure, Alibaba, or cgroup
|
|
146
|
+
`container.id`), which call metadata APIs at startup. Set cloud or container
|
|
147
|
+
identity with `additionalResourceAttributes`.
|
|
148
|
+
|
|
149
|
+
`additionalInstrumentations` registers extra OpenTelemetry instrumentations
|
|
150
|
+
alongside Foam's Node auto bundle and `ConsoleInstrumentation`. Foam already
|
|
151
|
+
enables [`@opentelemetry/instrumentation-openai`](https://www.npmjs.com/package/@opentelemetry/instrumentation-openai)
|
|
152
|
+
from that bundle (`openai` `>=4.19 <7`); message content is captured only when
|
|
153
|
+
`networkCapture` is `"advanced"`. Use this option for libraries Foam does not
|
|
154
|
+
ship, such as community Anthropic instrumentations
|
|
155
|
+
([`@traceloop/instrumentation-anthropic`](https://www.npmjs.com/package/@traceloop/instrumentation-anthropic)
|
|
156
|
+
or
|
|
157
|
+
[`@arizeai/openinference-instrumentation-anthropic`](https://www.npmjs.com/package/@arizeai/openinference-instrumentation-anthropic)),
|
|
158
|
+
or to replace a bundled instrumentation by name: last entry wins, and Foam
|
|
159
|
+
disables its own copy. Replacing HTTP or Undici this way also replaces Foam's
|
|
160
|
+
network capture hooks.
|
|
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.
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
import { createFoamIngestSpanProcessor } from "@foam-ai/node";
|
|
172
|
+
|
|
173
|
+
const spanProcessor = createFoamIngestSpanProcessor(
|
|
174
|
+
"checkout-api",
|
|
175
|
+
"production",
|
|
176
|
+
process.env.FOAM_OTEL_TOKEN,
|
|
177
|
+
);
|
|
178
|
+
tracerProvider.addSpanProcessor(spanProcessor);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### `createFoamIngestLogRecordProcessor(name, environment, token)`
|
|
182
|
+
|
|
183
|
+
Returns a log-record processor that exports logs to Foam without taking over
|
|
184
|
+
the global LoggerProvider.
|
|
185
|
+
|
|
186
|
+
Use this when another SDK already owns logs and you want those records in Foam.
|
|
74
187
|
|
|
75
|
-
|
|
188
|
+
```js
|
|
189
|
+
import { createFoamIngestLogRecordProcessor } from "@foam-ai/node";
|
|
76
190
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
serviceName: 'my-nextjs-app',
|
|
84
|
-
isProduction: process.env.NODE_ENV === 'production',
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
}
|
|
191
|
+
const logRecordProcessor = createFoamIngestLogRecordProcessor(
|
|
192
|
+
"checkout-api",
|
|
193
|
+
"production",
|
|
194
|
+
process.env.FOAM_OTEL_TOKEN,
|
|
195
|
+
);
|
|
196
|
+
loggerProvider.addLogRecordProcessor(logRecordProcessor);
|
|
88
197
|
```
|
|
89
198
|
|
|
90
|
-
|
|
199
|
+
### `createFoamIngestMetricReader(name, environment, token)`
|
|
91
200
|
|
|
92
|
-
|
|
201
|
+
Returns a metric reader that exports metrics to Foam without taking over the
|
|
202
|
+
global MeterProvider.
|
|
93
203
|
|
|
94
|
-
|
|
204
|
+
Use this when another SDK already owns metrics and you want those meters in
|
|
205
|
+
Foam.
|
|
95
206
|
|
|
96
|
-
|
|
207
|
+
```js
|
|
208
|
+
import { createFoamIngestMetricReader } from "@foam-ai/node";
|
|
97
209
|
|
|
98
|
-
|
|
99
|
-
-
|
|
100
|
-
|
|
101
|
-
|
|
210
|
+
const metricReader = createFoamIngestMetricReader(
|
|
211
|
+
"checkout-api",
|
|
212
|
+
"production",
|
|
213
|
+
process.env.FOAM_OTEL_TOKEN,
|
|
214
|
+
);
|
|
215
|
+
meterProvider.addMetricReader(metricReader);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The factories do not claim global OTel providers. `name`, `environment`, and
|
|
219
|
+
`token` are required; Foam stamps `service.name`,
|
|
220
|
+
`deployment.environment.name`, and `foam.ingest.tier=external` onto the
|
|
221
|
+
exported resource. If the other SDK already set `service.name` or environment,
|
|
222
|
+
Foam's values overwrite them on the copy sent to Foam only. The original
|
|
223
|
+
spans, logs, and metrics are not mutated.
|
|
224
|
+
|
|
225
|
+
Use `init()` or ingest, not both on the same signal. If another SDK already
|
|
226
|
+
owns traces (or another slot), call `init()` for the free signals and attach
|
|
227
|
+
ingest only to the provider you do not own. Do not pass ingest factories as
|
|
228
|
+
`additionalSpanProcessors` / readers: `init()` already installs Foam exporters.
|
|
229
|
+
|
|
230
|
+
The global propagator is also first-wins. OpenTelemetry allows only one, and
|
|
231
|
+
HTTP, Express, Undici, and Fetch all call it to copy trace headers onto
|
|
232
|
+
requests. Foam registers W3C Trace Context and W3C Baggage
|
|
233
|
+
(`traceparent`, `tracestate`, `baggage`) and nothing else: not B3, Jaeger, or
|
|
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.
|
|
242
|
+
|
|
243
|
+
### `injectTraceContext(headers)`
|
|
244
|
+
|
|
245
|
+
Writes W3C `traceparent`, `tracestate`, and `baggage` onto a header map.
|
|
246
|
+
|
|
247
|
+
Use this when you send a message yourself (Kafka, a queue, a custom socket).
|
|
248
|
+
Auto-instrumented HTTP, Express, Undici, and Fetch already inject these
|
|
249
|
+
headers; do not call this on ordinary HTTP. This path uses Foam's local W3C
|
|
250
|
+
propagator even if Foam lost the global slot.
|
|
251
|
+
|
|
252
|
+
```js
|
|
253
|
+
const headers = {};
|
|
254
|
+
injectTraceContext(headers);
|
|
255
|
+
await kafka.send({ value: payload, headers });
|
|
256
|
+
```
|
|
102
257
|
|
|
103
|
-
|
|
258
|
+
### `extractTraceContext(headers)`
|
|
104
259
|
|
|
105
|
-
|
|
260
|
+
Reads W3C trace and baggage headers and returns a parent OpenTelemetry context.
|
|
261
|
+
|
|
262
|
+
Use this when you receive a message on a custom transport so the work continues
|
|
263
|
+
the same trace. Ignoring those headers starts a new one.
|
|
264
|
+
|
|
265
|
+
```js
|
|
266
|
+
import { context } from "@opentelemetry/api";
|
|
267
|
+
|
|
268
|
+
const parent = extractTraceContext(message.headers);
|
|
269
|
+
await context.with(parent, () => handle(message));
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### `setBaggage(key, value)`
|
|
273
|
+
|
|
274
|
+
Stores a small string on the current request context.
|
|
275
|
+
|
|
276
|
+
Use this for request-scoped identity (tenant, user id) that should follow the
|
|
277
|
+
request across `await`s without being passed through every function. Values do
|
|
278
|
+
not leak to other concurrent requests. Auto-instrumented HTTP does not send
|
|
279
|
+
these keys until you call `injectTraceContext`.
|
|
280
|
+
|
|
281
|
+
```js
|
|
282
|
+
setBaggage("tenant.id", "acme");
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### `getBaggage(key)`
|
|
286
|
+
|
|
287
|
+
Returns a baggage value from the current request, or `undefined`.
|
|
106
288
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
289
|
+
Use this later in the same request to read a value set with `setBaggage`, or a
|
|
290
|
+
key that arrived on an inbound `baggage` header.
|
|
291
|
+
|
|
292
|
+
```js
|
|
293
|
+
const tenant = getBaggage("tenant.id"); // "acme"
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### `incrementCounter(name, value?, attributes?, options?)`
|
|
297
|
+
|
|
298
|
+
Adds to a counter that only increases.
|
|
299
|
+
|
|
300
|
+
Use this for event counts: orders created, jobs finished, errors.
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
incrementCounter("orders.created", 1, { "cloud.region": "us-west-1" });
|
|
304
|
+
```
|
|
113
305
|
|
|
114
|
-
### `
|
|
306
|
+
### `recordHistogram(name, value, attributes?, options?)`
|
|
115
307
|
|
|
116
|
-
|
|
308
|
+
Records a measurement in a distribution.
|
|
117
309
|
|
|
118
|
-
|
|
310
|
+
Use this for values you want percentiles or averages of: latency, duration,
|
|
311
|
+
payload size.
|
|
312
|
+
|
|
313
|
+
```js
|
|
314
|
+
recordHistogram("checkout.duration", 0.142, undefined, { unit: "s" });
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### `addUpDownCounter(name, value, attributes?, options?)`
|
|
318
|
+
|
|
319
|
+
Adds a signed delta to a counter that can go up or down.
|
|
320
|
+
|
|
321
|
+
Use this for occupancy: active jobs, open connections, items in a pool.
|
|
322
|
+
|
|
323
|
+
```js
|
|
324
|
+
addUpDownCounter("jobs.active", -1);
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### `setMetric(name, value, attributes?, options?)`
|
|
328
|
+
|
|
329
|
+
Sets the current value of a gauge.
|
|
330
|
+
|
|
331
|
+
Use this for a point-in-time level: queue depth, heap used, cache size.
|
|
332
|
+
|
|
333
|
+
```js
|
|
334
|
+
setMetric("queue.depth", 27);
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Each metric helper caches instruments by kind and name. `unit` and
|
|
338
|
+
`description` therefore apply on first use. Observable (pull) instruments are
|
|
339
|
+
not part of this API; use `@opentelemetry/api` or automatic instrumentation
|
|
340
|
+
when a value should be sampled on export rather than recorded on an event.
|
|
341
|
+
|
|
342
|
+
### `getState()`
|
|
343
|
+
|
|
344
|
+
Returns whether Foam initialized, which instrumentations registered, and which
|
|
345
|
+
signals have an export path.
|
|
346
|
+
|
|
347
|
+
Use this in health checks, tests, or diagnostics to confirm Foam is running.
|
|
348
|
+
|
|
349
|
+
```js
|
|
350
|
+
import { getState } from "@foam-ai/node";
|
|
351
|
+
|
|
352
|
+
getState();
|
|
353
|
+
// {
|
|
354
|
+
// initialized: true,
|
|
355
|
+
// instrumentations: ["http", "express", "pg", ...],
|
|
356
|
+
// signals: {
|
|
357
|
+
// traces: true,
|
|
358
|
+
// metrics: true,
|
|
359
|
+
// logs: true,
|
|
360
|
+
// baggage: true,
|
|
361
|
+
// profile: false,
|
|
362
|
+
// },
|
|
363
|
+
// }
|
|
364
|
+
```
|
|
119
365
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
366
|
+
`instrumentations` contains only the instrumentations registered with a
|
|
367
|
+
successfully started Foam SDK, after upstream defaults and
|
|
368
|
+
`OTEL_NODE_ENABLED_INSTRUMENTATIONS` /
|
|
369
|
+
`OTEL_NODE_DISABLED_INSTRUMENTATIONS` filtering. Registration means an
|
|
370
|
+
instrumentation is ready to patch a supported module when that module loads; it
|
|
371
|
+
does not prove that the module has loaded or emitted spans.
|
|
372
|
+
|
|
373
|
+
Each `signals` boolean means Foam successfully constructed an export path for
|
|
374
|
+
that signal. This can be a Foam-owned provider from `init()` or a
|
|
375
|
+
processor/reader returned by an ingest factory. For ingest factories, Foam
|
|
376
|
+
cannot prove that the caller subsequently registered the returned object. The
|
|
377
|
+
boolean therefore means telemetry is expected, not that the application has
|
|
378
|
+
already produced or successfully exported it.
|
|
379
|
+
|
|
380
|
+
## Network capture
|
|
381
|
+
|
|
382
|
+
`networkCapture` defaults to `"basic"` and controls HTTP enrichment: `"off"`
|
|
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.
|
|
431
|
+
|
|
432
|
+
## OpenTelemetry configuration and compliance
|
|
433
|
+
|
|
434
|
+
Foam composes the official OpenTelemetry JavaScript API, SDK, resource,
|
|
435
|
+
propagation, instrumentation, and OTLP exporter packages. It does not fork
|
|
436
|
+
their data model or redefine spans, metrics, logs, resources, or context.
|
|
437
|
+
|
|
438
|
+
Foam does not use any of environment configuration. So they remain available, including:
|
|
439
|
+
|
|
440
|
+
- `OTEL_RESOURCE_ATTRIBUTES` (the Basic API's required service name and
|
|
441
|
+
environment take precedence for those resource attributes)
|
|
442
|
+
- `OTEL_NODE_ENABLED_INSTRUMENTATIONS` and
|
|
443
|
+
`OTEL_NODE_DISABLED_INSTRUMENTATIONS`
|
|
444
|
+
- `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT`
|
|
445
|
+
- `OTEL_TRACES_SAMPLER`
|
|
446
|
+
|
|
447
|
+
The [OpenTelemetry JavaScript compliance matrix](https://github.com/open-telemetry/opentelemetry-specification/blob/main/spec-compliance-matrix/js.yaml)
|
|
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 `?`.
|
|
457
|
+
|
|
458
|
+
## TODO(pcga11): OpenTelemetry profiling
|
|
459
|
+
|
|
460
|
+
Profiling is not supported: the official OpenTelemetry JavaScript SDK has no
|
|
461
|
+
in-process Profiles provider, processor, or exporter yet.
|
|
462
|
+
|
|
463
|
+
Add profiling and report `getState().signals.profile: true` after that ships.
|
|
464
|
+
|
|
465
|
+
Track upstream progress:
|
|
466
|
+
|
|
467
|
+
- [OpenTelemetry JS profiling implementation issue](https://github.com/open-telemetry/opentelemetry-js/issues/6500)
|
|
468
|
+
- [Profiling SIG language SDK support tracker](https://github.com/open-telemetry/sig-profiling/issues/106)
|
|
469
|
+
- [OpenTelemetry Profiles public alpha announcement](https://opentelemetry.io/blog/2026/profiles-alpha/)
|
|
470
|
+
- [OpenTelemetry eBPF profiler](https://github.com/open-telemetry/opentelemetry-ebpf-profiler)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export declare const FOAM_ENDPOINT = "https://otel.api.foam.ai";
|
|
2
|
+
export declare const FOAM_IDENTIFIER_NAME = "foam-otel";
|
|
3
|
+
export declare const FOAM_DISTRO_NAME = "@foam-ai/node";
|
|
4
|
+
export declare const FOAM_DISTRO_VERSION = "0.1.0-alpha.4";
|
|
5
|
+
export declare const FOAM_INGEST_TIER = "foam.ingest.tier";
|
|
6
|
+
export declare const FOAM_OTLP_TRACES_PATH = "/v1/traces";
|
|
7
|
+
export declare const FOAM_OTLP_LOGS_PATH = "/v1/logs";
|
|
8
|
+
export declare const FOAM_OTLP_METRICS_PATH = "/v1/metrics";
|
|
9
|
+
export declare const NETWORK_BODY_CHUNK_BYTES: number;
|
|
10
|
+
export declare const NETWORK_BODY_MAX_CAPTURE_BYTES: number;
|
|
11
|
+
export declare const NETWORK_CAPTURE_EVENT = "foam.http.body.chunk";
|
|
12
|
+
export declare const NETWORK_CAPTURE_EVENT_BODY = "HTTP body chunk";
|
|
13
|
+
export declare const NETWORK_CAPTURE_DEADLINE_MS = 60000;
|
|
14
|
+
export declare const MAX_ACTIVE_CAPTURES = 128;
|
|
15
|
+
export declare const ATTR_HTTP_REQUEST_BODY_SIZE = "http.request.body.size";
|
|
16
|
+
export declare const ATTR_HTTP_RESPONSE_BODY_SIZE = "http.response.body.size";
|
|
17
|
+
export declare const ATTR_HTTP_REQUEST_BODY_CONTENT = "http.request.body.content";
|
|
18
|
+
export declare const ATTR_HTTP_RESPONSE_BODY_CONTENT = "http.response.body.content";
|
|
19
|
+
export declare const ATTR_FOAM_HTTP_BODY_ID = "foam.http.body.id";
|
|
20
|
+
export declare const ATTR_FOAM_HTTP_BODY_SIDE = "foam.http.body.side";
|
|
21
|
+
export declare const ATTR_FOAM_HTTP_BODY_DIRECTION = "foam.http.body.direction";
|
|
22
|
+
export declare const ATTR_FOAM_HTTP_BODY_COMPLETE = "foam.http.body.complete";
|
|
23
|
+
export declare const ATTR_FOAM_HTTP_BODY_TRUNCATED = "foam.http.body.truncated";
|
|
24
|
+
export declare const ATTR_FOAM_HTTP_BODY_OUTCOME = "foam.http.body.outcome";
|
|
25
|
+
export declare const ATTR_FOAM_HTTP_BODY_CAPTURED_BYTES = "foam.http.body.captured_bytes";
|
|
26
|
+
export declare const ATTR_FOAM_HTTP_BODY_CHUNK_COUNT = "foam.http.body.chunk.count";
|
|
27
|
+
export declare const ATTR_FOAM_HTTP_BODY_CHUNK_INDEX = "foam.http.body.chunk.index";
|
|
28
|
+
export declare const ATTR_FOAM_HTTP_BODY_CHUNK_BYTES = "foam.http.body.chunk.bytes";
|
|
29
|
+
export declare const ATTR_FOAM_HTTP_BODY_CHUNK_CONTENT = "foam.http.body.chunk.content";
|
|
30
|
+
export declare const ATTR_FOAM_HTTP_REQUEST_BODY_CAPTURE_ID = "foam.http.request.body.capture_id";
|
|
31
|
+
export declare const ATTR_FOAM_HTTP_RESPONSE_BODY_CAPTURE_ID = "foam.http.response.body.capture_id";
|
|
32
|
+
export declare const ATTR_FOAM_HTTP_REQUEST_BODY_COMPLETE = "foam.http.request.body.complete";
|
|
33
|
+
export declare const ATTR_FOAM_HTTP_RESPONSE_BODY_COMPLETE = "foam.http.response.body.complete";
|
|
34
|
+
export declare const ATTR_FOAM_HTTP_REQUEST_BODY_TRUNCATED = "foam.http.request.body.truncated";
|
|
35
|
+
export declare const ATTR_FOAM_HTTP_RESPONSE_BODY_TRUNCATED = "foam.http.response.body.truncated";
|
|
36
|
+
export declare const SAFE_NETWORK_HEADERS: readonly ["content-type", "content-length", "content-encoding"];
|
|
37
|
+
export declare const DIAG_LOG_LEVEL = "OTEL_LOG_LEVEL";
|
|
38
|
+
export declare const SEVERITY_TEXT: {
|
|
39
|
+
readonly 9: "INFO";
|
|
40
|
+
readonly 13: "WARN";
|
|
41
|
+
readonly 17: "ERROR";
|
|
42
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SEVERITY_TEXT = exports.DIAG_LOG_LEVEL = exports.SAFE_NETWORK_HEADERS = exports.ATTR_FOAM_HTTP_RESPONSE_BODY_TRUNCATED = exports.ATTR_FOAM_HTTP_REQUEST_BODY_TRUNCATED = exports.ATTR_FOAM_HTTP_RESPONSE_BODY_COMPLETE = exports.ATTR_FOAM_HTTP_REQUEST_BODY_COMPLETE = exports.ATTR_FOAM_HTTP_RESPONSE_BODY_CAPTURE_ID = exports.ATTR_FOAM_HTTP_REQUEST_BODY_CAPTURE_ID = exports.ATTR_FOAM_HTTP_BODY_CHUNK_CONTENT = exports.ATTR_FOAM_HTTP_BODY_CHUNK_BYTES = exports.ATTR_FOAM_HTTP_BODY_CHUNK_INDEX = exports.ATTR_FOAM_HTTP_BODY_CHUNK_COUNT = exports.ATTR_FOAM_HTTP_BODY_CAPTURED_BYTES = exports.ATTR_FOAM_HTTP_BODY_OUTCOME = exports.ATTR_FOAM_HTTP_BODY_TRUNCATED = exports.ATTR_FOAM_HTTP_BODY_COMPLETE = exports.ATTR_FOAM_HTTP_BODY_DIRECTION = exports.ATTR_FOAM_HTTP_BODY_SIDE = exports.ATTR_FOAM_HTTP_BODY_ID = exports.ATTR_HTTP_RESPONSE_BODY_CONTENT = exports.ATTR_HTTP_REQUEST_BODY_CONTENT = exports.ATTR_HTTP_RESPONSE_BODY_SIZE = exports.ATTR_HTTP_REQUEST_BODY_SIZE = exports.MAX_ACTIVE_CAPTURES = exports.NETWORK_CAPTURE_DEADLINE_MS = exports.NETWORK_CAPTURE_EVENT_BODY = exports.NETWORK_CAPTURE_EVENT = exports.NETWORK_BODY_MAX_CAPTURE_BYTES = exports.NETWORK_BODY_CHUNK_BYTES = exports.FOAM_OTLP_METRICS_PATH = exports.FOAM_OTLP_LOGS_PATH = exports.FOAM_OTLP_TRACES_PATH = exports.FOAM_INGEST_TIER = exports.FOAM_DISTRO_VERSION = exports.FOAM_DISTRO_NAME = exports.FOAM_IDENTIFIER_NAME = exports.FOAM_ENDPOINT = void 0;
|
|
4
|
+
const api_logs_1 = require("@opentelemetry/api-logs");
|
|
5
|
+
exports.FOAM_ENDPOINT = "https://otel.api.foam.ai";
|
|
6
|
+
exports.FOAM_IDENTIFIER_NAME = "foam-otel";
|
|
7
|
+
exports.FOAM_DISTRO_NAME = "@foam-ai/node";
|
|
8
|
+
exports.FOAM_DISTRO_VERSION = "0.1.0-alpha.4";
|
|
9
|
+
exports.FOAM_INGEST_TIER = "foam.ingest.tier";
|
|
10
|
+
exports.FOAM_OTLP_TRACES_PATH = "/v1/traces";
|
|
11
|
+
exports.FOAM_OTLP_LOGS_PATH = "/v1/logs";
|
|
12
|
+
exports.FOAM_OTLP_METRICS_PATH = "/v1/metrics";
|
|
13
|
+
exports.NETWORK_BODY_CHUNK_BYTES = 64 * 1024; // 64Kib
|
|
14
|
+
exports.NETWORK_BODY_MAX_CAPTURE_BYTES = 1024 * 1024; // 1Mib
|
|
15
|
+
exports.NETWORK_CAPTURE_EVENT = "foam.http.body.chunk";
|
|
16
|
+
exports.NETWORK_CAPTURE_EVENT_BODY = "HTTP body chunk";
|
|
17
|
+
exports.NETWORK_CAPTURE_DEADLINE_MS = 60_000;
|
|
18
|
+
exports.MAX_ACTIVE_CAPTURES = 128;
|
|
19
|
+
// pcga11: Do not change these.
|
|
20
|
+
// Spec names copied from OpenTelemetry HTTP semantic conventions. 1.43.0 does
|
|
21
|
+
// not export BODY_CONTENT, and OTel JS recommends copying unstable names
|
|
22
|
+
// instead of importing `@opentelemetry/semantic-conventions/incubating`.
|
|
23
|
+
exports.ATTR_HTTP_REQUEST_BODY_SIZE = "http.request.body.size";
|
|
24
|
+
exports.ATTR_HTTP_RESPONSE_BODY_SIZE = "http.response.body.size";
|
|
25
|
+
exports.ATTR_HTTP_REQUEST_BODY_CONTENT = "http.request.body.content";
|
|
26
|
+
exports.ATTR_HTTP_RESPONSE_BODY_CONTENT = "http.response.body.content";
|
|
27
|
+
exports.ATTR_FOAM_HTTP_BODY_ID = "foam.http.body.id";
|
|
28
|
+
exports.ATTR_FOAM_HTTP_BODY_SIDE = "foam.http.body.side";
|
|
29
|
+
exports.ATTR_FOAM_HTTP_BODY_DIRECTION = "foam.http.body.direction";
|
|
30
|
+
exports.ATTR_FOAM_HTTP_BODY_COMPLETE = "foam.http.body.complete";
|
|
31
|
+
exports.ATTR_FOAM_HTTP_BODY_TRUNCATED = "foam.http.body.truncated";
|
|
32
|
+
exports.ATTR_FOAM_HTTP_BODY_OUTCOME = "foam.http.body.outcome";
|
|
33
|
+
exports.ATTR_FOAM_HTTP_BODY_CAPTURED_BYTES = "foam.http.body.captured_bytes";
|
|
34
|
+
exports.ATTR_FOAM_HTTP_BODY_CHUNK_COUNT = "foam.http.body.chunk.count";
|
|
35
|
+
exports.ATTR_FOAM_HTTP_BODY_CHUNK_INDEX = "foam.http.body.chunk.index";
|
|
36
|
+
exports.ATTR_FOAM_HTTP_BODY_CHUNK_BYTES = "foam.http.body.chunk.bytes";
|
|
37
|
+
exports.ATTR_FOAM_HTTP_BODY_CHUNK_CONTENT = "foam.http.body.chunk.content";
|
|
38
|
+
exports.ATTR_FOAM_HTTP_REQUEST_BODY_CAPTURE_ID = "foam.http.request.body.capture_id";
|
|
39
|
+
exports.ATTR_FOAM_HTTP_RESPONSE_BODY_CAPTURE_ID = "foam.http.response.body.capture_id";
|
|
40
|
+
exports.ATTR_FOAM_HTTP_REQUEST_BODY_COMPLETE = "foam.http.request.body.complete";
|
|
41
|
+
exports.ATTR_FOAM_HTTP_RESPONSE_BODY_COMPLETE = "foam.http.response.body.complete";
|
|
42
|
+
exports.ATTR_FOAM_HTTP_REQUEST_BODY_TRUNCATED = "foam.http.request.body.truncated";
|
|
43
|
+
exports.ATTR_FOAM_HTTP_RESPONSE_BODY_TRUNCATED = "foam.http.response.body.truncated";
|
|
44
|
+
exports.SAFE_NETWORK_HEADERS = [
|
|
45
|
+
"content-type",
|
|
46
|
+
"content-length",
|
|
47
|
+
"content-encoding",
|
|
48
|
+
];
|
|
49
|
+
exports.DIAG_LOG_LEVEL = "OTEL_LOG_LEVEL";
|
|
50
|
+
exports.SEVERITY_TEXT = {
|
|
51
|
+
[api_logs_1.SeverityNumber.INFO]: "INFO",
|
|
52
|
+
[api_logs_1.SeverityNumber.WARN]: "WARN",
|
|
53
|
+
[api_logs_1.SeverityNumber.ERROR]: "ERROR",
|
|
54
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
2
|
+
export declare function configureDiagnostics(): void;
|
|
3
|
+
export declare const info: (message: string) => void;
|
|
4
|
+
export declare const warn: (message: string) => void;
|
|
5
|
+
export declare const error: (message: string) => void;
|
|
6
|
+
export declare function sendStateDiagnosticLog({ name, environment, token, tier, severity, error, }: {
|
|
7
|
+
name: string;
|
|
8
|
+
environment: string;
|
|
9
|
+
token: string;
|
|
10
|
+
tier: "internal" | "external";
|
|
11
|
+
severity: SeverityNumber;
|
|
12
|
+
error?: string;
|
|
13
|
+
}): Promise<void>;
|