UNPKG

autotel

Version:
434 lines 14.7 kB
import { n as TraceContext } from "./trace-context-IuyoPyNq.cjs"; import { p as Sampler } from "./sampling-BR9fkSwx.cjs"; import { Span } from "@opentelemetry/api"; //#region src/functional.d.ts type WrappedFunction<TArgs extends unknown[], TReturn> = (...args: TArgs) => TReturn | Promise<TReturn>; /** * 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 final span name) * If name not provided, span name becomes: ${serviceName}.${functionName} */ serviceName?: string; /** * Sampling strategy * @default AlwaysSampler */ sampler?: Sampler; /** * Enable metrics collection (counter, histogram) * @default false */ withMetrics?: boolean; /** * Extract attributes from function arguments */ attributesFromArgs?: (args: TArgs) => Record<string, unknown>; /** * Extract attributes from function result */ attributesFromResult?: (result: TReturn) => Record<string, unknown>; /** * Capture the function arguments onto the span as `autotel.input` * (JSON, truncated). One arg is captured directly; multiple are captured as * an array. Off by default — opt in per call. Tools (visualizers, devtools) * read this alongside `ai.toolCall.args` to show function I/O uniformly. * Avoid on args with secrets/PII, or pair with a redacting processor. */ captureInput?: boolean; /** * Capture the function return value onto the span as `autotel.output` * (JSON, truncated). Off by default. Same caveats as {@link captureInput}. */ captureOutput?: boolean; /** * Start a new root span instead of creating a child * Useful for serverless entry points * @default false */ startNewRoot?: boolean; /** * Flush events queue when span ends * Only flushes on root spans (to avoid excessive flushing) * @default true */ flushOnRootSpanEnd?: boolean; /** * Span kind for semantic convention compliance * Used for messaging (PRODUCER/CONSUMER), HTTP (CLIENT/SERVER), etc. * @default SpanKind.INTERNAL */ spanKind?: import('@opentelemetry/api').SpanKind; /** * Classify a thrown value as a real error. Return `false` to treat the throw * as expected control flow: the span is marked OK, no exception is recorded, * and the value is rethrown untouched. Use this for framework control-flow * signals that propagate via `throw` but are not failures — e.g. TanStack * Router / Next.js `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 (won't be instrumented) * Supports: * - String keys: 'functionName' * - RegExp: /^_internal/ * - Predicate: (key, fn) => boolean * * By default, functions starting with _ are skipped */ 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; } /** * Constraint alias for `instrument()` and friends. `never[]` parameters make * every concretely-typed function (e.g. `(name: string) => Promise<User>`) * satisfy the constraint under `strictFunctionTypes` without resorting to * `any`. Use ONLY in constraint positions — the concrete `T` is still * inferred from the actual argument, so call-site argument and return types * are fully preserved. */ type AnyInstrumentable = ((...args: never[]) => unknown) & { displayName?: string; name?: string; }; /** * 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; /** * Approach 1: trace() - Zero-ceremony HOF * * 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. * * @example Auto-inferred name - Plain function * ```typescript * export const createUser = trace(async (data) => { * return await db.users.create(data) * }) * // → Traced as "createUser" * ``` * * @example Ambient context access * ```typescript * export const createUser = trace(async (data) => { * getActiveTraceContext()?.setAttribute('user.id', data.id) * return await db.users.create(data) * }) * ``` * * @example Custom name - Plain function * ```typescript * export const createUser = trace('user.create', async (data) => { * return await db.users.create(data) * }) * // → Traced as "user.create" * ``` * * @example Explicit factory context with withTracing() * ```typescript * export const createUser = withTracing({ name: 'user.create' })((ctx) => async (data) => { * ctx.setAttribute('user.id', data.id) * return await db.users.create(data) * }) * ``` * * @example Full options - Plain function * ```typescript * export const createUser = trace({ * name: 'user.create', * sampler: new AdaptiveSampler(), * withMetrics: true * }, async (data) => { * return await db.users.create(data) * }) * ``` * */ declare function trace<TArgs extends unknown[], TReturn>(fn: (...args: TArgs) => TReturn): (...args: TArgs) => TReturn; declare function trace<TArgs extends unknown[], TReturn>(name: string, fn: (...args: TArgs) => TReturn): (...args: TArgs) => TReturn; declare function trace<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 { InstrumentOptions, SingleInstrumentOptions, SpanOptions, type TraceContext, TracingOptions, WithBaggageOptions, WithNewContextOptions, ctx, getActiveTraceContext, instrument, span, trace, withBaggage, withNewContext, withTracing }; //# sourceMappingURL=functional.d.cts.map