UNPKG

autotel

Version:
389 lines 13.4 kB
import { n as TraceContext } from "./trace-context-IuyoPyNq.js"; import { p as Sampler } from "./sampling-CP3e7-Yr.js"; import { Span, SpanKind } from "@opentelemetry/api"; //#region src/functional-wrapper.d.ts type WrappedFunction<TArgs extends unknown[], TReturn> = (...args: TArgs) => TReturn | Promise<TReturn>; /** * Constraint alias for `instrument()` and friends. `never[]` parameters make * every concretely-typed function satisfy the constraint under * `strictFunctionTypes` while preserving its inferred call signature. */ type AnyInstrumentable = ((...args: never[]) => unknown) & { displayName?: string; name?: string; }; /** Common options for functional tracing. */ interface TracingOptions<TArgs extends unknown[] = unknown[], TReturn = unknown> { /** * Span name (highest priority). * If provided, this is used as the span name. */ name?: string; /** * Service name used to compose `${serviceName}.${functionName}` when no * explicit name is provided. */ serviceName?: string; /** * Sampling strategy. * @default AlwaysSampler */ sampler?: Sampler; /** * Enable call and duration metrics. * @default false */ withMetrics?: boolean; /** Extract attributes from function arguments. */ attributesFromArgs?: (args: TArgs) => Record<string, unknown>; /** Extract attributes from the function result. */ attributesFromResult?: (result: TReturn) => Record<string, unknown>; /** * Capture arguments on the span as the truncated JSON `autotel.input` * attribute. One argument is captured directly; multiple arguments are an * array. Avoid this for secrets/PII, or pair it with a redacting processor. */ captureInput?: boolean; /** * Capture the result on the span as the truncated JSON `autotel.output` * attribute. The same sensitive-data caveats as {@link captureInput} apply. */ captureOutput?: boolean; /** * Start a new root span instead of creating a child. * Useful for serverless entry points. * @default false */ startNewRoot?: boolean; /** * Flush telemetry when a root span ends. * @default true */ flushOnRootSpanEnd?: boolean; /** * OpenTelemetry span kind for semantic convention compliance. * @default SpanKind.INTERNAL */ spanKind?: SpanKind; /** * Classify a thrown value as a real error. Returning false treats the throw * as expected control flow: the span is marked OK, no exception is recorded, * and the original value is rethrown. This supports framework signals such * as `redirect()` and `notFound()`. * @default every throw is treated as an error */ isError?: (error: unknown) => boolean; } /** Options for `instrument()` batch instrumentation. */ interface InstrumentOptions<T extends Record<string, AnyInstrumentable> = Record<string, AnyInstrumentable>> extends TracingOptions { /** Object whose function properties should be instrumented. */ functions: T; /** Per-function configuration overrides. */ overrides?: Record<string, Partial<TracingOptions>>; /** * Functions to skip. Supports string keys, regular expressions, and * predicates. Functions whose keys start with `_` are skipped by default. */ skip?: (string | RegExp | ((key: string, fn: Function) => boolean))[]; } /** Options for instrumenting one function with an explicit stable key. */ interface SingleInstrumentOptions<TFunction extends AnyInstrumentable = AnyInstrumentable> extends TracingOptions { /** Stable function key used for span naming. */ key: string; /** Function to instrument. */ fn: TFunction; } //#endregion //#region src/functional.d.ts /** * Get the autotel {@link TraceContext} for the currently active span. * * This is the ambient accessor for the functional API: instead of threading a * `ctx` parameter through a factory, call this inside any traced function (or a * helper it calls) to reach `setAttribute`, `setUser`, `getBaggage`, and the * rest of the context surface. Returns `undefined` when no span is active. * * @example * ```typescript * const getUser = trace('getUser', async (id: string) => { * getActiveTraceContext()?.setAttribute('user.id', id); * return db.users.find(id); * }); * ``` * * @see getActiveSpan for the raw OpenTelemetry span * @see getRequestLogger which reads the active context when called with no args */ declare function getActiveTraceContext<TBaggage extends Record<string, unknown> | undefined = undefined>(): TraceContext<TBaggage> | undefined; /** * Context object that lazily evaluates the active span on property access * * Access trace context directly without function call syntax. * * @example * ```typescript * import { trace, ctx } from 'autotel' * * export const createUser = trace(async (data) => { * // Direct property access - no function call! * if (ctx.traceId) { * ctx.setAttribute('user.id', data.id) * console.log('Trace:', ctx.traceId) * } * }) * ``` */ declare const ctx: TraceContext; /** * Wrap a plain function with automatic tracing. The function receives its real * arguments; no context parameter is injected. Use * {@link getActiveTraceContext} inside the function, or use {@link withTracing} * for the explicit `(ctx) => (...args) => result` factory form. * * `trace()` never executes or inspects the function during wrapper * construction. * * @example Auto-inferred name * ```typescript * export const createUser = trace(async (data) => { * return await db.users.create(data) * }) * ``` * * @example Ambient context access * ```typescript * export const createUser = trace(async (data) => { * getActiveTraceContext()?.setAttribute('user.id', data.id) * return await db.users.create(data) * }) * ``` * * @example Explicit name * ```typescript * export const createUser = trace('user.create', async (data) => { * return await db.users.create(data) * }) * ``` */ declare function trace$1<TArgs extends unknown[], TReturn>(fn: (...args: TArgs) => TReturn): (...args: TArgs) => TReturn; declare function trace$1<TArgs extends unknown[], TReturn>(name: string, fn: (...args: TArgs) => TReturn): (...args: TArgs) => TReturn; declare function trace$1<TArgs extends unknown[], TReturn>(options: TracingOptions<TArgs, TReturn>, fn: (...args: TArgs) => TReturn): (...args: TArgs) => TReturn; /** * Approach 2: withTracing() - Middleware-style composable wrapper * * Returns a HOF that wraps functions with tracing. * Perfect for composition and reusable configuration. * * @example Standard usage * ```typescript * export const createUser = withTracing({ * name: 'user.create' * })(ctx => async (data) => { * ctx.setAttribute('user.id', data.id) * return await db.users.create(data) * }) * ``` * * @example Composable * ```typescript * const tracer = withTracing({ serviceName: 'user' }) * * export const createUser = tracer(ctx => async (data) => { }) * export const updateUser = tracer(ctx => async (id, data) => { }) * ``` * * @example With other middleware * ```typescript * export const createUser = compose( * withAuth({ role: 'admin' }), * withTracing({ name: 'user.create' }), * withRateLimit({ max: 100 }) * )(ctx => async (data) => { }) * ``` */ declare function withTracing<TCfgArgs extends unknown[] = unknown[], TCfgReturn = unknown>(options?: TracingOptions<TCfgArgs, TCfgReturn>): <TArgs extends TCfgArgs, TReturn extends TCfgReturn>(fnFactory: (ctx: TraceContext) => (...args: TArgs) => TReturn | Promise<TReturn>) => WrappedFunction<TArgs, TReturn>; /** * Approach 3: instrument() - Batch auto-instrumentation * * Instrument an entire module/object at once. * Closest to @Instrumented decorator pattern. * * @example Basic usage * ```typescript * export default instrument({ * functions: { * createUser: async (data) => { }, * updateUser: async (id, data) => { }, * deleteUser: async (id) => { } * }, * serviceName: 'user', * sampler: new AdaptiveSampler() * }) * // → Traced as "user.createUser", "user.updateUser", "user.deleteUser" * ``` * * @example Per-function overrides * ```typescript * export default instrument({ * functions: { * createUser: async (data) => { }, * deleteUser: async (id) => { } * }, * serviceName: 'user', * overrides: { * deleteUser: { * sampler: new AlwaysSampler(), * withMetrics: true * } * } * }) * ``` * * @example Skip functions * ```typescript * export default instrument({ * functions: { * createUser: async (data) => { }, * _internal: async () => { }, // Auto-skipped (_-prefix) * deleteUser: async (id) => { } * }, * serviceName: 'user', * skip: [/^test/, (key) => key.includes('debug')] * }) * ``` */ declare function instrument<TFunction extends AnyInstrumentable>(options: SingleInstrumentOptions<TFunction>): TFunction; declare function instrument<T extends Record<string, AnyInstrumentable>>(options: InstrumentOptions<T>): T; /** * Options for span() function */ interface SpanOptions { /** Span name */ name: string; /** Attributes to set on the span */ attributes?: Record<string, string | number | boolean>; /** OpenTelemetry span kind */ spanKind?: import('@opentelemetry/api').SpanKind; } /** * Execute a function within a named span * * Useful for adding tracing to specific code blocks without wrapping * the entire function. Supports both synchronous and asynchronous functions. * * Mirrors `trace()`: pass a span name as the first argument for the common * case, or full `SpanOptions` when you need to attach attributes. * * @example * ```typescript * // Name shorthand * await span('payment.charge', async (span) => { * await chargeCustomer(order); * }) * * // Full options when attributes are needed * await span( * { name: 'payment.charge', attributes: { amount: order.total } }, * async (span) => { * await chargeCustomer(order); * }, * ) * * // Sync * const total = span('calculateTotal', (span) => { * return items.reduce((sum, item) => sum + item.price, 0); * }) * ``` */ declare function span<T = unknown>(name: string, fn: (span: Span) => T): T; declare function span<T = unknown>(name: string, fn: (span: Span) => Promise<T>): Promise<T>; declare function span<T = unknown>(options: SpanOptions, fn: (span: Span) => T): T; declare function span<T = unknown>(options: SpanOptions, fn: (span: Span) => Promise<T>): Promise<T>; /** * Options for withNewContext() function */ interface WithNewContextOptions<T = unknown> { /** Function to execute in new root context */ fn: () => Promise<T>; } /** * Execute a function in a new root context (prevents span propagation) * * Useful when you want to start a completely new trace without * parent-child relationships. * * @example * ```typescript * async function handleWebhook(payload: WebhookPayload) { * // This creates a new root trace, not connected to the HTTP request trace * await withNewContext({ * fn: async () => { * await span('webhook.process', async () => { * await processWebhookPayload(payload) * }) * } * }) * } * ``` */ declare function withNewContext<T = unknown>(options: WithNewContextOptions<T>): Promise<T>; /** * Options for withBaggage() function */ interface WithBaggageOptions<T = unknown> { /** Baggage entries to set (key-value pairs) */ baggage: Record<string, string>; /** Function to execute with the updated baggage */ fn: () => T | Promise<T>; } /** * Execute a function with updated baggage entries * * Baggage is immutable in OpenTelemetry, so this helper creates a new context * with the specified baggage entries and runs the function within that context. * All child spans created within the function will inherit the baggage. * * @example Setting baggage for downstream services * ```typescript * import { withTracing, withBaggage } from 'autotel'; * * export const createOrder = withTracing({ name: 'order.create' })((ctx) => async (order: Order) => { * // Set baggage that will be propagated to downstream HTTP calls * return await withBaggage({ * baggage: { * 'tenant.id': order.tenantId, * 'user.id': order.userId, * }, * fn: async () => { * // This HTTP call will include the baggage in headers * await fetch('/api/charge', { * method: 'POST', * body: JSON.stringify(order), * }); * }, * }); * }); * ``` * * @example Using with existing baggage * ```typescript * export const processOrder = withTracing({ name: 'order.process' })((ctx) => async (order: Order) => { * // Read existing baggage * const tenantId = ctx.getBaggage('tenant.id'); * * // Add additional baggage entries * return await withBaggage({ * baggage: { * 'order.id': order.id, * 'order.amount': String(order.amount), * }, * fn: async () => { * await charge(order); * }, * }); * }); * ``` */ declare function withBaggage<T = unknown>(options: WithBaggageOptions<T>): T | Promise<T>; //#endregion export { getActiveTraceContext as a, trace$1 as c, withTracing as d, InstrumentOptions as f, ctx as i, withBaggage as l, TracingOptions as m, WithBaggageOptions as n, instrument as o, SingleInstrumentOptions as p, WithNewContextOptions as r, span as s, SpanOptions as t, withNewContext as u };