autotel 2.26.3 → 3.0.3
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 +50 -23
- package/dist/attribute-redacting-processor.cjs +8 -8
- package/dist/attribute-redacting-processor.d.cts +10 -1
- package/dist/attribute-redacting-processor.d.ts +10 -1
- package/dist/attribute-redacting-processor.js +1 -1
- package/dist/attributes.cjs +21 -21
- package/dist/attributes.d.cts +3 -3
- package/dist/attributes.d.ts +3 -3
- package/dist/attributes.js +2 -2
- package/dist/auto.cjs +3 -3
- package/dist/auto.js +2 -2
- package/dist/business-baggage.d.cts +1 -1
- package/dist/business-baggage.d.ts +1 -1
- package/dist/chunk-4P6ZOARG.cjs +33 -0
- package/dist/chunk-4P6ZOARG.cjs.map +1 -0
- package/dist/{chunk-U54FTVFH.js → chunk-52PUSFC2.js} +3 -3
- package/dist/{chunk-U54FTVFH.js.map → chunk-52PUSFC2.js.map} +1 -1
- package/dist/{chunk-YEVCD6DR.cjs → chunk-7SMNC4LS.cjs} +7 -7
- package/dist/{chunk-YEVCD6DR.cjs.map → chunk-7SMNC4LS.cjs.map} +1 -1
- package/dist/{chunk-563EL6O6.cjs → chunk-BPO2PQ3T.cjs} +12 -8
- package/dist/chunk-BPO2PQ3T.cjs.map +1 -0
- package/dist/{chunk-WZOKY3PW.cjs → chunk-DAZ7EGR4.cjs} +19 -19
- package/dist/{chunk-WZOKY3PW.cjs.map → chunk-DAZ7EGR4.cjs.map} +1 -1
- package/dist/{chunk-ER43K7ES.js → chunk-DDXIUZEG.js} +3 -3
- package/dist/{chunk-ER43K7ES.js.map → chunk-DDXIUZEG.js.map} +1 -1
- package/dist/{chunk-JKIMEPI2.cjs → chunk-DQ2SUROF.cjs} +4 -4
- package/dist/{chunk-JKIMEPI2.cjs.map → chunk-DQ2SUROF.cjs.map} +1 -1
- package/dist/{chunk-B3ZHLLMP.js → chunk-DSMSIVTG.js} +2 -2
- package/dist/chunk-DSMSIVTG.js.map +1 -0
- package/dist/{chunk-OBWXM4NN.cjs → chunk-HKZHUGGN.cjs} +15 -14
- package/dist/chunk-HKZHUGGN.cjs.map +1 -0
- package/dist/{chunk-TDNKIHKT.js → chunk-JVWJDHDB.js} +13 -4
- package/dist/chunk-JVWJDHDB.js.map +1 -0
- package/dist/{chunk-YN7USLHW.js → chunk-K7HSRLP5.js} +11 -10
- package/dist/chunk-K7HSRLP5.js.map +1 -0
- package/dist/chunk-KIL5CUN6.js +31 -0
- package/dist/chunk-KIL5CUN6.js.map +1 -0
- package/dist/chunk-KKGM42RQ.cjs +1207 -0
- package/dist/chunk-KKGM42RQ.cjs.map +1 -0
- package/dist/{chunk-6YGUN7IY.cjs → chunk-MOO75VE4.cjs} +18 -17
- package/dist/chunk-MOO75VE4.cjs.map +1 -0
- package/dist/{chunk-GML3FBOT.cjs → chunk-NCSMD3TK.cjs} +2 -2
- package/dist/chunk-NCSMD3TK.cjs.map +1 -0
- package/dist/{chunk-CMNGGTQL.cjs → chunk-NXLRY2CE.cjs} +13 -4
- package/dist/chunk-NXLRY2CE.cjs.map +1 -0
- package/dist/{chunk-BJ2XPN77.js → chunk-OM4OSBOP.js} +5 -5
- package/dist/{chunk-BJ2XPN77.js.map → chunk-OM4OSBOP.js.map} +1 -1
- package/dist/{chunk-HPUGKUMZ.js → chunk-PMRWMRXY.js} +13 -640
- package/dist/chunk-PMRWMRXY.js.map +1 -0
- package/dist/{chunk-UTZR7P7E.cjs → chunk-QPH5ZKP5.cjs} +43 -673
- package/dist/chunk-QPH5ZKP5.cjs.map +1 -0
- package/dist/chunk-SEO6NAQT.js +14 -0
- package/dist/chunk-SEO6NAQT.js.map +1 -0
- package/dist/{chunk-QC5MNKVF.js → chunk-TFRZOUTV.js} +13 -12
- package/dist/chunk-TFRZOUTV.js.map +1 -0
- package/dist/chunk-VQTCQKHQ.cjs +17 -0
- package/dist/chunk-VQTCQKHQ.cjs.map +1 -0
- package/dist/chunk-Z7VAOK5X.js +1183 -0
- package/dist/chunk-Z7VAOK5X.js.map +1 -0
- package/dist/{chunk-W35FVJBC.js → chunk-ZDPIWKWD.js} +9 -5
- package/dist/chunk-ZDPIWKWD.js.map +1 -0
- package/dist/correlation-id.cjs +22 -10
- package/dist/correlation-id.js +14 -2
- package/dist/decorators.cjs +7 -8
- package/dist/decorators.cjs.map +1 -1
- package/dist/decorators.d.cts +1 -1
- package/dist/decorators.d.ts +1 -1
- package/dist/decorators.js +6 -7
- package/dist/decorators.js.map +1 -1
- package/dist/event.cjs +8 -9
- package/dist/event.js +5 -6
- package/dist/functional.cjs +13 -14
- package/dist/functional.d.cts +1 -1
- package/dist/functional.d.ts +1 -1
- package/dist/functional.js +6 -7
- package/dist/http.cjs +13 -2
- package/dist/http.cjs.map +1 -1
- package/dist/http.js +12 -1
- package/dist/http.js.map +1 -1
- package/dist/index.cjs +305 -280
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +89 -10
- package/dist/index.d.ts +89 -10
- package/dist/index.js +180 -181
- package/dist/index.js.map +1 -1
- package/dist/instrumentation.cjs +9 -9
- package/dist/instrumentation.js +2 -2
- package/dist/messaging-adapters.d.cts +1 -1
- package/dist/messaging-adapters.d.ts +1 -1
- package/dist/messaging-testing.d.cts +1 -1
- package/dist/messaging-testing.d.ts +1 -1
- package/dist/messaging.cjs +11 -11
- package/dist/messaging.d.cts +1 -1
- package/dist/messaging.d.ts +1 -1
- package/dist/messaging.js +8 -8
- package/dist/semantic-helpers.cjs +11 -12
- package/dist/semantic-helpers.d.cts +1 -1
- package/dist/semantic-helpers.d.ts +1 -1
- package/dist/semantic-helpers.js +7 -8
- package/dist/{trace-context-t5X1AP-e.d.cts → trace-context-DbGKd1Rn.d.cts} +18 -5
- package/dist/{trace-context-t5X1AP-e.d.ts → trace-context-DbGKd1Rn.d.ts} +18 -5
- package/dist/trace-helpers.cjs +13 -13
- package/dist/trace-helpers.d.cts +2 -2
- package/dist/trace-helpers.d.ts +2 -2
- package/dist/trace-helpers.js +1 -1
- package/dist/{utils-CbUkl8r1.d.cts → utils-BahBCFtJ.d.cts} +1 -1
- package/dist/{utils-Buel3cj0.d.ts → utils-CLKwaUlG.d.ts} +1 -1
- package/dist/webhook.cjs +21 -12
- package/dist/webhook.cjs.map +1 -1
- package/dist/webhook.d.cts +1 -1
- package/dist/webhook.d.ts +1 -1
- package/dist/webhook.js +20 -11
- package/dist/webhook.js.map +1 -1
- package/dist/workflow-distributed.cjs +25 -21
- package/dist/workflow-distributed.cjs.map +1 -1
- package/dist/workflow-distributed.d.cts +1 -1
- package/dist/workflow-distributed.d.ts +1 -1
- package/dist/workflow-distributed.js +23 -19
- package/dist/workflow-distributed.js.map +1 -1
- package/dist/workflow.cjs +12 -12
- package/dist/workflow.d.cts +1 -1
- package/dist/workflow.d.ts +1 -1
- package/dist/workflow.js +8 -8
- package/package.json +43 -45
- package/skills/analyze-traces/SKILL.md +178 -0
- package/skills/autotel-core/SKILL.md +2 -7
- package/skills/autotel-events/SKILL.md +2 -6
- package/skills/autotel-frameworks/SKILL.md +2 -9
- package/skills/autotel-instrumentation/SKILL.md +2 -7
- package/skills/autotel-request-logging/SKILL.md +2 -8
- package/skills/autotel-structured-errors/SKILL.md +2 -7
- package/skills/build-audit-trails/SKILL.md +302 -0
- package/skills/debug-missing-spans/SKILL.md +248 -0
- package/skills/migrate-to-autotel/SKILL.md +268 -0
- package/skills/review-otel-patterns/SKILL.md +488 -0
- package/skills/review-otel-patterns/references/code-review.md +75 -0
- package/skills/review-otel-patterns/references/processor-pipeline.md +205 -0
- package/skills/review-otel-patterns/references/structured-errors.md +102 -0
- package/skills/review-otel-patterns/references/wide-spans.md +85 -0
- package/skills/tune-sampling/SKILL.md +210 -0
- package/src/attribute-redacting-processor.test.ts +6 -4
- package/src/attribute-redacting-processor.ts +11 -2
- package/src/correlated-events.test.ts +151 -0
- package/src/correlated-events.ts +47 -0
- package/src/drain-toolkit.test.ts +113 -0
- package/src/drain-toolkit.ts +129 -0
- package/src/enricher-toolkit.test.ts +67 -0
- package/src/enricher-toolkit.ts +79 -0
- package/src/functional.ts +2 -0
- package/src/gen-ai-events.ts +14 -5
- package/src/index.ts +39 -4
- package/src/messaging.ts +10 -9
- package/src/redact-values.test.ts +24 -10
- package/src/redact-values.ts +9 -2
- package/src/request-logger.test.ts +91 -0
- package/src/request-logger.ts +40 -5
- package/src/structured-error.test.ts +86 -1
- package/src/structured-error.ts +9 -2
- package/src/trace-context.ts +39 -11
- package/src/trace-helpers.ts +2 -2
- package/src/trace-hybrid.test.ts +42 -0
- package/src/trace-hybrid.ts +37 -0
- package/src/webhook.ts +16 -7
- package/src/workflow-distributed.ts +18 -13
- package/src/workflow.ts +7 -6
- package/bin/intent.js +0 -6
- package/dist/chunk-563EL6O6.cjs.map +0 -1
- package/dist/chunk-6YGUN7IY.cjs.map +0 -1
- package/dist/chunk-B3ZHLLMP.js.map +0 -1
- package/dist/chunk-BBBWDIYQ.js +0 -211
- package/dist/chunk-BBBWDIYQ.js.map +0 -1
- package/dist/chunk-CMNGGTQL.cjs.map +0 -1
- package/dist/chunk-D5LMF53P.cjs +0 -150
- package/dist/chunk-D5LMF53P.cjs.map +0 -1
- package/dist/chunk-GML3FBOT.cjs.map +0 -1
- package/dist/chunk-HPUGKUMZ.js.map +0 -1
- package/dist/chunk-HZ3FYBJG.cjs +0 -217
- package/dist/chunk-HZ3FYBJG.cjs.map +0 -1
- package/dist/chunk-JSNUWSBH.cjs +0 -62
- package/dist/chunk-JSNUWSBH.cjs.map +0 -1
- package/dist/chunk-OBWXM4NN.cjs.map +0 -1
- package/dist/chunk-QC5MNKVF.js.map +0 -1
- package/dist/chunk-S4OFEXLA.js +0 -53
- package/dist/chunk-S4OFEXLA.js.map +0 -1
- package/dist/chunk-TDNKIHKT.js.map +0 -1
- package/dist/chunk-UTZR7P7E.cjs.map +0 -1
- package/dist/chunk-W35FVJBC.js.map +0 -1
- package/dist/chunk-WD4RP6IV.js +0 -146
- package/dist/chunk-WD4RP6IV.js.map +0 -1
- package/dist/chunk-YN7USLHW.js.map +0 -1
- package/src/package-manifest.test.ts +0 -24
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: review-otel-patterns
|
|
3
|
+
description: >
|
|
4
|
+
Review TypeScript/JavaScript code for OpenTelemetry instrumentation patterns and guide adoption of autotel.
|
|
5
|
+
Covers Next.js, Nuxt, Nitro, TanStack Start, SvelteKit, NestJS, Express, Hono, Fastify, Elysia,
|
|
6
|
+
Cloudflare Workers, AWS Lambda, edge runtimes, and standalone Node. Detects unstructured tracing,
|
|
7
|
+
missing span attributes, manual exporter setup, broken context propagation, exposed PII, and ad-hoc
|
|
8
|
+
error handling. Covers spans, metrics, logs, structured errors, the autotel processor pipeline
|
|
9
|
+
(tail-sampling, attribute redaction, span-name normalisation, filtering, baggage),
|
|
10
|
+
`defineWorkerFetch` for Cloudflare async drains, multi-vendor OTLP backends (Honeycomb, Datadog,
|
|
11
|
+
Grafana Cloud, Sentry, Axiom, HyperDX), `composeSpanProcessors` / `composeSubscribers` /
|
|
12
|
+
`composePostProcessors` for pipelines, AI SDK observability with gen-ai semantic conventions, and
|
|
13
|
+
end-to-end OTLP testing.
|
|
14
|
+
license: MIT
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Review OpenTelemetry patterns
|
|
18
|
+
|
|
19
|
+
Review and improve OpenTelemetry instrumentation in TypeScript/JavaScript codebases using autotel. Replace ad-hoc tracing with idiomatic OTel-native spans, metrics and structured logs that work across every major framework and edge runtime — without vendor lock-in.
|
|
20
|
+
|
|
21
|
+
## When to use
|
|
22
|
+
|
|
23
|
+
- Setting up autotel in a new or existing project (any supported framework)
|
|
24
|
+
- Reviewing code for OpenTelemetry best practices
|
|
25
|
+
- Converting `console.log` / ad-hoc tracing to spans + structured events
|
|
26
|
+
- Improving error handling with structured errors and span status
|
|
27
|
+
- Configuring sampling, redaction, processors, or backends
|
|
28
|
+
- Migrating between observability vendors
|
|
29
|
+
|
|
30
|
+
## Quick reference
|
|
31
|
+
|
|
32
|
+
| Working on… | Resource |
|
|
33
|
+
| -------------------------------- | -------------------------------------------------------------------- |
|
|
34
|
+
| Span design + wide events | [references/wide-spans.md](references/wide-spans.md) |
|
|
35
|
+
| Structured errors | [references/structured-errors.md](references/structured-errors.md) |
|
|
36
|
+
| Code review checklist | [references/code-review.md](references/code-review.md) |
|
|
37
|
+
| Processor pipeline + composition | [references/processor-pipeline.md](references/processor-pipeline.md) |
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install autotel
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
For framework-specific helpers, install the relevant package alongside core:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install autotel-cloudflare # Workers + Durable Objects + Workflows
|
|
49
|
+
npm install autotel-edge # Vendor-agnostic edge runtime base
|
|
50
|
+
npm install autotel-hono # Hono middleware
|
|
51
|
+
npm install autotel-tanstack # TanStack Start
|
|
52
|
+
npm install autotel-adapters # Next.js / Nitro / Hono / Cloudflare adapter toolkit
|
|
53
|
+
npm install autotel-vitest # Span assertions in tests
|
|
54
|
+
npm install autotel-playwright # Browser → server trace propagation
|
|
55
|
+
npm install autotel-drizzle # Drizzle ORM auto-instrumentation
|
|
56
|
+
npm install autotel-mongoose # Mongoose auto-instrumentation
|
|
57
|
+
npm install autotel-sentry # Sentry exporter via OTLP
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Framework setup
|
|
63
|
+
|
|
64
|
+
### Next.js (App Router)
|
|
65
|
+
|
|
66
|
+
Step 1 — Initialise once in `instrumentation.ts`:
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// instrumentation.ts
|
|
70
|
+
import { init } from 'autotel';
|
|
71
|
+
|
|
72
|
+
export function register() {
|
|
73
|
+
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
74
|
+
init({
|
|
75
|
+
service: 'my-app',
|
|
76
|
+
exporter: { url: process.env.OTLP_ENDPOINT! },
|
|
77
|
+
sampling: { rates: { server: 25, client: 5 } },
|
|
78
|
+
attributeRedactor: 'default',
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Step 2 — Wrap route handlers:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// app/api/checkout/route.ts
|
|
88
|
+
import { withAutotel } from 'autotel-adapters';
|
|
89
|
+
|
|
90
|
+
export const POST = withAutotel(async (request: Request) => {
|
|
91
|
+
const log = useLogger();
|
|
92
|
+
log.set({ user: { id: 'usr_123', plan: 'enterprise' } });
|
|
93
|
+
log.set({ cart: { items: 3, total: 14_999 } });
|
|
94
|
+
return Response.json({ ok: true });
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Step 3 — Tag Server Actions the same way (`withAutotel`).
|
|
99
|
+
|
|
100
|
+
Step 4 — Browser → server trace propagation: drop in `<TraceProvider />` from `autotel-web` so the W3C `traceparent` header is forwarded automatically.
|
|
101
|
+
|
|
102
|
+
### Nuxt + Nitro v3
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// nitro.config.ts
|
|
106
|
+
import { defineConfig } from 'nitro';
|
|
107
|
+
import autotel from 'autotel-adapters/nitro';
|
|
108
|
+
|
|
109
|
+
export default defineConfig({
|
|
110
|
+
modules: [
|
|
111
|
+
autotel({ service: 'my-api', sampling: { rates: { server: 25 } } }),
|
|
112
|
+
],
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// routes/api/checkout.post.ts
|
|
118
|
+
import { useLogger } from 'autotel-adapters/nitro';
|
|
119
|
+
|
|
120
|
+
export default defineEventHandler(async (event) => {
|
|
121
|
+
const log = useLogger(event);
|
|
122
|
+
log.set({ action: 'checkout', user: { id: event.context.user?.id } });
|
|
123
|
+
return { ok: true };
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### TanStack Start
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// nitro.config.ts
|
|
131
|
+
import autotel from 'autotel-adapters/nitro';
|
|
132
|
+
export default defineConfig({
|
|
133
|
+
modules: [autotel({ service: 'my-app' })],
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// src/routes/__root.tsx
|
|
139
|
+
import { createMiddleware } from '@tanstack/react-start';
|
|
140
|
+
import { autotelMiddleware } from 'autotel-tanstack';
|
|
141
|
+
|
|
142
|
+
export const Route = createRootRoute({
|
|
143
|
+
server: { middleware: [createMiddleware().server(autotelMiddleware())] },
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Hono
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { Hono } from 'hono';
|
|
151
|
+
import { honoToolkit } from 'autotel-adapters';
|
|
152
|
+
|
|
153
|
+
const { useLogger, withAutotel } = honoToolkit;
|
|
154
|
+
const app = new Hono();
|
|
155
|
+
app.use('*', withAutotel({ service: 'my-api' }));
|
|
156
|
+
|
|
157
|
+
app.get('/api/users', (c) => {
|
|
158
|
+
const log = useLogger(c);
|
|
159
|
+
log.set({ users: { count: 42 } });
|
|
160
|
+
return c.json({ users: [] });
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Express / Fastify / Elysia / NestJS
|
|
165
|
+
|
|
166
|
+
The `autotel-adapters` toolkit ships a uniform shape for these: `withAutotel` middleware + `useLogger()` from anywhere in the call stack. See per-framework docs in `packages/autotel-adapters/skills/autotel-adapters/SKILL.md`.
|
|
167
|
+
|
|
168
|
+
### Cloudflare Workers (with auto `waitUntil`)
|
|
169
|
+
|
|
170
|
+
`defineWorkerFetch` instruments the handler **and** wires `ctx.waitUntil` for span exports — without it, async exports silently drop:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { defineWorkerFetch } from 'autotel-cloudflare';
|
|
174
|
+
|
|
175
|
+
export default defineWorkerFetch(
|
|
176
|
+
{ service: { name: 'edge-api' } },
|
|
177
|
+
async (request, env, ctx, log) => {
|
|
178
|
+
log.set({ route: '/health', user: { id: env.userId } });
|
|
179
|
+
return new Response('ok');
|
|
180
|
+
},
|
|
181
|
+
);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
For broader use cases (`scheduled`, `queue`, `email` handlers, Durable Objects, Workflows), use `wrapModule` / `wrapDurableObject` / `instrumentWorkflow` — same auto-`waitUntil` semantics.
|
|
185
|
+
|
|
186
|
+
### AWS Lambda
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
import { withLambda } from 'autotel-aws';
|
|
190
|
+
|
|
191
|
+
export const handler = withLambda(async (event) => {
|
|
192
|
+
const log = useLogger();
|
|
193
|
+
log.set({ event: { source: event.source } });
|
|
194
|
+
return { statusCode: 200 };
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Standalone Node / scripts
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { init, trace } from 'autotel';
|
|
202
|
+
|
|
203
|
+
init({ service: 'my-worker', exporter: { url: process.env.OTLP_ENDPOINT! } });
|
|
204
|
+
|
|
205
|
+
const processJob = trace(async (job: Job) => {
|
|
206
|
+
// span auto-named after the function
|
|
207
|
+
const log = useLogger();
|
|
208
|
+
log.set({ job: { id: job.id, source: job.source } });
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Configuration options
|
|
215
|
+
|
|
216
|
+
All options work with `init()`, framework adapters, and `wrapModule` / `defineWorkerFetch`:
|
|
217
|
+
|
|
218
|
+
| Option | Type | Default | Description |
|
|
219
|
+
| --------------------------------------- | --------------------------------------------------------------- | ----------------- | --------------------------------------------------------------- |
|
|
220
|
+
| `service` / `service.name` | `string` | `'app'` | Service name in `service.name` resource attribute |
|
|
221
|
+
| `exporter` | `{ url, headers?, protocol? }` | — | OTLP HTTP/JSON or HTTP/protobuf endpoint |
|
|
222
|
+
| `spanProcessors` | `SpanProcessor[]` | — | Use **instead of** `exporter` for full control |
|
|
223
|
+
| `sampling.rates` | `{ server?: number, client?: number, internal?: number }` | `100%` | Head sampling per span kind (0–100%) |
|
|
224
|
+
| `sampling.tail` | `TailSampleFn` | — | Keep traces matching predicate (e.g. errors, slow) |
|
|
225
|
+
| `attributeRedactor` | `'default' \| 'strict' \| 'pci-dss' \| AttributeRedactorConfig` | — | PII redaction; on by default in production |
|
|
226
|
+
| `instrumentation.disabled` | `boolean` | `false` | Hard-off switch (ideal for local dev) |
|
|
227
|
+
| `instrumentation.instrumentGlobalFetch` | `boolean` | `true` | Patch `globalThis.fetch` for outbound HTTP spans |
|
|
228
|
+
| `subscribers` | `EdgeSubscriber[]` | — | In-process side effects (metrics, audit, AI cost) |
|
|
229
|
+
| `postProcessor` | `PostProcessorFn` | — | Mutate spans before export (redact, drop, tag) |
|
|
230
|
+
| `propagator` | `TextMapPropagator` | W3C trace-context | Override propagation format |
|
|
231
|
+
| `dataSafety` | `DataSafetyConfig` | — | Per-attribute safety (`captureDbStatement: 'obfuscated'`, etc.) |
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Backends (multi-vendor OTLP)
|
|
236
|
+
|
|
237
|
+
Switch backends with **no code changes** — autotel speaks OTLP HTTP/JSON and HTTP/protobuf out of the box.
|
|
238
|
+
|
|
239
|
+
| Backend | Endpoint | Headers |
|
|
240
|
+
| -------------------------------- | ---------------------------------------------------------- | ------------------------------------- |
|
|
241
|
+
| Honeycomb | `https://api.honeycomb.io/v1/traces` | `{ 'x-honeycomb-team': '<key>' }` |
|
|
242
|
+
| Grafana Cloud | `https://otlp-gateway-<region>.grafana.net/otlp/v1/traces` | `{ authorization: 'Basic <b64>' }` |
|
|
243
|
+
| Datadog (OTLP intake) | `https://trace.agent.datadoghq.com/api/v0.4/traces` | `{ 'dd-api-key': '<key>' }` |
|
|
244
|
+
| Sentry | `<dsn>/api/<id>/envelope/` (use `autotel-sentry`) | `{ 'x-sentry-auth': '…' }` |
|
|
245
|
+
| Axiom | `https://api.axiom.co/v1/traces` | `{ authorization: 'Bearer <token>' }` |
|
|
246
|
+
| HyperDX | `https://in-otel.hyperdx.io/v1/traces` | `{ authorization: '<key>' }` |
|
|
247
|
+
| New Relic | `https://otlp.nr-data.net/v1/traces` | `{ 'api-key': '<key>' }` |
|
|
248
|
+
| Local Jaeger / Tempo / Collector | `http://localhost:4318/v1/traces` | — |
|
|
249
|
+
|
|
250
|
+
Use `init({ exporter: { url, headers } })`. Multiple destinations? Use `composeSpanProcessors([batchA, batchB])` (see Composition below).
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Auto-redaction (PII protection)
|
|
255
|
+
|
|
256
|
+
Built-in masking scrubs sensitive data from span attributes **before** export. **On by default in production**, off in development. Smart partial masking preserves debug signal:
|
|
257
|
+
|
|
258
|
+
| Pattern | Example input | Masked output |
|
|
259
|
+
| ------------ | --------------------- | -------------------------------------------- |
|
|
260
|
+
| `creditCard` | `4111-1111-1111-1111` | `****1111` (PCI-DSS compliant) |
|
|
261
|
+
| `email` | `alice@example.com` | `a***@***.com` |
|
|
262
|
+
| `ipv4` | `192.168.1.100` | `***.***.***.100` |
|
|
263
|
+
| `phone` | `+33 6 12 34 56 78` | `+33******78` (requires `+cc` or `(parens)`) |
|
|
264
|
+
| `jwt` | `eyJhbGciOi…` | `eyJ***.***` |
|
|
265
|
+
| `bearer` | `Bearer sk_live_abc…` | `Bearer ***` |
|
|
266
|
+
| `iban` | `FR76 3000 6000 …189` | `FR76****189` |
|
|
267
|
+
|
|
268
|
+
Three presets ship out of the box: `'default'` (PII-grade), `'strict'` (adds JWT / Bearer / IBAN, redacts more keys), `'pci-dss'` (cards only, PCI focus).
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
init({
|
|
272
|
+
service: 'my-app',
|
|
273
|
+
attributeRedactor: 'default',
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Custom config:
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
import { builtinPatterns, type AttributeRedactorConfig } from 'autotel';
|
|
281
|
+
|
|
282
|
+
const config: AttributeRedactorConfig = {
|
|
283
|
+
keyPatterns: [/password/i, /^x-internal-/i],
|
|
284
|
+
builtins: ['email', 'creditCard', 'jwt'], // pick specific masks
|
|
285
|
+
valuePatterns: [
|
|
286
|
+
{ name: 'customerId', pattern: /CUST-\d{8}/g, replacement: 'CUST-***' },
|
|
287
|
+
],
|
|
288
|
+
};
|
|
289
|
+
init({ attributeRedactor: config });
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
For free-text fields outside the span pipeline (logs, error messages, frontend payloads), use `createStringRedactor('default')` — same masks, returns a `(s: string) => string`.
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Composition (build pipelines from small parts)
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
import {
|
|
300
|
+
composeSpanProcessors,
|
|
301
|
+
composeSubscribers,
|
|
302
|
+
composePostProcessors,
|
|
303
|
+
defineConfig,
|
|
304
|
+
} from 'autotel-edge';
|
|
305
|
+
|
|
306
|
+
export const otelConfig = defineConfig({
|
|
307
|
+
service: { name: 'checkout' },
|
|
308
|
+
spanProcessors: composeSpanProcessors([
|
|
309
|
+
new BatchSpanProcessor(honeycombExporter),
|
|
310
|
+
new BatchSpanProcessor(datadogExporter),
|
|
311
|
+
new TailSamplingProcessor({ keep: { errors: true, slow: 500 } }),
|
|
312
|
+
]),
|
|
313
|
+
subscribers: [
|
|
314
|
+
composeSubscribers([metricsSubscriber, auditSubscriber, aiCostSubscriber]),
|
|
315
|
+
],
|
|
316
|
+
postProcessor: composePostProcessors([
|
|
317
|
+
redactPiiInStackTraces,
|
|
318
|
+
dropHealthChecks,
|
|
319
|
+
]),
|
|
320
|
+
});
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Errors are isolated per item — one bad processor cannot break the others. See [references/processor-pipeline.md](references/processor-pipeline.md) for the full pipeline cookbook.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Processors (autotel-specific superpowers)
|
|
328
|
+
|
|
329
|
+
| Processor | Purpose |
|
|
330
|
+
| ------------------------------ | ----------------------------------------------------------- |
|
|
331
|
+
| `AttributeRedactingProcessor` | PII masking with smart partial output |
|
|
332
|
+
| `TailSamplingProcessor` | Keep errors + slow + sampled-by-rate |
|
|
333
|
+
| `FilteringSpanProcessor` | Drop spans matching predicate (health checks, etc.) |
|
|
334
|
+
| `SpanNameNormalizingProcessor` | Normalise `/users/123` → `/users/{id}` to bound cardinality |
|
|
335
|
+
| `BaggageSpanProcessor` | Lift baggage entries onto every span |
|
|
336
|
+
| `PrettyConsoleExporter` | Hierarchical colourised output for local dev |
|
|
337
|
+
|
|
338
|
+
Compose them at build time with `composeSpanProcessors([...])` — no boilerplate.
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## AI SDK integration (gen-ai semantic conventions)
|
|
343
|
+
|
|
344
|
+
autotel implements the **OTel gen-ai semantic conventions** out of the box. Token usage, tool calls, model info, latency, cost — captured as standard attributes (`gen_ai.usage.input_tokens`, `gen_ai.tool.name`, `gen_ai.response.finish_reason`, …) so any backend that understands OTel can render LLM telemetry without custom mapping.
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
import { trace } from 'autotel';
|
|
348
|
+
import { withAiTelemetry } from 'autotel-edge';
|
|
349
|
+
import { streamText } from 'ai';
|
|
350
|
+
|
|
351
|
+
const handler = trace(async (req) => {
|
|
352
|
+
const result = await streamText({
|
|
353
|
+
model: withAiTelemetry('anthropic/claude-sonnet-4.6'),
|
|
354
|
+
messages: req.messages,
|
|
355
|
+
experimental_telemetry: { isEnabled: true },
|
|
356
|
+
});
|
|
357
|
+
return result.toResponse();
|
|
358
|
+
});
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Captured attributes per call: `gen_ai.system`, `gen_ai.request.model`, `gen_ai.usage.{input,output,reasoning,cache_read}_tokens`, `gen_ai.response.finish_reason`, `gen_ai.response.id`, plus per-tool spans with `gen_ai.tool.name`, `gen_ai.tool.duration`. Cost estimation comes for free if you pass a pricing map to `withAiTelemetry`.
|
|
362
|
+
|
|
363
|
+
Anti-patterns to detect:
|
|
364
|
+
|
|
365
|
+
| Anti-pattern | Fix |
|
|
366
|
+
| ------------------------------------- | --------------------------------------------------------- |
|
|
367
|
+
| Manual `result.usage` printing | `withAiTelemetry()` — captures via middleware |
|
|
368
|
+
| Custom `ai.tokens` attribute names | Use OTel gen-ai conventions (`gen_ai.usage.input_tokens`) |
|
|
369
|
+
| Tool calls as plain log lines | Each tool call gets a child span automatically |
|
|
370
|
+
| No retry / partial-failure visibility | `experimental_telemetry: { isEnabled: true }` flips it on |
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## Structured errors
|
|
375
|
+
|
|
376
|
+
Throw rich errors that carry status, audience, and remediation hints — and consume them at HTTP boundaries:
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
import { createStructuredError, parseError } from 'autotel';
|
|
380
|
+
|
|
381
|
+
throw createStructuredError({
|
|
382
|
+
message: 'Payment declined',
|
|
383
|
+
status: 402,
|
|
384
|
+
why: 'Card declined by issuer — insufficient funds',
|
|
385
|
+
fix: 'Use a different payment method or contact your bank',
|
|
386
|
+
link: 'https://docs.example.com/payments/declined',
|
|
387
|
+
internal: { correlationId: 'req_abc', resourceId: 'cust_123' },
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// at the HTTP boundary
|
|
391
|
+
app.onError((error, c) => {
|
|
392
|
+
const parsed = parseError(error);
|
|
393
|
+
// `internal` is stripped from `parsed` — never returned to clients
|
|
394
|
+
return c.json(parsed, parsed.status);
|
|
395
|
+
});
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
`createStructuredError` records the error onto the active span automatically (`exception.type`, `exception.message`, `exception.stacktrace`) and sets `span.status = ERROR`.
|
|
399
|
+
|
|
400
|
+
See [references/structured-errors.md](references/structured-errors.md) for templates.
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
## Testing your instrumentation
|
|
405
|
+
|
|
406
|
+
### Unit tests (in-memory exporter)
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
import { InMemorySpanExporter } from 'autotel/exporters';
|
|
410
|
+
import { SimpleSpanProcessor } from 'autotel/processors';
|
|
411
|
+
import { init, trace } from 'autotel';
|
|
412
|
+
|
|
413
|
+
const exporter = new InMemorySpanExporter();
|
|
414
|
+
init({ service: 'test', spanProcessors: [new SimpleSpanProcessor(exporter)] });
|
|
415
|
+
|
|
416
|
+
await trace(async () => {
|
|
417
|
+
// … code under test
|
|
418
|
+
})();
|
|
419
|
+
|
|
420
|
+
const spans = exporter.getFinishedSpans();
|
|
421
|
+
expect(spans).toContainSpan({
|
|
422
|
+
name: 'processOrder',
|
|
423
|
+
attributes: { 'order.id': '123' },
|
|
424
|
+
});
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
`autotel-vitest` ships a custom matcher (`toContainSpan`) and a `withSpans()` helper.
|
|
428
|
+
|
|
429
|
+
### End-to-end (real OTLP backend)
|
|
430
|
+
|
|
431
|
+
`packages/autotel/test/e2e/` ships a working OTLP HTTP/JSON smoke test that tags every span with `e2e_run_id` / `e2e_correlation_id` for cleanup, skips gracefully when env vars are missing, and is wired up to a daily GitHub Actions cron. Copy it to test against your own backend (Honeycomb, Grafana Cloud, Datadog, …):
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
pnpm --filter autotel run test:e2e
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Bundle size guard
|
|
438
|
+
|
|
439
|
+
`scripts/check-bundle-size.mjs` measures every `packages/autotel*/dist` against `bundle-size-baseline.json` and fails CI on growth past 5 % / 2 KiB. Update the baseline only when the growth is intentional.
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## Anti-patterns to detect
|
|
444
|
+
|
|
445
|
+
| Anti-pattern | Fix |
|
|
446
|
+
| --------------------------------------------------- | ------------------------------------------------------------------- |
|
|
447
|
+
| `console.log` in handlers | Use `useLogger()` — fields land on the active span |
|
|
448
|
+
| Manual `tracer.startSpan` boilerplate | `trace(fn)` — auto-named, auto-ended, auto-status |
|
|
449
|
+
| `try { … } catch (e) { console.error(e); throw e }` | Replace with `createStructuredError({ … })` |
|
|
450
|
+
| `throw new Error('something went wrong')` | `createStructuredError({ message, status, why, fix })` |
|
|
451
|
+
| Ad-hoc `span.setAttribute('user_id', id)` | Use `useLogger().set({ user: { id } })` — flattens with stable keys |
|
|
452
|
+
| Multiple exporters wired in parallel by hand | `composeSpanProcessors([…])` |
|
|
453
|
+
| PII in attributes | `attributeRedactor: 'default'` (on in prod by default) |
|
|
454
|
+
| Cloudflare Workers without `waitUntil` | Use `defineWorkerFetch` / `wrapModule` |
|
|
455
|
+
| High-cardinality span names (`/users/123`) | `SpanNameNormalizingProcessor` |
|
|
456
|
+
| AI SDK token logs | `withAiTelemetry()` + gen-ai semantic conventions |
|
|
457
|
+
| Health checks blowing up trace volume | `FilteringSpanProcessor` |
|
|
458
|
+
| No tests for instrumentation | `InMemorySpanExporter` + `autotel-vitest` matchers |
|
|
459
|
+
| Manual context propagation in fetch | `instrumentation.instrumentGlobalFetch: true` (default) |
|
|
460
|
+
|
|
461
|
+
See [references/code-review.md](references/code-review.md) for the full checklist.
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Why autotel beats manually-wired OTel
|
|
466
|
+
|
|
467
|
+
| Concern | Plain `@opentelemetry/sdk-node` | autotel |
|
|
468
|
+
| --------------------- | ---------------------------------------------------- | ----------------------------------------------------- |
|
|
469
|
+
| Setup | Multi-page checklist, vendor-specific resource attrs | One `init()` call, sane defaults |
|
|
470
|
+
| Cloudflare Workers | DIY `waitUntil` (logs/spans drop silently if missed) | `defineWorkerFetch` auto-wires it |
|
|
471
|
+
| PII redaction | DIY span processor | `attributeRedactor: 'default'` (smart masks built in) |
|
|
472
|
+
| High-cardinality URLs | DIY span name munging | `SpanNameNormalizingProcessor` |
|
|
473
|
+
| Multi-backend | Hand-write a tee processor | `composeSpanProcessors([…])` |
|
|
474
|
+
| Local debugging | Manual `ConsoleSpanExporter` plumbing | `init({ debug: 'pretty' })` |
|
|
475
|
+
| AI SDK | Custom attributes, vendor-specific dashboards | OTel gen-ai semconv out of the box |
|
|
476
|
+
| Bundle size | Unbounded | CI guard with `bundle-size-baseline.json` |
|
|
477
|
+
| Real-backend tests | DIY | `pnpm test:e2e` ships a working OTLP smoke test |
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## Loading reference files
|
|
482
|
+
|
|
483
|
+
Load based on what you're working on — **do not load all at once**:
|
|
484
|
+
|
|
485
|
+
- Designing spans → [references/wide-spans.md](references/wide-spans.md)
|
|
486
|
+
- Improving errors → [references/structured-errors.md](references/structured-errors.md)
|
|
487
|
+
- Full code review → [references/code-review.md](references/code-review.md)
|
|
488
|
+
- Pipeline / processors → [references/processor-pipeline.md](references/processor-pipeline.md)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# OpenTelemetry code review checklist
|
|
2
|
+
|
|
3
|
+
Run through this list when adding observability to a new service or auditing an existing one.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
- [ ] **Service name set.** `service.name` resource attribute is unique per deployable unit (not just `"app"`).
|
|
8
|
+
- [ ] **OTLP endpoint configured** via `OTLP_ENDPOINT` env var. Don't hard-code per-vendor URLs.
|
|
9
|
+
- [ ] **No global SDK init in handlers.** Init runs once at module load (Next.js `instrumentation.ts`, Workers top of module, Node `--require`).
|
|
10
|
+
- [ ] **Production redaction on.** `attributeRedactor: 'default'` (or `'strict'` / `'pci-dss'`) — autotel turns it on automatically when `NODE_ENV === 'production'`.
|
|
11
|
+
- [ ] **Sampling configured.** Don't ship 100% in production unless volume is genuinely tiny. `sampling.rates: { server: 25 }` is a reasonable starting point.
|
|
12
|
+
|
|
13
|
+
## Spans
|
|
14
|
+
|
|
15
|
+
- [ ] **One wide span per logical unit of work.** Avoid hundreds of trivial spans.
|
|
16
|
+
- [ ] **Span names are low-cardinality.** `/users/:id`, not `/users/123`. Use `SpanNameNormalizingProcessor` if necessary.
|
|
17
|
+
- [ ] **No raw bodies in attributes.** Pick fields explicitly: `{ user: { id, plan } }` not `{ user: requestBody }`.
|
|
18
|
+
- [ ] **Status is set on errors.** `createStructuredError` does it; manual throws should set `span.status = ERROR`.
|
|
19
|
+
- [ ] **Context propagation works end-to-end.** W3C `traceparent` headers on outbound requests (autotel's global fetch instrumentation handles this).
|
|
20
|
+
- [ ] **Cross-service propagation works.** Browser → server, server → service, server → queue worker.
|
|
21
|
+
|
|
22
|
+
## Cloudflare Workers
|
|
23
|
+
|
|
24
|
+
- [ ] **`defineWorkerFetch` (or `wrapModule`).** Async drains drop silently if `ctx.waitUntil` isn't wired.
|
|
25
|
+
- [ ] **No SDK calls inside `addEventListener`.** Use the module worker style.
|
|
26
|
+
- [ ] **Bindings instrumented.** `instrumentBindings: true` to get spans for KV / R2 / D1 / Service Bindings.
|
|
27
|
+
- [ ] **Durable Objects wrapped.** `wrapDurableObject` for transactional state.
|
|
28
|
+
|
|
29
|
+
## Logs (structured events)
|
|
30
|
+
|
|
31
|
+
- [ ] **`useLogger().set({ … })` instead of `console.log`.** Logger fields land on the active span automatically.
|
|
32
|
+
- [ ] **No PII in attributes** (or rely on redactor). Don't log raw email / cards / phones / JWTs.
|
|
33
|
+
- [ ] **Group fields with objects.** `{ user: { id, plan } }`, not flat `userId` / `userPlan`.
|
|
34
|
+
- [ ] **Decisions captured.** Which branch, which fallback, which feature flag — not just inputs.
|
|
35
|
+
- [ ] **Background work uses `log.fork()`** — gets its own span and `_parentCorrelationId` for correlation.
|
|
36
|
+
|
|
37
|
+
## Errors
|
|
38
|
+
|
|
39
|
+
- [ ] **`createStructuredError({ message, status, why, fix })`.** No bare `new Error('…')`.
|
|
40
|
+
- [ ] **`parseError()` at HTTP boundaries.** `internal` is stripped; clients only see safe fields.
|
|
41
|
+
- [ ] **Validation errors include `details`.** Per-field problems for the client to render.
|
|
42
|
+
- [ ] **No double-logging.** Don't `console.error(e); throw e`; the span will pick it up.
|
|
43
|
+
|
|
44
|
+
## Metrics
|
|
45
|
+
|
|
46
|
+
- [ ] **Use OTel meter API**, not service-specific clients.
|
|
47
|
+
- [ ] **Counters for events, histograms for durations, gauges for snapshots** — match the right type.
|
|
48
|
+
- [ ] **Bounded label cardinality.** Don't put `userId` on a metric label.
|
|
49
|
+
|
|
50
|
+
## AI / LLM
|
|
51
|
+
|
|
52
|
+
- [ ] **`withAiTelemetry()` from `autotel-edge`.** Captures `gen_ai.*` semantic attributes automatically.
|
|
53
|
+
- [ ] **No bespoke `ai.tokens` attributes.** Use `gen_ai.usage.input_tokens`, etc.
|
|
54
|
+
- [ ] **Cost tracking via the `cost` option** — outputs `gen_ai.cost.usd` on the span.
|
|
55
|
+
- [ ] **Tool-call spans enabled.** `experimental_telemetry: { isEnabled: true }`.
|
|
56
|
+
|
|
57
|
+
## Testing
|
|
58
|
+
|
|
59
|
+
- [ ] **`InMemorySpanExporter` in unit tests.** Assert spans + attributes.
|
|
60
|
+
- [ ] **`autotel-vitest` matchers** (`toContainSpan`, `withSpans()`).
|
|
61
|
+
- [ ] **One e2e smoke test against a real OTLP backend.** `pnpm test:e2e` template ships in `packages/autotel/test/e2e/`.
|
|
62
|
+
- [ ] **Bundle-size baseline.** `pnpm bundle-size` runs on PRs; commit the baseline only on intentional growth.
|
|
63
|
+
|
|
64
|
+
## Pipeline / processors
|
|
65
|
+
|
|
66
|
+
- [ ] **One source of composition.** `composeSpanProcessors` / `composeSubscribers` / `composePostProcessors` over hand-rolled tee logic.
|
|
67
|
+
- [ ] **Tail sampling for noisy services.** Keep errors + slow + a sampled %.
|
|
68
|
+
- [ ] **Filter health checks.** `FilteringSpanProcessor({ exclude: ['/healthz', '/ready'] })`.
|
|
69
|
+
- [ ] **Baggage for cross-cutting context.** `BaggageSpanProcessor` lifts `feature_flags`, `tenant`, etc. onto every span automatically.
|
|
70
|
+
|
|
71
|
+
## Secrets
|
|
72
|
+
|
|
73
|
+
- [ ] **No exporter tokens in client bundles.** Server-only env vars.
|
|
74
|
+
- [ ] **No span attributes containing secrets.** Even with redaction on, prefer not to capture them at all.
|
|
75
|
+
- [ ] **CI runs e2e with secret-protected workflow** (label `e2e`, never on fork PRs).
|