@omob/otel-kit 0.1.0 → 0.1.1
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 +61 -255
- package/dist/telemetry.types.d.ts +2 -1
- package/dist/utils/otlp-options.d.ts +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -20,45 +20,25 @@ Those are the three signals of observability, and they answer different question
|
|
|
20
20
|
|
|
21
21
|
Tracing is the one that connects the other two, and it is what this package is mostly for. You pick where traces, metrics and logs go; it handles the SDK, the sampling, the shutdown flush, and the boilerplate around spans — and stamps `trace_id` into your logs so the third column lines up with the second.
|
|
22
22
|
|
|
23
|
-
## The words, briefly
|
|
24
|
-
|
|
25
|
-
If you're new to OpenTelemetry, this is everything you need to read the rest of this page.
|
|
26
|
-
|
|
27
|
-
A **trace** is the story of one request, start to finish. A **span** is one timed step inside that story. Spans nest, so a trace is a tree:
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
GET /login 240ms ← the trace starts here
|
|
31
|
-
├─ user.lookup 180ms
|
|
32
|
-
│ └─ mongodb.find 175ms ← this one you get for free
|
|
33
|
-
└─ token.generate 12ms
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Each span carries a name, a start and end time, a status (did it fail), and **attributes** — key/value labels like `user.id` or `http.method`. Every span in that tree shares one **trace ID**, which is how the tree gets reassembled at the other end.
|
|
37
|
-
|
|
38
|
-
You get most spans for free. **Instrumentation** is code that wraps common libraries — HTTP, Mongo, Postgres, Redis — and opens a span whenever they're used. You never call it. `withSpan` is for the steps only you know are worth timing, like the two named above.
|
|
39
|
-
|
|
40
|
-
**Sampling** is deciding what to keep. Tracing every request at scale is expensive, so `sampleRatio: 0.1` keeps a tenth. The choice is made once at the root and the whole tree follows it, so you never get half a trace.
|
|
41
|
-
|
|
42
|
-
**Propagation** is how a trace survives a network hop. Your service puts the trace ID in an outgoing header; the next service reads it and continues the same trace instead of starting a new one. Both sides must agree on the header format — that's what `propagators` configures.
|
|
43
|
-
|
|
44
|
-
An **exporter** is where the finished data is sent: your collector, Jaeger, Google Cloud, or the console. A **resource** is the facts about the service itself — name, version, environment — stamped on everything you send.
|
|
45
|
-
|
|
46
|
-
Traces answer "what happened in this one request". **Metrics** are numbers over time ("requests per second"), and **logs** are the text lines you already write. This package can send all three; most people start with traces alone.
|
|
47
|
-
|
|
48
23
|
## Install
|
|
49
24
|
|
|
50
25
|
```bash
|
|
51
26
|
npm install @omob/otel-kit @opentelemetry/api
|
|
52
27
|
```
|
|
53
28
|
|
|
54
|
-
|
|
29
|
+
That is everything for most setups. OTLP — protobuf, JSON and gRPC — and Prometheus are already included.
|
|
55
30
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
31
|
+
Extra packages are needed for Google Cloud only, and which one depends on the route you take:
|
|
32
|
+
|
|
33
|
+
| If you use | Install |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| `ExporterType.GCP` for **traces** | `@google-cloud/opentelemetry-cloud-trace-exporter` |
|
|
36
|
+
| `ExporterType.GCP` for **metrics** | `@google-cloud/opentelemetry-cloud-monitoring-exporter` |
|
|
37
|
+
| Google Cloud over **OTLP** | `google-auth-library` — and neither of the above |
|
|
60
38
|
|
|
61
|
-
|
|
39
|
+
They are independent: exporting traces to Google needs the trace package only. Google is deprecating both in favour of the OTLP route, which is covered under [recipes](https://github.com/omob/otel-kit/blob/main/docs/recipes.md).
|
|
40
|
+
|
|
41
|
+
If you pick an exporter whose package is not installed, startup fails and names the package.
|
|
62
42
|
|
|
63
43
|
## Quick start
|
|
64
44
|
|
|
@@ -74,14 +54,38 @@ Telemetry.start({
|
|
|
74
54
|
environment: process.env.NODE_ENV,
|
|
75
55
|
enabled: process.env.NODE_ENV !== "test",
|
|
76
56
|
traces: {
|
|
77
|
-
exporter: ExporterType.
|
|
78
|
-
otlp: { url: "http://localhost:4318/v1/traces" },
|
|
57
|
+
exporter: ExporterType.CONSOLE,
|
|
79
58
|
sampleRatio: Number(process.env.OTEL_TRACES_SAMPLE_RATIO ?? 1),
|
|
80
59
|
},
|
|
60
|
+
|
|
61
|
+
// metrics and logs stay off until you add their block. Uncomment to turn them on —
|
|
62
|
+
// you get HTTP latency, event loop and heap metrics, and your existing pino or winston
|
|
63
|
+
// output bridged with its trace id, without writing any instrumentation yourself.
|
|
64
|
+
// metrics: { exporter: ExporterType.OTLP, otlp: { url: process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT } },
|
|
65
|
+
// logs: { exporter: ExporterType.OTLP, otlp: { url: process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT } },
|
|
66
|
+
|
|
81
67
|
instrumentation: { ignoreIncomingPaths: ["/health"] },
|
|
82
68
|
});
|
|
83
69
|
```
|
|
84
70
|
|
|
71
|
+
`CONSOLE` needs no infrastructure — spans print to stdout, so you can confirm tracing works before you have anywhere to send it. Swap it for a real destination once you do:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
traces: {
|
|
75
|
+
exporter: ExporterType.OTLP,
|
|
76
|
+
otlp: { url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT },
|
|
77
|
+
sampleRatio: Number(process.env.OTEL_TRACES_SAMPLE_RATIO ?? 1),
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Nothing listens on port 4318 unless you run something there.** If you point at a collector that is not up, every export fails with `ECONNREFUSED` and you see no error at all — OpenTelemetry's internal logging is off by default. The quickest real destination is Jaeger, which ingests OTLP directly:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
docker run -d --name jaeger -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one:1.62.0
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Then set `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces` and open the UI at http://localhost:16686.
|
|
88
|
+
|
|
85
89
|
Load it before your app:
|
|
86
90
|
|
|
87
91
|
```json
|
|
@@ -98,7 +102,7 @@ Keep the ratio at 1 while you are setting things up. Sampling below 1 is a produ
|
|
|
98
102
|
instrumentation: { enable: [InstrumentationName.FASTIFY], ignoreIncomingPaths: ["/health"] }
|
|
99
103
|
```
|
|
100
104
|
|
|
101
|
-
Without it, every request is one bare `GET` span with no `http.route`, so nothing groups by route. Express, Koa, Hapi, NestJS, Mongo, Postgres, Redis, Kafka and outbound HTTP need no such step — they are on by default. The wrinkle: the bundled Fastify instrumentation is deprecated upstream, which is *why* it is disabled. It works, and the alternative is covered under [
|
|
105
|
+
Without it, every request is one bare `GET` span with no `http.route`, so nothing groups by route. Express, Koa, Hapi, NestJS, Mongo, Postgres, Redis, Kafka and outbound HTTP need no such step — they are on by default. The wrinkle: the bundled Fastify instrumentation is deprecated upstream, which is *why* it is disabled. It works, and the alternative is covered under [recipes](https://github.com/omob/otel-kit/blob/main/docs/recipes.md).
|
|
102
106
|
|
|
103
107
|
### Why `--require`
|
|
104
108
|
|
|
@@ -152,247 +156,49 @@ fastify.setErrorHandler((err, request, reply) =>
|
|
|
152
156
|
);
|
|
153
157
|
```
|
|
154
158
|
|
|
155
|
-
##
|
|
156
|
-
|
|
157
|
-
Spans sit in a batch buffer for up to five seconds, so a process that exits without flushing loses them — on every deploy, which is exactly when you want them.
|
|
158
|
-
|
|
159
|
-
By default the package listens for SIGTERM and SIGINT, flushes, and then **hands the signal back**: your own handlers still run, and if there are none the process terminates with the conventional exit code (143 for SIGTERM, 130 for SIGINT). It never calls `process.exit` itself unless you ask it to with `exitOnSignal: true`.
|
|
160
|
-
|
|
161
|
-
If your app already drains connections, own the order yourself — close the server first so no new spans are created, then flush:
|
|
162
|
-
|
|
163
|
-
```ts
|
|
164
|
-
Telemetry.start({ ..., handleShutdownSignals: false });
|
|
165
|
-
|
|
166
|
-
const drain = async (code: number) => {
|
|
167
|
-
await app.close();
|
|
168
|
-
await Telemetry.shutdown();
|
|
169
|
-
process.exit(code);
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
process.on("SIGTERM", () => drain(143));
|
|
173
|
-
process.on("SIGINT", () => drain(130));
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
Do not leave `handleShutdownSignals` on *and* call `Telemetry.shutdown()` from your own handler — one flush runs, both callers await it, but the ordering of your drain is no longer guaranteed.
|
|
177
|
-
|
|
178
|
-
## Turning things off
|
|
179
|
-
|
|
180
|
-
Every signal is optional and off by default. Omit what you don't want:
|
|
181
|
-
|
|
182
|
-
```ts
|
|
183
|
-
Telemetry.start({
|
|
184
|
-
serviceName: "my-service",
|
|
185
|
-
traces: { exporter: ExporterType.OTLP, otlp: { url } },
|
|
186
|
-
// no metrics block, no logs block — nothing is created for them
|
|
187
|
-
});
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
To disable everything at once, set `enabled: false`. `Telemetry.start` becomes a no-op, no SDK is loaded. Use it for tests.
|
|
191
|
-
|
|
192
|
-
## When configuration is rejected
|
|
193
|
-
|
|
194
|
-
A telemetry mistake should not stop your service from serving traffic. If the configuration is rejected, `Telemetry.start` does **not** throw: it logs, leaves telemetry off, and lets your app boot. Pass `onStartupError` to route that into your own logger or alerting:
|
|
195
|
-
|
|
196
|
-
```ts
|
|
197
|
-
Telemetry.start({ ..., onStartupError: (error) => logger.error({ error }, "telemetry disabled") });
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
Pass a handler that rethrows if you would rather fail the boot.
|
|
201
|
-
|
|
202
|
-
## Options
|
|
203
|
-
|
|
204
|
-
Only `serviceName` is required. Everything else has a working default.
|
|
205
|
-
|
|
206
|
-
**The service**
|
|
207
|
-
|
|
208
|
-
| Option | Default | What it does |
|
|
209
|
-
| --- | --- | --- |
|
|
210
|
-
| `serviceName` | **required** | Name your service appears under. |
|
|
211
|
-
| `serviceVersion` | — | Shows on every span as `service.version`. |
|
|
212
|
-
| `environment` | — | Shows as `deployment.environment.name`. |
|
|
213
|
-
| `enabled` | `true` | `false` turns everything off and loads no SDK. |
|
|
214
|
-
| `resourceAttributes` | `{}` | Extra attributes on every span, metric and log. |
|
|
215
|
-
| `resourceDetection` | `true` | Auto-detects host and process attributes. Stamps `process.command_args` — your argv — on everything, so set `false` if you pass secrets as flags. |
|
|
216
|
-
|
|
217
|
-
**Traces**
|
|
218
|
-
|
|
219
|
-
| Option | Default | What it does |
|
|
220
|
-
| --- | --- | --- |
|
|
221
|
-
| `traces.exporter` | `none` | `none` · `console` · `otlp` · `gcp` |
|
|
222
|
-
| `traces.sampleRatio` | keep all | `0.1` keeps 10%. Children follow their parent's decision. |
|
|
223
|
-
| `traces.sampler` | — | A sampler of your own. Takes precedence over `sampleRatio`. |
|
|
224
|
-
| `traces.otlp.url` | — | Collector endpoint. Also takes `headers` and `timeoutMillis`. |
|
|
225
|
-
| `traces.otlp.protocol` | `http/protobuf` | `http/protobuf` · `http/json` · `grpc` |
|
|
226
|
-
| `traces.gcp.projectId` | `$GCP_PROJECT_ID` | Also takes `keyFile`; falls back to application default credentials. |
|
|
227
|
-
| `traces.batch` | SDK defaults | `maxQueueSize`, `maxExportBatchSize`, `scheduledDelayMillis`, `exportTimeoutMillis`. Raise the queue if you drop spans under load. |
|
|
228
|
-
| `traces.additionalProcessors` | `[]` | Extra span processors — scrub attributes, enrich spans, or dual-write to a second collector. |
|
|
229
|
-
| `traces.sanitizeAttributes` | `true` | Drops `NaN` and `Infinity` attribute values, which some backends cannot represent. |
|
|
230
|
-
| `spanLimits.attributeValueLengthLimit` | `4096` | Caps attribute size so one oversized request can't produce an unbounded span. |
|
|
231
|
-
|
|
232
|
-
**Metrics and logs**
|
|
233
|
-
|
|
234
|
-
| Option | Default | What it does |
|
|
235
|
-
| --- | --- | --- |
|
|
236
|
-
| `metrics.exporter` | `none` | `none` · `console` · `otlp` · `gcp` · `prometheus` |
|
|
237
|
-
| `metrics.exportIntervalMillis` | `60000` | How often metrics are pushed. Prometheus ignores it — it's pull-based. |
|
|
238
|
-
| `metrics.prometheus` | `127.0.0.1:9464` | `host`, `port`, `endpoint`. Binds loopback by default — the endpoint is unauthenticated, so only widen it behind a private network. |
|
|
239
|
-
| `metrics.views` | `[]` | Histogram buckets and cardinality limits. |
|
|
240
|
-
| `logs.exporter` | `none` | `none` · `console` · `otlp` |
|
|
241
|
-
|
|
242
|
-
**Instrumentation and propagation**
|
|
243
|
-
|
|
244
|
-
| Option | Default | What it does |
|
|
245
|
-
| --- | --- | --- |
|
|
246
|
-
| `instrumentation.disable` | `[]` | Instrumentations to switch off, e.g. `[InstrumentationName.DNS]`. |
|
|
247
|
-
| `instrumentation.enable` | `[]` | Switch on one that's off by default. Beats `disable`. |
|
|
248
|
-
| `instrumentation.ignoreIncomingPaths` | `[]` | No spans for these paths. Put your health check here. |
|
|
249
|
-
| `instrumentation.config` | `{}` | Options for individual instrumentations, passed to OpenTelemetry unchanged. |
|
|
250
|
-
| `instrumentation.additional` | `[]` | Instrumentations outside the auto set — community ones, or your own. |
|
|
251
|
-
| `propagators` | `tracecontext`, `baggage` | Trace context formats to read and write. |
|
|
252
|
-
|
|
253
|
-
**Lifecycle and diagnostics**
|
|
254
|
-
|
|
255
|
-
| Option | Default | What it does |
|
|
256
|
-
| --- | --- | --- |
|
|
257
|
-
| `handleShutdownSignals` | `true` | Flush on SIGTERM/SIGINT, then hand the signal back. |
|
|
258
|
-
| `exitOnSignal` | `false` | Call `process.exit(0)` after flushing instead of handing the signal back. |
|
|
259
|
-
| `shutdownTimeoutMillis` | `5000` | Give up if the flush hangs, so shutdown can't stall. |
|
|
260
|
-
| `onStartupError` | logs and continues | Called instead of throwing when the configuration is rejected. |
|
|
261
|
-
| `diagLogLevel` | off | Turns on OpenTelemetry's own internal logging. |
|
|
262
|
-
| `diagLogger` | console | Where that internal logging goes. |
|
|
263
|
-
|
|
264
|
-
## Recipes
|
|
265
|
-
|
|
266
|
-
**Jaeger** — Jaeger accepts OTLP directly, so there's no Jaeger exporter to install:
|
|
159
|
+
## Metrics and logs
|
|
267
160
|
|
|
268
|
-
|
|
269
|
-
traces: { exporter: ExporterType.OTLP, otlp: { url: "http://jaeger:4318/v1/traces" } }
|
|
270
|
-
```
|
|
161
|
+
Both are off until you add their block. Neither needs code beyond the config.
|
|
271
162
|
|
|
272
|
-
|
|
163
|
+
**Metrics** — add a `metrics` block:
|
|
273
164
|
|
|
274
165
|
```ts
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
**Per-instrumentation options** — anything documented upstream works here. `instrumentation.config` is keyed by package name and handed to OpenTelemetry unchanged:
|
|
281
|
-
|
|
282
|
-
```ts
|
|
283
|
-
instrumentation: {
|
|
284
|
-
enable: [InstrumentationName.FASTIFY],
|
|
285
|
-
ignoreIncomingPaths: ["/health"],
|
|
286
|
-
config: {
|
|
287
|
-
[InstrumentationName.HTTP]: {
|
|
288
|
-
ignoreOutgoingRequestHook: (options) => options.hostname === "metrics.internal",
|
|
289
|
-
headersToSpanAttributes: { server: { requestHeaders: ["x-request-id"] } },
|
|
290
|
-
},
|
|
291
|
-
[InstrumentationName.PG]: { enhancedDatabaseReporting: true },
|
|
292
|
-
[InstrumentationName.FASTIFY]: { requestHook: (span, info) => span.setAttribute("plugin", info.pluginName) },
|
|
293
|
-
},
|
|
166
|
+
metrics: {
|
|
167
|
+
exporter: ExporterType.OTLP,
|
|
168
|
+
otlp: { url: process.env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT },
|
|
169
|
+
exportIntervalMillis: 60_000,
|
|
294
170
|
}
|
|
295
171
|
```
|
|
296
172
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
**Every instrumentation in [`@opentelemetry/auto-instrumentations-node`](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/packages/auto-instrumentations-node) works here** — all 41 of them, Express, Koa, Hapi, NestJS, Restify, Postgres, MySQL, Mongo, Redis, Kafka, gRPC, AWS SDK and the rest. They are on by default, `InstrumentationName` has an entry for each, and their own options go through `instrumentation.config` untouched.
|
|
300
|
-
|
|
301
|
-
Each documents its options in its own README:
|
|
173
|
+
You immediately get, with no instrumentation of your own:
|
|
302
174
|
|
|
303
|
-
-
|
|
304
|
-
-
|
|
305
|
-
|
|
306
|
-
`InstrumentationName` values *are* the package names, so the enum entry tells you which README to open.
|
|
307
|
-
|
|
308
|
-
Anything **not** in that set — a community instrumentation, or one you wrote — goes through `instrumentation.additional`, which takes instrumentation instances directly.
|
|
309
|
-
|
|
310
|
-
Three defaults worth knowing, all decided upstream rather than here:
|
|
311
|
-
|
|
312
|
-
- `fs` is off by default. It emits a span per file read and drowns everything else.
|
|
313
|
-
- `fastify` is off by default because `@opentelemetry/instrumentation-fastify` is **deprecated** in favour of [`@fastify/otel`](https://www.npmjs.com/package/@fastify/otel), maintained by the Fastify team. `enable: [InstrumentationName.FASTIFY]` still works and still produces route spans, but it is unmaintained; `@fastify/otel` registers as a Fastify plugin and reports through the same global API this package sets up.
|
|
314
|
-
- database instrumentations replace query values with `?`. `enhancedDatabaseReporting: true` puts the real parameters in your spans — think about customer data before turning it on.
|
|
315
|
-
|
|
316
|
-
**A gRPC collector** — OTLP defaults to HTTP/protobuf on port 4318. For a collector speaking gRPC on 4317:
|
|
317
|
-
|
|
318
|
-
```ts
|
|
319
|
-
traces: { exporter: ExporterType.OTLP, otlp: { protocol: OtlpProtocol.GRPC, url: "http://collector:4317" } }
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
Note the HTTP protocols need the full signal path (`/v1/traces`); gRPC takes the base URL. A missing path is a silent 404 on every export.
|
|
323
|
-
|
|
324
|
-
**Scrubbing attributes before they leave** — add your own span processor:
|
|
325
|
-
|
|
326
|
-
```ts
|
|
327
|
-
traces: { exporter: ExporterType.OTLP, additionalProcessors: [new RedactingSpanProcessor()] }
|
|
328
|
-
```
|
|
175
|
+
- `http.server.duration` and `http.client.duration` — request latency in and out, by route and status
|
|
176
|
+
- `nodejs.eventloop.delay.p50` / `p90` / `p99`, `nodejs.eventloop.utilization` — the event loop, which is what saturates first on a busy Node service
|
|
177
|
+
- `v8js.memory.heap.*` — heap usage and limit
|
|
329
178
|
|
|
330
|
-
|
|
179
|
+
For a pull-based setup, swap the exporter and Prometheus scrapes you instead:
|
|
331
180
|
|
|
332
181
|
```ts
|
|
333
182
|
metrics: { exporter: ExporterType.PROMETHEUS, prometheus: { port: 9464 } }
|
|
334
183
|
```
|
|
335
184
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
**Google Cloud**:
|
|
339
|
-
|
|
340
|
-
```ts
|
|
341
|
-
traces: { exporter: ExporterType.GCP, gcp: { projectId: "my-project" } }
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
Uses `GOOGLE_APPLICATION_CREDENTIALS` if it points at a readable file, otherwise application default credentials.
|
|
345
|
-
|
|
346
|
-
**Seeing spans locally** — no collector needed:
|
|
185
|
+
**Logs** — add a `logs` block:
|
|
347
186
|
|
|
348
187
|
```ts
|
|
349
|
-
|
|
188
|
+
logs: { exporter: ExporterType.OTLP, otlp: { url: process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT } }
|
|
350
189
|
```
|
|
351
190
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
**Quieter traces** — the noisiest instrumentations are usually DNS and net, especially with a database driver reconnecting:
|
|
355
|
-
|
|
356
|
-
```ts
|
|
357
|
-
instrumentation: { disable: [InstrumentationName.DNS, InstrumentationName.NET] }
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
## Troubleshooting
|
|
361
|
-
|
|
362
|
-
A rejected configuration produces a `TelemetryConfigError`. It is **reported, not thrown** — telemetry switches itself off and your service still boots. `onStartupError` decides where that goes; by default it is logged.
|
|
191
|
+
You do not change how you log. If you use pino, winston or bunyan, the log instrumentation bridges what you already write into OpenTelemetry, carrying the `trace_id` that ties each line to its span — so a trace links straight to the logs from that request.
|
|
363
192
|
|
|
364
|
-
|
|
365
|
-
| --- | --- |
|
|
366
|
-
| `MISSING_SERVICE_NAME` | `serviceName` empty or missing. |
|
|
367
|
-
| `INVALID_SAMPLE_RATIO` | `sampleRatio` outside 0–1. |
|
|
368
|
-
| `UNSUPPORTED_EXPORTER` | Exporter can't handle that signal, e.g. `prometheus` for traces. |
|
|
369
|
-
| `UNSUPPORTED_PROPAGATOR` | Unknown propagator name. |
|
|
370
|
-
| `MISSING_OPTIONAL_DEPENDENCY` | Exporter selected but its package isn't installed. |
|
|
371
|
-
|
|
372
|
-
**No traces showing up? Read the `trace_flags` on your own log lines first.** If you use pino, bunyan or winston, the log instrumentation stamps every line with the active trace:
|
|
373
|
-
|
|
374
|
-
```json
|
|
375
|
-
{"trace_id":"9d497527a7ec14c5a81325251113283d","span_id":"b58894198a10765d","trace_flags":"00"}
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
That one field splits the problem in half:
|
|
379
|
-
|
|
380
|
-
- **`trace_flags: "00"`** — the span was created and then dropped by the sampler. Nothing was ever sent, so the collector is irrelevant. Raise `sampleRatio` to 1, or check whether an inbound `traceparent` arrived already marked unsampled — parent-based sampling honours the caller's decision.
|
|
381
|
-
- **`trace_flags: "01"`** — the span was sampled and handed to the exporter, so the problem is downstream: the endpoint, the path, or the network.
|
|
382
|
-
- **no `trace_id` at all** — the SDK never started, or it started after your app loaded. Check the `--require` flag.
|
|
383
|
-
|
|
384
|
-
Then turn on OpenTelemetry's own logging — it is off by default, which is why a bad endpoint or an unreachable collector produces silence rather than an error:
|
|
385
|
-
|
|
386
|
-
```ts
|
|
387
|
-
import { DiagLogLevel } from "@opentelemetry/api";
|
|
388
|
-
|
|
389
|
-
Telemetry.start({ ..., diagLogLevel: DiagLogLevel.ERROR });
|
|
390
|
-
```
|
|
193
|
+
Two things to weigh before turning logs on. Your log volume goes to two places, so you pay to store it twice unless you drop stdout collection. And any gap in your redaction now reaches a second system: check what your logger emits — response bodies and auth headers are the usual leaks — before pointing it at a backend.
|
|
391
194
|
|
|
392
|
-
|
|
195
|
+
## More
|
|
393
196
|
|
|
394
|
-
|
|
197
|
+
- [Configuration](https://github.com/omob/otel-kit/blob/main/docs/configuration.md) — every option, shutdown behaviour, and what happens when a config is rejected
|
|
198
|
+
- [Recipes](https://github.com/omob/otel-kit/blob/main/docs/recipes.md) — Jaeger, Google Cloud, Prometheus, gRPC collectors, per-instrumentation options
|
|
199
|
+
- [Troubleshooting](https://github.com/omob/otel-kit/blob/main/docs/troubleshooting.md) — no traces appearing, wrong service name, broken propagation
|
|
200
|
+
- [Concepts](https://github.com/omob/otel-kit/blob/main/docs/concepts.md) — traces, spans, sampling and propagation, if OpenTelemetry is new to you
|
|
395
201
|
|
|
396
|
-
|
|
202
|
+
## Licence
|
|
397
203
|
|
|
398
|
-
|
|
204
|
+
MIT
|
|
@@ -8,6 +8,7 @@ import { InstrumentationName } from "./enums/instrumentation-name.enum";
|
|
|
8
8
|
import { OtlpProtocol } from "./enums/otlp-protocol.enum";
|
|
9
9
|
import { PropagatorType } from "./enums/propagator-type.enum";
|
|
10
10
|
export type ResourceAttributeValue = string | number | boolean;
|
|
11
|
+
export type OtlpHeaders = Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
|
|
11
12
|
export type SpanHandler<T> = (span: import("@opentelemetry/api").Span) => Promise<T> | T;
|
|
12
13
|
export interface IGcpTraceModule {
|
|
13
14
|
TraceExporter: new (options: object) => SpanExporter;
|
|
@@ -21,7 +22,7 @@ export interface IPrometheusModule {
|
|
|
21
22
|
export interface IOtlpOptions {
|
|
22
23
|
protocol?: OtlpProtocol;
|
|
23
24
|
url?: string;
|
|
24
|
-
headers?:
|
|
25
|
+
headers?: OtlpHeaders;
|
|
25
26
|
timeoutMillis?: number;
|
|
26
27
|
}
|
|
27
28
|
export interface IGcpOptions {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IOtlpOptions } from "../telemetry.types";
|
|
2
2
|
export declare function toOtlpExporterOptions(options?: IOtlpOptions): {
|
|
3
3
|
timeoutMillis?: number | undefined;
|
|
4
|
-
headers?:
|
|
4
|
+
headers?: import("../telemetry.types").OtlpHeaders | undefined;
|
|
5
5
|
url?: string | undefined;
|
|
6
6
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omob/otel-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Configurable OpenTelemetry bootstrap for Node services: traces, metrics and logs with pluggable exporters",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"lint": "eslint src test",
|
|
23
23
|
"lint:fix": "eslint src test --fix",
|
|
24
24
|
"test": "jest --runInBand",
|
|
25
|
-
"verify:consumers": "node scripts/verify-consumers.mjs"
|
|
25
|
+
"verify:consumers": "node scripts/verify-consumers.mjs",
|
|
26
|
+
"verify:docs": "node scripts/verify-docs.mjs"
|
|
26
27
|
},
|
|
27
28
|
"keywords": [
|
|
28
29
|
"opentelemetry",
|