@autter/runtime-node 1.0.0 → 1.2.0
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 +188 -1
- package/dist/index.cjs +1126 -28
- package/dist/index.d.cts +283 -2
- package/dist/index.d.ts +283 -2
- package/dist/index.js +1133 -30
- package/package.json +4 -2
package/dist/index.d.cts
CHANGED
|
@@ -43,13 +43,52 @@ declare function createBrowserRelayHandler(opts: RelayOptions): (req: IncomingMe
|
|
|
43
43
|
body?: unknown;
|
|
44
44
|
}, res: ServerResponse) => void;
|
|
45
45
|
|
|
46
|
+
interface RedactOptions {
|
|
47
|
+
/**
|
|
48
|
+
* Extra patterns matched against lower-cased attribute KEYS; a match
|
|
49
|
+
* masks the whole value. Strings become case-insensitive substring
|
|
50
|
+
* patterns. Extends the built-in list.
|
|
51
|
+
*/
|
|
52
|
+
additionalKeyPatterns?: (RegExp | string)[];
|
|
53
|
+
/**
|
|
54
|
+
* Extra patterns scrubbed inside string VALUES (same semantics as the
|
|
55
|
+
* built-in email/token patterns). Extends the built-in list.
|
|
56
|
+
*/
|
|
57
|
+
additionalValuePatterns?: (RegExp | string)[];
|
|
58
|
+
/** Replacement token. Default "[redacted]". */
|
|
59
|
+
mask?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Scrub email-shaped substrings from ALL string values, not just
|
|
62
|
+
* sensitive keys. Default true.
|
|
63
|
+
*/
|
|
64
|
+
scrubEmailValues?: boolean;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Return a copy of `attributes` with PII/secrets masked. Never mutates the
|
|
68
|
+
* input; non-string primitives pass through untouched; `undefined` values
|
|
69
|
+
* are dropped (OpenTelemetry rejects them).
|
|
70
|
+
*/
|
|
71
|
+
declare function redactAttributes(attributes?: Attributes | null, options?: RedactOptions): Attributes;
|
|
72
|
+
|
|
46
73
|
/**
|
|
47
74
|
* Curated OpenTelemetry setup for Autter Runtime — deliberately NOT the
|
|
48
75
|
* auto-instrumentation metapackage. Default data-volume policy:
|
|
49
76
|
*
|
|
50
77
|
* errors / captured exceptions : 100% (dedicated always-on error tracer)
|
|
78
|
+
* traces containing an error : 100% (tail retention — the full trace is
|
|
79
|
+
* exported when any of its spans
|
|
80
|
+
* errors, so a retained error keeps
|
|
81
|
+
* the trace that explains it)
|
|
82
|
+
* named process spans : 100% (withProcessSpan, always-on tracer)
|
|
83
|
+
* LLM / GenAI call spans : 100% (withLlmCall/trackLlmCall/
|
|
84
|
+
* instrumentLlmClient + an LLM-aware
|
|
85
|
+
* sampler that keeps GenAI/Vercel-AI
|
|
86
|
+
* spans — cost tracking needs every
|
|
87
|
+
* call)
|
|
51
88
|
* successful traces : 1% (head sampling, configurable)
|
|
52
|
-
* request metrics : aggregated every 60 s
|
|
89
|
+
* request metrics : aggregated every 60 s, keyed by route
|
|
90
|
+
* template (Express routes detected
|
|
91
|
+
* out of the box — see captureExpressRoute)
|
|
53
92
|
* logs : not collected
|
|
54
93
|
*
|
|
55
94
|
* Exports OTLP/HTTP JSON to the Autter ingester (`/v1/traces`,
|
|
@@ -66,14 +105,97 @@ interface AutterServerOptions {
|
|
|
66
105
|
release?: string;
|
|
67
106
|
/** Head-sampling ratio for regular traces. Default 0.01 (1%). */
|
|
68
107
|
traceSampleRate?: number;
|
|
108
|
+
/**
|
|
109
|
+
* Keep the FULL trace whenever it contains an error: an ERROR-status
|
|
110
|
+
* span (e.g. a 5xx request), a recorded exception, or a
|
|
111
|
+
* `captureException` / error-severity `captureMessage` call made inside
|
|
112
|
+
* it. Head sampling still decides what is exported for healthy traffic;
|
|
113
|
+
* erroring traces are rescued from the unsampled pool by a bounded
|
|
114
|
+
* in-process buffer — without this, errors are retained at 100% while
|
|
115
|
+
* the trace explaining them survives only `traceSampleRate` of the time.
|
|
116
|
+
* Default true.
|
|
117
|
+
*/
|
|
118
|
+
retainTracesOnError?: boolean;
|
|
69
119
|
/** Metric export interval. Default 60_000 ms. */
|
|
70
120
|
metricIntervalMs?: number;
|
|
71
121
|
/** Capture crashing exceptions via process.uncaughtExceptionMonitor (default true). */
|
|
72
122
|
captureGlobalErrors?: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* Flush all exporters when the process exits (`beforeExit`, SIGINT,
|
|
125
|
+
* SIGTERM) so a forgotten `shutdown()` doesn't silently drop buffered
|
|
126
|
+
* telemetry. If your code also handles the signal, Autter only flushes
|
|
127
|
+
* alongside it and never changes your exit path. Default true.
|
|
128
|
+
*/
|
|
129
|
+
autoFlush?: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Mask PII/secrets in custom attributes before they leave the process:
|
|
132
|
+
* emails, tokens (JWT, sk-, gh_, AWS, Slack), bearer headers,
|
|
133
|
+
* `scheme://user:pass@` URLs, and anything whose attribute KEY looks
|
|
134
|
+
* sensitive (password/token/secret/cookie/…). Mirrors the browser
|
|
135
|
+
* relay's whitelist sanitiser. `true`/undefined = defaults; pass a
|
|
136
|
+
* RedactOptions object to extend the patterns; `false` disables.
|
|
137
|
+
* Default true.
|
|
138
|
+
*/
|
|
139
|
+
redactAttributes?: boolean | RedactOptions;
|
|
140
|
+
/**
|
|
141
|
+
* Verbose lifecycle logging to stderr: "exported N spans", flushes and
|
|
142
|
+
* auto-flush activity — handy while wiring the SDK up. Also enabled by
|
|
143
|
+
* the AUTTER_DEBUG=1 environment variable. Default false.
|
|
144
|
+
*/
|
|
145
|
+
debug?: boolean;
|
|
146
|
+
/**
|
|
147
|
+
* Record LLM/GenAI spans (`gen_ai.*` semconv, Vercel AI SDK `ai.*`,
|
|
148
|
+
* `withLlmCall`) at 100% regardless of `traceSampleRate`, so every model
|
|
149
|
+
* call is tracked with tokens/cost (default true).
|
|
150
|
+
*/
|
|
151
|
+
llmTracing?: boolean;
|
|
73
152
|
/** Extra instrumentations (e.g. `new ExpressInstrumentation()`). */
|
|
74
153
|
instrumentations?: unknown[];
|
|
75
154
|
}
|
|
76
155
|
type AutterSeverity = "fatal" | "error" | "warning" | "info";
|
|
156
|
+
/** Token usage (and optionally exact cost) of one LLM call. */
|
|
157
|
+
interface LlmUsage {
|
|
158
|
+
inputTokens?: number;
|
|
159
|
+
outputTokens?: number;
|
|
160
|
+
/**
|
|
161
|
+
* Exact cost in USD, if the provider reports it. Overrides Autter's
|
|
162
|
+
* built-in per-model price estimate.
|
|
163
|
+
*/
|
|
164
|
+
costUsd?: number;
|
|
165
|
+
}
|
|
166
|
+
interface LlmCallInfo {
|
|
167
|
+
/** Provider system: "openai", "anthropic", "google", "mistral_ai", … */
|
|
168
|
+
provider: string;
|
|
169
|
+
/** Requested model id, e.g. "gpt-5-mini" or "claude-sonnet-4". */
|
|
170
|
+
model: string;
|
|
171
|
+
/** "chat" (default), "embeddings", "text_completion", … */
|
|
172
|
+
operation?: string;
|
|
173
|
+
/** Opaque end-user id for per-user cost attribution — never an email. */
|
|
174
|
+
userId?: string;
|
|
175
|
+
sessionId?: string;
|
|
176
|
+
/** Extra span attributes (kept under the gen_ai./autter.llm. families). */
|
|
177
|
+
attributes?: Attributes;
|
|
178
|
+
}
|
|
179
|
+
/** Reporting surface handed to the `withLlmCall` callback. */
|
|
180
|
+
interface LlmCallHandle {
|
|
181
|
+
/** Report token usage / exact cost from the provider response. */
|
|
182
|
+
setUsage(usage: LlmUsage): void;
|
|
183
|
+
/** Exact cost in USD; without it the ingester estimates from its price table. */
|
|
184
|
+
setCost(usd: number): void;
|
|
185
|
+
/** Record the model the provider actually served (gen_ai.response.model). */
|
|
186
|
+
setResponseModel(model: string): void;
|
|
187
|
+
/** Alias of setResponseModel. */
|
|
188
|
+
setModel(model: string): void;
|
|
189
|
+
/** Extra span attributes reported mid-call. */
|
|
190
|
+
setAttributes(attributes: Attributes): void;
|
|
191
|
+
}
|
|
192
|
+
/** A completed LLM call reported after the fact via `trackLlmCall`. */
|
|
193
|
+
interface TrackedLlmCall extends LlmCallInfo, LlmUsage {
|
|
194
|
+
/** Wall-clock duration of the call, if measured. */
|
|
195
|
+
durationMs?: number;
|
|
196
|
+
/** Pass the thrown error to record the call as failed. */
|
|
197
|
+
error?: unknown;
|
|
198
|
+
}
|
|
77
199
|
interface AutterServer {
|
|
78
200
|
/** Report a handled exception. Always recorded (never sampled out). */
|
|
79
201
|
captureException(error: unknown, attributes?: Attributes): void;
|
|
@@ -83,6 +205,41 @@ interface AutterServer {
|
|
|
83
205
|
* errors with a lower severity so aggregations can slice one dataset.
|
|
84
206
|
*/
|
|
85
207
|
captureMessage(message: string, severity?: AutterSeverity, attributes?: Attributes): void;
|
|
208
|
+
/**
|
|
209
|
+
* Wrap a named unit of work — a background job, queue consumer, cron
|
|
210
|
+
* tick, or DB-heavy call — in an always-recorded span. Unlike regular
|
|
211
|
+
* traces these are never head-sampled, so Autter's slow-process monitor
|
|
212
|
+
* sees accurate run counts and durations for non-HTTP work. Nested
|
|
213
|
+
* instrumented calls run inside the span's context, so slow traces show
|
|
214
|
+
* where the time went. Rethrows whatever `fn` throws.
|
|
215
|
+
*/
|
|
216
|
+
withProcessSpan<T>(name: string, fn: () => T | Promise<T>, attributes?: Attributes): Promise<T>;
|
|
217
|
+
/**
|
|
218
|
+
* Wrap one LLM provider call. Always recorded (never head-sampled), so
|
|
219
|
+
* Autter's LLM usage/cost dashboards and anomaly monitor see every call.
|
|
220
|
+
* Report tokens via the handle once the response arrives; cost is
|
|
221
|
+
* estimated from the model id unless `setCost(usd)` (or
|
|
222
|
+
* `setUsage({ costUsd })`) reports the exact figure. Rethrows whatever
|
|
223
|
+
* `fn` throws (failed calls are recorded with the error attached).
|
|
224
|
+
*
|
|
225
|
+
* const reply = await autter.withLlmCall(
|
|
226
|
+
* { provider: "openai", model: "gpt-5-mini", userId: user.id },
|
|
227
|
+
* async (llm) => {
|
|
228
|
+
* const res = await openai.chat.completions.create({ ... });
|
|
229
|
+
* llm.setUsage({
|
|
230
|
+
* inputTokens: res.usage?.prompt_tokens,
|
|
231
|
+
* outputTokens: res.usage?.completion_tokens,
|
|
232
|
+
* });
|
|
233
|
+
* return res;
|
|
234
|
+
* },
|
|
235
|
+
* );
|
|
236
|
+
*/
|
|
237
|
+
withLlmCall<T>(info: LlmCallInfo, fn: (llm: LlmCallHandle) => T | Promise<T>): Promise<T>;
|
|
238
|
+
/**
|
|
239
|
+
* Report an already-completed LLM call — for codepaths where wrapping is
|
|
240
|
+
* awkward (queues, callbacks, batch results). Fire-and-forget.
|
|
241
|
+
*/
|
|
242
|
+
trackLlmCall(call: TrackedLlmCall): void;
|
|
86
243
|
/** Flush and stop all exporters. Call on graceful shutdown. */
|
|
87
244
|
shutdown(): Promise<void>;
|
|
88
245
|
}
|
|
@@ -90,7 +247,131 @@ declare function initAutterServer(options: AutterServerOptions): AutterServer;
|
|
|
90
247
|
/** Module-level convenience — routes to the active server, or degrades to a
|
|
91
248
|
* span on the global tracer so libraries can call it unconditionally. */
|
|
92
249
|
declare function captureException(error: unknown, attributes?: Attributes): void;
|
|
250
|
+
/**
|
|
251
|
+
* Module-level convenience — see AutterServer.withProcessSpan. Before
|
|
252
|
+
* initAutterServer runs it degrades to the global tracer, where the span is
|
|
253
|
+
* subject to that provider's own sampling instead of the always-on pipe.
|
|
254
|
+
*/
|
|
255
|
+
declare function withProcessSpan<T>(name: string, fn: () => T | Promise<T>, attributes?: Attributes): Promise<T>;
|
|
256
|
+
/**
|
|
257
|
+
* Module-level convenience — see AutterServer.withLlmCall. Before
|
|
258
|
+
* initAutterServer runs it degrades to the global tracer, where the span is
|
|
259
|
+
* subject to that provider's own sampling instead of the always-on pipe.
|
|
260
|
+
*/
|
|
261
|
+
declare function withLlmCall<T>(info: LlmCallInfo, fn: (llm: LlmCallHandle) => T | Promise<T>): Promise<T>;
|
|
262
|
+
/** Module-level convenience — see AutterServer.trackLlmCall. */
|
|
263
|
+
declare function trackLlmCall(call: TrackedLlmCall): void;
|
|
93
264
|
/** Module-level convenience for warnings/info — see AutterServer.captureMessage. */
|
|
94
265
|
declare function captureMessage(message: string, severity?: AutterSeverity, attributes?: Attributes): void;
|
|
266
|
+
/** Capture surface with guaranteed redaction, regardless of how the host app
|
|
267
|
+
* configured initAutterServer — for library authors whose calls must never
|
|
268
|
+
* forward PII even when the host disabled it. Routes to the active server
|
|
269
|
+
* (or degrades to the global tracer exactly like captureException). */
|
|
270
|
+
interface SafeCapture {
|
|
271
|
+
captureException(error: unknown, attributes?: Attributes): void;
|
|
272
|
+
captureMessage(message: string, severity?: AutterSeverity, attributes?: Attributes): void;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Build a redacting capture wrapper:
|
|
276
|
+
*
|
|
277
|
+
* const safe = makeSafeCapture();
|
|
278
|
+
* safe.captureException(err, { "user.email": email }); // masked before export
|
|
279
|
+
*
|
|
280
|
+
* The returned functions have the same signatures as their plain twins;
|
|
281
|
+
* every attribute passes through redactAttributes() first. Note that when
|
|
282
|
+
* the host has NOT disabled redaction this is belt-and-braces (already on by
|
|
283
|
+
* default) — its value is enforcing privacy inside libraries and wrappers.
|
|
284
|
+
*/
|
|
285
|
+
declare function makeSafeCapture(options?: RedactOptions): SafeCapture;
|
|
286
|
+
/**
|
|
287
|
+
* Emit one clearly-named fake LLM call (provider/model "autter-selftest",
|
|
288
|
+
* 1 input + 1 output token, cost 0) and force-flush it, returning the
|
|
289
|
+
* traceId — proves LLM traces reach the ingester without calling any real
|
|
290
|
+
* model. For setup verification only; requires initAutterServer.
|
|
291
|
+
*/
|
|
292
|
+
declare function emitLlmSelftestTrace(): Promise<{
|
|
293
|
+
traceId: string;
|
|
294
|
+
}>;
|
|
295
|
+
|
|
296
|
+
interface AutterLogger {
|
|
297
|
+
log: (...args: unknown[]) => void;
|
|
298
|
+
warn: (...args: unknown[]) => void;
|
|
299
|
+
error: (...args: unknown[]) => void;
|
|
300
|
+
}
|
|
301
|
+
interface FlushTarget {
|
|
302
|
+
forceFlush(): Promise<unknown> | unknown;
|
|
303
|
+
}
|
|
304
|
+
interface AutoFlushOptions {
|
|
305
|
+
/**
|
|
306
|
+
* Targets to flush. Defaults to whatever initAutterServer has registered;
|
|
307
|
+
* pass your own for custom pipelines.
|
|
308
|
+
*/
|
|
309
|
+
targets?: FlushTarget[];
|
|
310
|
+
/** Where lifecycle messages go. Default console. */
|
|
311
|
+
logger?: AutterLogger;
|
|
312
|
+
/** Log each successful flush ("flushed telemetry (SIGTERM)"). Default true. */
|
|
313
|
+
log?: boolean;
|
|
314
|
+
/**
|
|
315
|
+
* Warn on stderr when the process exits with captures that were never
|
|
316
|
+
* confirmed exported. Default true.
|
|
317
|
+
*/
|
|
318
|
+
warnOnUnflushedExit?: boolean;
|
|
319
|
+
/**
|
|
320
|
+
* Max wall-clock time a single flush may take before giving up (and, on
|
|
321
|
+
* signals, proceeding to exit anyway). Default 3000 ms.
|
|
322
|
+
*/
|
|
323
|
+
timeoutMs?: number;
|
|
324
|
+
/** Signals handled besides the defaults. Default ["SIGINT", "SIGTERM"]. */
|
|
325
|
+
signals?: string[];
|
|
326
|
+
}
|
|
327
|
+
interface AutoFlushHandle {
|
|
328
|
+
/** Force-flush now. Safe to call repeatedly; concurrent calls coalesce. */
|
|
329
|
+
flush(reason?: string): Promise<boolean>;
|
|
330
|
+
/** Remove every listener this install added. */
|
|
331
|
+
dispose(): void;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Flush all exporters when the process is going away:
|
|
335
|
+
*
|
|
336
|
+
* - `beforeExit` — event-loop drained naturally: force-flush, then hand the
|
|
337
|
+
* loop back (a kept-alive timer makes sure the async flush finishes
|
|
338
|
+
* inside the beforeExit window).
|
|
339
|
+
* - SIGINT / SIGTERM — flush, then exit with the conventional code (130 /
|
|
340
|
+
* 143). If YOUR code also listens to the signal, we only flush
|
|
341
|
+
* concurrently and let your handler decide the exit — installing this
|
|
342
|
+
* never changes an existing graceful-shutdown path. A second signal
|
|
343
|
+
* always exits immediately.
|
|
344
|
+
*
|
|
345
|
+
* Idempotent per options object; `initAutterServer` installs it by default
|
|
346
|
+
* (`autoFlush: false` opts out).
|
|
347
|
+
*/
|
|
348
|
+
declare function installAutterAutoFlush(options?: AutoFlushOptions): AutoFlushHandle;
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* One-line LLM client auto-instrumentation:
|
|
352
|
+
*
|
|
353
|
+
* const openai = instrumentLlmClient(new OpenAI());
|
|
354
|
+
*
|
|
355
|
+
* Wraps a provider SDK instance (OpenAI, Anthropic, Google GenAI, or any
|
|
356
|
+
* client with the same call shapes) in a deep proxy. Every model invocation —
|
|
357
|
+
* a method like `create`/`stream`/`generateContent` called with an object
|
|
358
|
+
* argument carrying a `model` — is recorded as a gen_ai span on the
|
|
359
|
+
* always-recorded pipe: model, tokens, latency, errors, and (ingester-side)
|
|
360
|
+
* USD cost. Streaming responses are supported: the span stays open until the
|
|
361
|
+
* stream is consumed, picking up usage from the final chunks.
|
|
362
|
+
*
|
|
363
|
+
* Nothing about the client's behaviour changes; methods run with the
|
|
364
|
+
* original `this` (SDK private fields keep working) and results pass
|
|
365
|
+
* through untouched apart from the stream wrapper.
|
|
366
|
+
*/
|
|
367
|
+
interface InstrumentLlmOptions {
|
|
368
|
+
/** Provider id ("openai", "anthropic", …). Default: detected from the client. */
|
|
369
|
+
provider?: string;
|
|
370
|
+
/** Opaque end-user id (never an email) — static or resolved per call. */
|
|
371
|
+
userId?: string | (() => string | undefined);
|
|
372
|
+
/** Extra attributes for every span from this client. */
|
|
373
|
+
attributes?: Attributes;
|
|
374
|
+
}
|
|
375
|
+
declare function instrumentLlmClient<T extends object>(client: T, options?: InstrumentLlmOptions): T;
|
|
95
376
|
|
|
96
|
-
export { type AutterServer, type AutterServerOptions, type AutterSeverity, type RelayOptions, captureException, captureMessage, createBrowserRelayFetchHandler, createBrowserRelayHandler, initAutterServer, sanitizeBrowserPayload };
|
|
377
|
+
export { type AutoFlushHandle, type AutoFlushOptions, type AutterServer, type AutterServerOptions, type AutterSeverity, type FlushTarget, type InstrumentLlmOptions, type LlmCallHandle, type LlmCallInfo, type LlmUsage, type RedactOptions, type RelayOptions, type SafeCapture, type TrackedLlmCall, captureException, captureMessage, createBrowserRelayFetchHandler, createBrowserRelayHandler, emitLlmSelftestTrace, initAutterServer, installAutterAutoFlush, instrumentLlmClient, makeSafeCapture, redactAttributes, sanitizeBrowserPayload, trackLlmCall, withLlmCall, withProcessSpan };
|
package/dist/index.d.ts
CHANGED
|
@@ -43,13 +43,52 @@ declare function createBrowserRelayHandler(opts: RelayOptions): (req: IncomingMe
|
|
|
43
43
|
body?: unknown;
|
|
44
44
|
}, res: ServerResponse) => void;
|
|
45
45
|
|
|
46
|
+
interface RedactOptions {
|
|
47
|
+
/**
|
|
48
|
+
* Extra patterns matched against lower-cased attribute KEYS; a match
|
|
49
|
+
* masks the whole value. Strings become case-insensitive substring
|
|
50
|
+
* patterns. Extends the built-in list.
|
|
51
|
+
*/
|
|
52
|
+
additionalKeyPatterns?: (RegExp | string)[];
|
|
53
|
+
/**
|
|
54
|
+
* Extra patterns scrubbed inside string VALUES (same semantics as the
|
|
55
|
+
* built-in email/token patterns). Extends the built-in list.
|
|
56
|
+
*/
|
|
57
|
+
additionalValuePatterns?: (RegExp | string)[];
|
|
58
|
+
/** Replacement token. Default "[redacted]". */
|
|
59
|
+
mask?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Scrub email-shaped substrings from ALL string values, not just
|
|
62
|
+
* sensitive keys. Default true.
|
|
63
|
+
*/
|
|
64
|
+
scrubEmailValues?: boolean;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Return a copy of `attributes` with PII/secrets masked. Never mutates the
|
|
68
|
+
* input; non-string primitives pass through untouched; `undefined` values
|
|
69
|
+
* are dropped (OpenTelemetry rejects them).
|
|
70
|
+
*/
|
|
71
|
+
declare function redactAttributes(attributes?: Attributes | null, options?: RedactOptions): Attributes;
|
|
72
|
+
|
|
46
73
|
/**
|
|
47
74
|
* Curated OpenTelemetry setup for Autter Runtime — deliberately NOT the
|
|
48
75
|
* auto-instrumentation metapackage. Default data-volume policy:
|
|
49
76
|
*
|
|
50
77
|
* errors / captured exceptions : 100% (dedicated always-on error tracer)
|
|
78
|
+
* traces containing an error : 100% (tail retention — the full trace is
|
|
79
|
+
* exported when any of its spans
|
|
80
|
+
* errors, so a retained error keeps
|
|
81
|
+
* the trace that explains it)
|
|
82
|
+
* named process spans : 100% (withProcessSpan, always-on tracer)
|
|
83
|
+
* LLM / GenAI call spans : 100% (withLlmCall/trackLlmCall/
|
|
84
|
+
* instrumentLlmClient + an LLM-aware
|
|
85
|
+
* sampler that keeps GenAI/Vercel-AI
|
|
86
|
+
* spans — cost tracking needs every
|
|
87
|
+
* call)
|
|
51
88
|
* successful traces : 1% (head sampling, configurable)
|
|
52
|
-
* request metrics : aggregated every 60 s
|
|
89
|
+
* request metrics : aggregated every 60 s, keyed by route
|
|
90
|
+
* template (Express routes detected
|
|
91
|
+
* out of the box — see captureExpressRoute)
|
|
53
92
|
* logs : not collected
|
|
54
93
|
*
|
|
55
94
|
* Exports OTLP/HTTP JSON to the Autter ingester (`/v1/traces`,
|
|
@@ -66,14 +105,97 @@ interface AutterServerOptions {
|
|
|
66
105
|
release?: string;
|
|
67
106
|
/** Head-sampling ratio for regular traces. Default 0.01 (1%). */
|
|
68
107
|
traceSampleRate?: number;
|
|
108
|
+
/**
|
|
109
|
+
* Keep the FULL trace whenever it contains an error: an ERROR-status
|
|
110
|
+
* span (e.g. a 5xx request), a recorded exception, or a
|
|
111
|
+
* `captureException` / error-severity `captureMessage` call made inside
|
|
112
|
+
* it. Head sampling still decides what is exported for healthy traffic;
|
|
113
|
+
* erroring traces are rescued from the unsampled pool by a bounded
|
|
114
|
+
* in-process buffer — without this, errors are retained at 100% while
|
|
115
|
+
* the trace explaining them survives only `traceSampleRate` of the time.
|
|
116
|
+
* Default true.
|
|
117
|
+
*/
|
|
118
|
+
retainTracesOnError?: boolean;
|
|
69
119
|
/** Metric export interval. Default 60_000 ms. */
|
|
70
120
|
metricIntervalMs?: number;
|
|
71
121
|
/** Capture crashing exceptions via process.uncaughtExceptionMonitor (default true). */
|
|
72
122
|
captureGlobalErrors?: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* Flush all exporters when the process exits (`beforeExit`, SIGINT,
|
|
125
|
+
* SIGTERM) so a forgotten `shutdown()` doesn't silently drop buffered
|
|
126
|
+
* telemetry. If your code also handles the signal, Autter only flushes
|
|
127
|
+
* alongside it and never changes your exit path. Default true.
|
|
128
|
+
*/
|
|
129
|
+
autoFlush?: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Mask PII/secrets in custom attributes before they leave the process:
|
|
132
|
+
* emails, tokens (JWT, sk-, gh_, AWS, Slack), bearer headers,
|
|
133
|
+
* `scheme://user:pass@` URLs, and anything whose attribute KEY looks
|
|
134
|
+
* sensitive (password/token/secret/cookie/…). Mirrors the browser
|
|
135
|
+
* relay's whitelist sanitiser. `true`/undefined = defaults; pass a
|
|
136
|
+
* RedactOptions object to extend the patterns; `false` disables.
|
|
137
|
+
* Default true.
|
|
138
|
+
*/
|
|
139
|
+
redactAttributes?: boolean | RedactOptions;
|
|
140
|
+
/**
|
|
141
|
+
* Verbose lifecycle logging to stderr: "exported N spans", flushes and
|
|
142
|
+
* auto-flush activity — handy while wiring the SDK up. Also enabled by
|
|
143
|
+
* the AUTTER_DEBUG=1 environment variable. Default false.
|
|
144
|
+
*/
|
|
145
|
+
debug?: boolean;
|
|
146
|
+
/**
|
|
147
|
+
* Record LLM/GenAI spans (`gen_ai.*` semconv, Vercel AI SDK `ai.*`,
|
|
148
|
+
* `withLlmCall`) at 100% regardless of `traceSampleRate`, so every model
|
|
149
|
+
* call is tracked with tokens/cost (default true).
|
|
150
|
+
*/
|
|
151
|
+
llmTracing?: boolean;
|
|
73
152
|
/** Extra instrumentations (e.g. `new ExpressInstrumentation()`). */
|
|
74
153
|
instrumentations?: unknown[];
|
|
75
154
|
}
|
|
76
155
|
type AutterSeverity = "fatal" | "error" | "warning" | "info";
|
|
156
|
+
/** Token usage (and optionally exact cost) of one LLM call. */
|
|
157
|
+
interface LlmUsage {
|
|
158
|
+
inputTokens?: number;
|
|
159
|
+
outputTokens?: number;
|
|
160
|
+
/**
|
|
161
|
+
* Exact cost in USD, if the provider reports it. Overrides Autter's
|
|
162
|
+
* built-in per-model price estimate.
|
|
163
|
+
*/
|
|
164
|
+
costUsd?: number;
|
|
165
|
+
}
|
|
166
|
+
interface LlmCallInfo {
|
|
167
|
+
/** Provider system: "openai", "anthropic", "google", "mistral_ai", … */
|
|
168
|
+
provider: string;
|
|
169
|
+
/** Requested model id, e.g. "gpt-5-mini" or "claude-sonnet-4". */
|
|
170
|
+
model: string;
|
|
171
|
+
/** "chat" (default), "embeddings", "text_completion", … */
|
|
172
|
+
operation?: string;
|
|
173
|
+
/** Opaque end-user id for per-user cost attribution — never an email. */
|
|
174
|
+
userId?: string;
|
|
175
|
+
sessionId?: string;
|
|
176
|
+
/** Extra span attributes (kept under the gen_ai./autter.llm. families). */
|
|
177
|
+
attributes?: Attributes;
|
|
178
|
+
}
|
|
179
|
+
/** Reporting surface handed to the `withLlmCall` callback. */
|
|
180
|
+
interface LlmCallHandle {
|
|
181
|
+
/** Report token usage / exact cost from the provider response. */
|
|
182
|
+
setUsage(usage: LlmUsage): void;
|
|
183
|
+
/** Exact cost in USD; without it the ingester estimates from its price table. */
|
|
184
|
+
setCost(usd: number): void;
|
|
185
|
+
/** Record the model the provider actually served (gen_ai.response.model). */
|
|
186
|
+
setResponseModel(model: string): void;
|
|
187
|
+
/** Alias of setResponseModel. */
|
|
188
|
+
setModel(model: string): void;
|
|
189
|
+
/** Extra span attributes reported mid-call. */
|
|
190
|
+
setAttributes(attributes: Attributes): void;
|
|
191
|
+
}
|
|
192
|
+
/** A completed LLM call reported after the fact via `trackLlmCall`. */
|
|
193
|
+
interface TrackedLlmCall extends LlmCallInfo, LlmUsage {
|
|
194
|
+
/** Wall-clock duration of the call, if measured. */
|
|
195
|
+
durationMs?: number;
|
|
196
|
+
/** Pass the thrown error to record the call as failed. */
|
|
197
|
+
error?: unknown;
|
|
198
|
+
}
|
|
77
199
|
interface AutterServer {
|
|
78
200
|
/** Report a handled exception. Always recorded (never sampled out). */
|
|
79
201
|
captureException(error: unknown, attributes?: Attributes): void;
|
|
@@ -83,6 +205,41 @@ interface AutterServer {
|
|
|
83
205
|
* errors with a lower severity so aggregations can slice one dataset.
|
|
84
206
|
*/
|
|
85
207
|
captureMessage(message: string, severity?: AutterSeverity, attributes?: Attributes): void;
|
|
208
|
+
/**
|
|
209
|
+
* Wrap a named unit of work — a background job, queue consumer, cron
|
|
210
|
+
* tick, or DB-heavy call — in an always-recorded span. Unlike regular
|
|
211
|
+
* traces these are never head-sampled, so Autter's slow-process monitor
|
|
212
|
+
* sees accurate run counts and durations for non-HTTP work. Nested
|
|
213
|
+
* instrumented calls run inside the span's context, so slow traces show
|
|
214
|
+
* where the time went. Rethrows whatever `fn` throws.
|
|
215
|
+
*/
|
|
216
|
+
withProcessSpan<T>(name: string, fn: () => T | Promise<T>, attributes?: Attributes): Promise<T>;
|
|
217
|
+
/**
|
|
218
|
+
* Wrap one LLM provider call. Always recorded (never head-sampled), so
|
|
219
|
+
* Autter's LLM usage/cost dashboards and anomaly monitor see every call.
|
|
220
|
+
* Report tokens via the handle once the response arrives; cost is
|
|
221
|
+
* estimated from the model id unless `setCost(usd)` (or
|
|
222
|
+
* `setUsage({ costUsd })`) reports the exact figure. Rethrows whatever
|
|
223
|
+
* `fn` throws (failed calls are recorded with the error attached).
|
|
224
|
+
*
|
|
225
|
+
* const reply = await autter.withLlmCall(
|
|
226
|
+
* { provider: "openai", model: "gpt-5-mini", userId: user.id },
|
|
227
|
+
* async (llm) => {
|
|
228
|
+
* const res = await openai.chat.completions.create({ ... });
|
|
229
|
+
* llm.setUsage({
|
|
230
|
+
* inputTokens: res.usage?.prompt_tokens,
|
|
231
|
+
* outputTokens: res.usage?.completion_tokens,
|
|
232
|
+
* });
|
|
233
|
+
* return res;
|
|
234
|
+
* },
|
|
235
|
+
* );
|
|
236
|
+
*/
|
|
237
|
+
withLlmCall<T>(info: LlmCallInfo, fn: (llm: LlmCallHandle) => T | Promise<T>): Promise<T>;
|
|
238
|
+
/**
|
|
239
|
+
* Report an already-completed LLM call — for codepaths where wrapping is
|
|
240
|
+
* awkward (queues, callbacks, batch results). Fire-and-forget.
|
|
241
|
+
*/
|
|
242
|
+
trackLlmCall(call: TrackedLlmCall): void;
|
|
86
243
|
/** Flush and stop all exporters. Call on graceful shutdown. */
|
|
87
244
|
shutdown(): Promise<void>;
|
|
88
245
|
}
|
|
@@ -90,7 +247,131 @@ declare function initAutterServer(options: AutterServerOptions): AutterServer;
|
|
|
90
247
|
/** Module-level convenience — routes to the active server, or degrades to a
|
|
91
248
|
* span on the global tracer so libraries can call it unconditionally. */
|
|
92
249
|
declare function captureException(error: unknown, attributes?: Attributes): void;
|
|
250
|
+
/**
|
|
251
|
+
* Module-level convenience — see AutterServer.withProcessSpan. Before
|
|
252
|
+
* initAutterServer runs it degrades to the global tracer, where the span is
|
|
253
|
+
* subject to that provider's own sampling instead of the always-on pipe.
|
|
254
|
+
*/
|
|
255
|
+
declare function withProcessSpan<T>(name: string, fn: () => T | Promise<T>, attributes?: Attributes): Promise<T>;
|
|
256
|
+
/**
|
|
257
|
+
* Module-level convenience — see AutterServer.withLlmCall. Before
|
|
258
|
+
* initAutterServer runs it degrades to the global tracer, where the span is
|
|
259
|
+
* subject to that provider's own sampling instead of the always-on pipe.
|
|
260
|
+
*/
|
|
261
|
+
declare function withLlmCall<T>(info: LlmCallInfo, fn: (llm: LlmCallHandle) => T | Promise<T>): Promise<T>;
|
|
262
|
+
/** Module-level convenience — see AutterServer.trackLlmCall. */
|
|
263
|
+
declare function trackLlmCall(call: TrackedLlmCall): void;
|
|
93
264
|
/** Module-level convenience for warnings/info — see AutterServer.captureMessage. */
|
|
94
265
|
declare function captureMessage(message: string, severity?: AutterSeverity, attributes?: Attributes): void;
|
|
266
|
+
/** Capture surface with guaranteed redaction, regardless of how the host app
|
|
267
|
+
* configured initAutterServer — for library authors whose calls must never
|
|
268
|
+
* forward PII even when the host disabled it. Routes to the active server
|
|
269
|
+
* (or degrades to the global tracer exactly like captureException). */
|
|
270
|
+
interface SafeCapture {
|
|
271
|
+
captureException(error: unknown, attributes?: Attributes): void;
|
|
272
|
+
captureMessage(message: string, severity?: AutterSeverity, attributes?: Attributes): void;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Build a redacting capture wrapper:
|
|
276
|
+
*
|
|
277
|
+
* const safe = makeSafeCapture();
|
|
278
|
+
* safe.captureException(err, { "user.email": email }); // masked before export
|
|
279
|
+
*
|
|
280
|
+
* The returned functions have the same signatures as their plain twins;
|
|
281
|
+
* every attribute passes through redactAttributes() first. Note that when
|
|
282
|
+
* the host has NOT disabled redaction this is belt-and-braces (already on by
|
|
283
|
+
* default) — its value is enforcing privacy inside libraries and wrappers.
|
|
284
|
+
*/
|
|
285
|
+
declare function makeSafeCapture(options?: RedactOptions): SafeCapture;
|
|
286
|
+
/**
|
|
287
|
+
* Emit one clearly-named fake LLM call (provider/model "autter-selftest",
|
|
288
|
+
* 1 input + 1 output token, cost 0) and force-flush it, returning the
|
|
289
|
+
* traceId — proves LLM traces reach the ingester without calling any real
|
|
290
|
+
* model. For setup verification only; requires initAutterServer.
|
|
291
|
+
*/
|
|
292
|
+
declare function emitLlmSelftestTrace(): Promise<{
|
|
293
|
+
traceId: string;
|
|
294
|
+
}>;
|
|
295
|
+
|
|
296
|
+
interface AutterLogger {
|
|
297
|
+
log: (...args: unknown[]) => void;
|
|
298
|
+
warn: (...args: unknown[]) => void;
|
|
299
|
+
error: (...args: unknown[]) => void;
|
|
300
|
+
}
|
|
301
|
+
interface FlushTarget {
|
|
302
|
+
forceFlush(): Promise<unknown> | unknown;
|
|
303
|
+
}
|
|
304
|
+
interface AutoFlushOptions {
|
|
305
|
+
/**
|
|
306
|
+
* Targets to flush. Defaults to whatever initAutterServer has registered;
|
|
307
|
+
* pass your own for custom pipelines.
|
|
308
|
+
*/
|
|
309
|
+
targets?: FlushTarget[];
|
|
310
|
+
/** Where lifecycle messages go. Default console. */
|
|
311
|
+
logger?: AutterLogger;
|
|
312
|
+
/** Log each successful flush ("flushed telemetry (SIGTERM)"). Default true. */
|
|
313
|
+
log?: boolean;
|
|
314
|
+
/**
|
|
315
|
+
* Warn on stderr when the process exits with captures that were never
|
|
316
|
+
* confirmed exported. Default true.
|
|
317
|
+
*/
|
|
318
|
+
warnOnUnflushedExit?: boolean;
|
|
319
|
+
/**
|
|
320
|
+
* Max wall-clock time a single flush may take before giving up (and, on
|
|
321
|
+
* signals, proceeding to exit anyway). Default 3000 ms.
|
|
322
|
+
*/
|
|
323
|
+
timeoutMs?: number;
|
|
324
|
+
/** Signals handled besides the defaults. Default ["SIGINT", "SIGTERM"]. */
|
|
325
|
+
signals?: string[];
|
|
326
|
+
}
|
|
327
|
+
interface AutoFlushHandle {
|
|
328
|
+
/** Force-flush now. Safe to call repeatedly; concurrent calls coalesce. */
|
|
329
|
+
flush(reason?: string): Promise<boolean>;
|
|
330
|
+
/** Remove every listener this install added. */
|
|
331
|
+
dispose(): void;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Flush all exporters when the process is going away:
|
|
335
|
+
*
|
|
336
|
+
* - `beforeExit` — event-loop drained naturally: force-flush, then hand the
|
|
337
|
+
* loop back (a kept-alive timer makes sure the async flush finishes
|
|
338
|
+
* inside the beforeExit window).
|
|
339
|
+
* - SIGINT / SIGTERM — flush, then exit with the conventional code (130 /
|
|
340
|
+
* 143). If YOUR code also listens to the signal, we only flush
|
|
341
|
+
* concurrently and let your handler decide the exit — installing this
|
|
342
|
+
* never changes an existing graceful-shutdown path. A second signal
|
|
343
|
+
* always exits immediately.
|
|
344
|
+
*
|
|
345
|
+
* Idempotent per options object; `initAutterServer` installs it by default
|
|
346
|
+
* (`autoFlush: false` opts out).
|
|
347
|
+
*/
|
|
348
|
+
declare function installAutterAutoFlush(options?: AutoFlushOptions): AutoFlushHandle;
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* One-line LLM client auto-instrumentation:
|
|
352
|
+
*
|
|
353
|
+
* const openai = instrumentLlmClient(new OpenAI());
|
|
354
|
+
*
|
|
355
|
+
* Wraps a provider SDK instance (OpenAI, Anthropic, Google GenAI, or any
|
|
356
|
+
* client with the same call shapes) in a deep proxy. Every model invocation —
|
|
357
|
+
* a method like `create`/`stream`/`generateContent` called with an object
|
|
358
|
+
* argument carrying a `model` — is recorded as a gen_ai span on the
|
|
359
|
+
* always-recorded pipe: model, tokens, latency, errors, and (ingester-side)
|
|
360
|
+
* USD cost. Streaming responses are supported: the span stays open until the
|
|
361
|
+
* stream is consumed, picking up usage from the final chunks.
|
|
362
|
+
*
|
|
363
|
+
* Nothing about the client's behaviour changes; methods run with the
|
|
364
|
+
* original `this` (SDK private fields keep working) and results pass
|
|
365
|
+
* through untouched apart from the stream wrapper.
|
|
366
|
+
*/
|
|
367
|
+
interface InstrumentLlmOptions {
|
|
368
|
+
/** Provider id ("openai", "anthropic", …). Default: detected from the client. */
|
|
369
|
+
provider?: string;
|
|
370
|
+
/** Opaque end-user id (never an email) — static or resolved per call. */
|
|
371
|
+
userId?: string | (() => string | undefined);
|
|
372
|
+
/** Extra attributes for every span from this client. */
|
|
373
|
+
attributes?: Attributes;
|
|
374
|
+
}
|
|
375
|
+
declare function instrumentLlmClient<T extends object>(client: T, options?: InstrumentLlmOptions): T;
|
|
95
376
|
|
|
96
|
-
export { type AutterServer, type AutterServerOptions, type AutterSeverity, type RelayOptions, captureException, captureMessage, createBrowserRelayFetchHandler, createBrowserRelayHandler, initAutterServer, sanitizeBrowserPayload };
|
|
377
|
+
export { type AutoFlushHandle, type AutoFlushOptions, type AutterServer, type AutterServerOptions, type AutterSeverity, type FlushTarget, type InstrumentLlmOptions, type LlmCallHandle, type LlmCallInfo, type LlmUsage, type RedactOptions, type RelayOptions, type SafeCapture, type TrackedLlmCall, captureException, captureMessage, createBrowserRelayFetchHandler, createBrowserRelayHandler, emitLlmSelftestTrace, initAutterServer, installAutterAutoFlush, instrumentLlmClient, makeSafeCapture, redactAttributes, sanitizeBrowserPayload, trackLlmCall, withLlmCall, withProcessSpan };
|