UNPKG

autotel

Version:
1 lines 3.87 kB
{"version":3,"file":"filtering-span-processor.cjs","names":[],"sources":["../src/filtering-span-processor.ts"],"sourcesContent":["/**\n * Filtering Span Processor\n *\n * Filters spans based on a user-provided predicate function.\n * Runs filtering on onEnd() to have access to complete span data.\n *\n * @example Filter out Next.js instrumentation spans\n * ```typescript\n * init({\n * service: 'my-app',\n * spanFilter: (span) => span.instrumentationScope.name !== 'next.js'\n * })\n * ```\n *\n * @example Filter out health check endpoints\n * ```typescript\n * init({\n * service: 'my-app',\n * spanFilter: (span) => !span.name.includes('/health')\n * })\n * ```\n */\n\nimport type {\n SpanProcessor,\n ReadableSpan,\n} from '@opentelemetry/sdk-trace-base';\nimport type { Context } from '@opentelemetry/api';\nimport type { Span } from '@opentelemetry/sdk-trace-base';\n\n/**\n * Predicate function for filtering spans\n *\n * @param span - The completed span (ReadableSpan) with all attributes and metadata\n * @returns true to keep the span, false to drop it\n *\n * Available span properties for filtering:\n * - `span.name` - Span name\n * - `span.attributes` - All span attributes\n * - `span.instrumentationScope` - `{ name, version }` of the instrumentation\n * - `span.status` - Span status code and message\n * - `span.duration` - Span duration as `[seconds, nanoseconds]`\n * - `span.kind` - SpanKind (INTERNAL, SERVER, CLIENT, etc.)\n */\nexport type SpanFilterPredicate = (span: ReadableSpan) => boolean;\n\nexport interface FilteringSpanProcessorOptions {\n /**\n * Predicate function to determine if a span should be kept\n * Return true to keep the span, false to drop it\n */\n filter: SpanFilterPredicate;\n}\n\n/**\n * Span processor that filters spans based on a predicate function.\n *\n * The filter is applied on onEnd() when the span has complete data including:\n * - All attributes\n * - Status code and message\n * - Duration\n * - Events and links\n * - Instrumentation scope (useful for filtering by library)\n *\n * onStart() passes through unchanged to ensure child spans can still be created.\n *\n * Error handling: If the filter predicate throws, the span is forwarded (fail-open).\n */\nexport class FilteringSpanProcessor implements SpanProcessor {\n private readonly wrappedProcessor: SpanProcessor;\n private readonly filter: SpanFilterPredicate;\n\n constructor(\n wrappedProcessor: SpanProcessor,\n options: FilteringSpanProcessorOptions,\n ) {\n this.wrappedProcessor = wrappedProcessor;\n this.filter = options.filter;\n }\n\n /**\n * Pass through onStart - we need spans to start so child spans work\n */\n onStart(span: Span, parentContext: Context): void {\n this.wrappedProcessor.onStart(span, parentContext);\n }\n\n /**\n * Apply filter predicate on span end\n * If filter returns false, span is dropped (not forwarded)\n */\n onEnd(span: ReadableSpan): void {\n try {\n if (this.filter(span)) {\n this.wrappedProcessor.onEnd(span);\n }\n // If filter returns false, span is silently dropped\n } catch {\n // If filter throws, forward the span (fail-open behavior)\n this.wrappedProcessor.onEnd(span);\n }\n }\n\n forceFlush(): Promise<void> {\n return this.wrappedProcessor.forceFlush();\n }\n\n shutdown(): Promise<void> {\n return this.wrappedProcessor.shutdown();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAoEA,IAAa,yBAAb,MAA6D;CAC3D,AAAiB;CACjB,AAAiB;CAEjB,YACE,kBACA,SACA;EACA,KAAK,mBAAmB;EACxB,KAAK,SAAS,QAAQ;CACxB;;;;CAKA,QAAQ,MAAY,eAA8B;EAChD,KAAK,iBAAiB,QAAQ,MAAM,aAAa;CACnD;;;;;CAMA,MAAM,MAA0B;EAC9B,IAAI;GACF,IAAI,KAAK,OAAO,IAAI,GAClB,KAAK,iBAAiB,MAAM,IAAI;EAGpC,QAAQ;GAEN,KAAK,iBAAiB,MAAM,IAAI;EAClC;CACF;CAEA,aAA4B;EAC1B,OAAO,KAAK,iBAAiB,WAAW;CAC1C;CAEA,WAA0B;EACxB,OAAO,KAAK,iBAAiB,SAAS;CACxC;AACF"}