diff --git a/README.md b/README.md index f9fa114..cf2ab1f 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ All options are passed to `init()` as an `ObservabilityConfig` object. | `ignoredRoutes` | `string[]` | `[]` | Routes never sampled by Sentry (e.g. `['/health', '/metrics']`). Trailing slashes and query strings are handled automatically. | | `ignoredBreadcrumbCategories` | `string[]` | `['http', 'fetch', 'xhr']` | Breadcrumb categories to apply URL-pattern filtering to. | | `ignoredBreadcrumbPatterns` | `RegExp[]` | Pyroscope + PostHog patterns | URL patterns for breadcrumbs that should be dropped entirely. | +| `ignoredSpanOps` | `string[]` | `[]` | Span ops dropped before sending to Sentry (e.g. `['middleware.express', 'router.express']`). Children are re-parented; OTLP export is unaffected. | ## API diff --git a/biome.json b/biome.json index 415a5ab..18ddb8a 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.5.6/schema.json", + "$schema": "https://biomejs.dev/schemas/2.5.15/schema.json", "files": { "ignoreUnknown": true, "includes": ["**", "!dist", "!node_modules", "!coverage"] diff --git a/package-lock.json b/package-lock.json index e89032b..6660840 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scope3/observability-js", - "version": "2.3.0", + "version": "2.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@scope3/observability-js", - "version": "2.3.0", + "version": "2.4.0", "license": "MIT", "dependencies": { "@opentelemetry/api": "^1.9.0", diff --git a/package.json b/package.json index bbc841b..b755946 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@scope3/observability-js", - "version": "2.3.0", + "version": "2.4.0", "description": "Unified observability (Sentry, OpenTelemetry, Pyroscope) for Node.js services", "keywords": [ "observability", diff --git a/src/__tests__/otel-setup.test.ts b/src/__tests__/otel-setup.test.ts index e6adb8d..b3428cf 100644 --- a/src/__tests__/otel-setup.test.ts +++ b/src/__tests__/otel-setup.test.ts @@ -172,3 +172,26 @@ describe('trace provider registration on the Sentry client', () => { } }, 2000) }) + +describe('Sentry span filtering', () => { + it('passes ignoredSpanOps to Sentry as ignoreSpans', () => { + init({ + serviceName: 'otel-setup-test', + environment: 'development', + enableOtel: true, + sentry: { dsn: SENTRY_DSN, enabled: false }, + filters: { ignoredSpanOps: ['middleware.express', 'router.express'] }, + }) + + expect(Sentry.getClient()?.getOptions().ignoreSpans).toEqual([ + { op: 'middleware.express' }, + { op: 'router.express' }, + ]) + }) + + it('ignores no spans by default', () => { + initWithOtel() + + expect(Sentry.getClient()?.getOptions().ignoreSpans).toEqual([]) + }) +}) diff --git a/src/config.ts b/src/config.ts index 12e7712..c87b407 100644 --- a/src/config.ts +++ b/src/config.ts @@ -81,6 +81,7 @@ export function resolveConfig(config: ObservabilityConfig): ResolvedConfig { /posthog\.com/i, /i\.posthog\.com/i, ], + ignoredSpanOps: config.filters?.ignoredSpanOps ?? [], }, shouldDropError: config.shouldDropError, diff --git a/src/provider.ts b/src/provider.ts index 050ad37..b860eaf 100644 --- a/src/provider.ts +++ b/src/provider.ts @@ -109,6 +109,7 @@ function initializeSentry(config: ResolvedConfig): void { ], enabled: config.sentry.enabled, tracesSampler: buildTracesSampler(config), + ignoreSpans: config.filters.ignoredSpanOps.map((op) => ({ op })), beforeSend(event, hint) { const error = hint?.originalException diff --git a/src/types.ts b/src/types.ts index caef78e..7d4c871 100644 --- a/src/types.ts +++ b/src/types.ts @@ -89,6 +89,12 @@ export interface FilterConfig { * Defaults to patterns matching Pyroscope and PostHog URLs. */ ignoredBreadcrumbPatterns?: RegExp[] + /** + * Sentry span ops dropped before spans are sent to Sentry. Children of a + * dropped span are re-parented, and OTLP export is unaffected. + * Example: `['middleware.express', 'router.express']`. Defaults to `[]`. + */ + ignoredSpanOps?: string[] } /** Top-level configuration passed to {@link init}. */ @@ -177,6 +183,7 @@ export interface ResolvedConfig { ignoredRoutes: string[] ignoredBreadcrumbCategories: string[] ignoredBreadcrumbPatterns: RegExp[] + ignoredSpanOps: string[] } shouldDropError?: (error: unknown) => boolean