@atrim/instrument-node 0.4.1 → 0.5.0-14fdea7-20260108232035

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.
@@ -46,35 +46,33 @@ declare function createOtlpExporter(options?: OtlpExporterOptions): OTLPTraceExp
46
46
  declare function getOtlpEndpoint(options?: OtlpExporterOptions): string;
47
47
 
48
48
  /**
49
- * Node.js configuration loader using Effect Platform
49
+ * Node.js configuration loader
50
50
  *
51
- * Provides FileSystem and HttpClient layers for the core ConfigLoader service
51
+ * Provides configuration loading using native Node.js APIs (fs, fetch)
52
+ * This module doesn't require Effect Platform, making it work without Effect installed.
52
53
  */
53
54
 
54
55
  /**
55
- * Reset the cached loader (for testing purposes)
56
- * @internal
57
- */
58
- declare function _resetConfigLoaderCache(): void;
59
- /**
60
- * Load configuration from URI (Promise-based convenience API)
61
- *
62
- * Automatically provides Node.js platform layers (FileSystem + HttpClient)
56
+ * Load configuration from URI (file path or URL)
63
57
  *
64
58
  * @param uri - Configuration URI (file://, http://, https://, or relative path)
65
- * @param options - Optional loading options (e.g., to disable caching)
66
59
  * @returns Promise that resolves to validated configuration
67
60
  */
68
- declare function loadConfig(uri: string, options?: {
61
+ declare function loadConfig(uri: string, _options?: {
69
62
  cacheTimeout?: number;
70
63
  }): Promise<InstrumentationConfig>;
71
64
  /**
72
- * Load configuration from inline content (Promise-based convenience API)
65
+ * Load configuration from inline content
73
66
  *
74
67
  * @param content - YAML string, JSON string, or plain object
75
68
  * @returns Promise that resolves to validated configuration
76
69
  */
77
70
  declare function loadConfigFromInline(content: string | unknown): Promise<InstrumentationConfig>;
71
+ /**
72
+ * Reset the config loader cache (no-op for native implementation)
73
+ * @internal
74
+ */
75
+ declare function _resetConfigLoaderCache(): void;
78
76
  /**
79
77
  * Legacy options interface for backward compatibility
80
78
  */
@@ -548,6 +546,10 @@ declare function getServiceVersionAsync(): Promise<string | undefined>;
548
546
  * This processor filters spans based on configured patterns before they are exported.
549
547
  * It wraps another processor (typically BatchSpanProcessor) and only forwards spans
550
548
  * that match the instrumentation patterns.
549
+ *
550
+ * Filtering is applied at two levels:
551
+ * 1. Span name patterns (instrument_patterns / ignore_patterns)
552
+ * 2. HTTP path patterns (http.ignore_incoming_paths) - for HTTP spans
551
553
  */
552
554
 
553
555
  /**
@@ -570,6 +572,7 @@ declare function getServiceVersionAsync(): Promise<string | undefined>;
570
572
  declare class PatternSpanProcessor implements SpanProcessor {
571
573
  private matcher;
572
574
  private wrappedProcessor;
575
+ private httpIgnorePatterns;
573
576
  constructor(config: InstrumentationConfig, wrappedProcessor: SpanProcessor);
574
577
  /**
575
578
  * Called when a span is started
@@ -582,8 +585,20 @@ declare class PatternSpanProcessor implements SpanProcessor {
582
585
  * Called when a span is ended
583
586
  *
584
587
  * This is where we make the final decision on whether to export the span.
588
+ * We check both span name patterns and HTTP path patterns.
585
589
  */
586
590
  onEnd(span: ReadableSpan): void;
591
+ /**
592
+ * Check if span should be ignored based on HTTP path attributes
593
+ *
594
+ * This checks the span's url.path, http.route, or http.target attributes
595
+ * against the configured http.ignore_incoming_paths patterns.
596
+ *
597
+ * This enables filtering of Effect HTTP spans (and any other HTTP spans)
598
+ * based on path patterns, which is essential for filtering out OTLP
599
+ * endpoint requests like /v1/traces, /v1/logs, /v1/metrics.
600
+ */
601
+ private shouldIgnoreHttpSpan;
587
602
  /**
588
603
  * Shutdown the processor
589
604
  */
@@ -0,0 +1,484 @@
1
+ import { Layer, Supervisor, Effect, Context, Option, Fiber, Exit, FiberRef } from 'effect';
2
+ import * as OtelApi from '@opentelemetry/api';
3
+ import { AutoInstrumentationConfig, InstrumentationConfig } from '@atrim/instrument-core';
4
+ export { AutoInstrumentationConfig } from '@atrim/instrument-core';
5
+
6
+ /**
7
+ * Effect-Native Tracing Layer
8
+ *
9
+ * Uses @effect/opentelemetry's NodeSdk.layer for proper Effect integration.
10
+ * This provides automatic HTTP request tracing and Effect.withSpan() support.
11
+ *
12
+ * Requires @effect/opentelemetry peer dependency to be installed.
13
+ */
14
+
15
+ /**
16
+ * Create a layer that provides Effect-native tracing via NodeSdk.layer
17
+ *
18
+ * This integrates with Effect's built-in tracing system, which means:
19
+ * - HTTP requests are automatically traced by @effect/platform
20
+ * - Effect.withSpan() creates proper OTel spans
21
+ * - Parent-child span relationships work correctly
22
+ * - No need to fork fibers for tracing to work
23
+ *
24
+ * Configuration is loaded from instrumentation.yaml (exporter_config section).
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * import { EffectTracingLive } from '@atrim/instrument-node/effect/auto'
29
+ *
30
+ * // HTTP requests automatically traced!
31
+ * const HttpLive = router.pipe(
32
+ * HttpServer.serve(),
33
+ * Layer.provide(ServerLive),
34
+ * Layer.provide(EffectTracingLive),
35
+ * )
36
+ * ```
37
+ */
38
+ declare const createEffectTracingLayer: () => Layer.Layer<never>;
39
+ /**
40
+ * Effect-native tracing layer using @effect/opentelemetry
41
+ *
42
+ * This is the **recommended** layer for Effect HTTP applications. It provides:
43
+ * - Automatic HTTP request tracing (built into @effect/platform)
44
+ * - Full Effect.withSpan() support for manual spans
45
+ * - Proper parent-child span relationships
46
+ * - No need to fork fibers manually
47
+ *
48
+ * Configuration is loaded from your instrumentation.yaml file.
49
+ *
50
+ * @example
51
+ * ```yaml
52
+ * # instrumentation.yaml
53
+ * effect:
54
+ * exporter_config:
55
+ * type: otlp # or 'console' for dev
56
+ * endpoint: http://localhost:4318
57
+ * headers:
58
+ * x-api-key: your-key
59
+ * processor: batch # or 'simple' for immediate export
60
+ * ```
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * import { EffectTracingLive } from '@atrim/instrument-node/effect/auto'
65
+ * import { HttpRouter, HttpServer } from "@effect/platform"
66
+ * import { NodeHttpServer, NodeRuntime } from "@effect/platform-node"
67
+ *
68
+ * const router = HttpRouter.empty.pipe(
69
+ * HttpRouter.get("/", Effect.succeed(HttpServerResponse.text("Hello!"))),
70
+ * )
71
+ *
72
+ * const HttpLive = router.pipe(
73
+ * HttpServer.serve(),
74
+ * Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })),
75
+ * Layer.provide(EffectTracingLive), // <- Just add this!
76
+ * )
77
+ *
78
+ * NodeRuntime.runMain(Layer.launch(HttpLive))
79
+ * // All HTTP requests now automatically traced!
80
+ * ```
81
+ */
82
+ declare const EffectTracingLive: Layer.Layer<never>;
83
+ /**
84
+ * Create a combined layer that provides both:
85
+ * 1. Effect-native HTTP tracing (via NodeSdk.layer)
86
+ * 2. Fiber-level auto-tracing (via Supervisor)
87
+ *
88
+ * This gives you automatic spans for:
89
+ * - Every HTTP request (from Effect's platform middleware)
90
+ * - Every forked fiber (from our Supervisor)
91
+ *
92
+ * No manual Effect.withSpan() calls needed.
93
+ */
94
+ declare const createCombinedTracingLayer: () => Layer.Layer<never>;
95
+ /**
96
+ * Combined tracing layer providing both HTTP and fiber-level auto-tracing
97
+ *
98
+ * This is the **most comprehensive** tracing option. You get automatic spans for:
99
+ * - Every HTTP request (from Effect's @effect/platform middleware)
100
+ * - Every forked fiber (from our Supervisor)
101
+ *
102
+ * No manual Effect.withSpan() calls needed - everything is traced automatically.
103
+ *
104
+ * @example
105
+ * ```yaml
106
+ * # instrumentation.yaml
107
+ * effect:
108
+ * auto_instrumentation:
109
+ * enabled: true
110
+ * granularity: fiber
111
+ * exporter_config:
112
+ * type: otlp
113
+ * endpoint: http://localhost:4318
114
+ * ```
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * import { CombinedTracingLive } from '@atrim/instrument-node/effect/auto'
119
+ *
120
+ * const HttpLive = router.pipe(
121
+ * HttpServer.serve(),
122
+ * Layer.provide(ServerLive),
123
+ * Layer.provide(CombinedTracingLive),
124
+ * )
125
+ *
126
+ * // Both HTTP requests AND forked fibers are auto-traced!
127
+ * ```
128
+ */
129
+ declare const CombinedTracingLive: Layer.Layer<never>;
130
+
131
+ /**
132
+ * Auto-Tracing Supervisor for Effect-TS
133
+ *
134
+ * Provides automatic tracing of all Effect fibers without manual Effect.withSpan() calls.
135
+ * Uses Effect's Supervisor API to intercept fiber creation/termination and create
136
+ * OpenTelemetry spans automatically.
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * import { AutoTracingLive } from '@atrim/instrument-node/effect/auto'
141
+ *
142
+ * const program = Effect.gen(function* () {
143
+ * yield* doWork() // Automatically traced!
144
+ * }).pipe(Effect.provide(AutoTracingLive))
145
+ * ```
146
+ */
147
+
148
+ /**
149
+ * FiberRef to enable/disable auto-tracing for specific fibers
150
+ * Use withoutAutoTracing() to disable
151
+ */
152
+ declare const AutoTracingEnabled: FiberRef.FiberRef<boolean>;
153
+ /**
154
+ * FiberRef to override auto-generated span name
155
+ * Use setSpanName() to override
156
+ */
157
+ declare const AutoTracingSpanName: FiberRef.FiberRef<Option.Option<string>>;
158
+ /**
159
+ * Supervisor that automatically creates OpenTelemetry spans for all Effect fibers
160
+ *
161
+ * This supervisor intercepts fiber creation and termination, creating spans
162
+ * based on configuration from instrumentation.yaml.
163
+ */
164
+ declare class AutoTracingSupervisor extends Supervisor.AbstractSupervisor<void> {
165
+ private readonly config;
166
+ private readonly fiberSpans;
167
+ private readonly fiberStartTimes;
168
+ private _tracer;
169
+ private readonly includePatterns;
170
+ private readonly excludePatterns;
171
+ private activeFiberCount;
172
+ private _rootSpan;
173
+ constructor(config: AutoInstrumentationConfig);
174
+ /**
175
+ * Set the root span for parent context propagation
176
+ */
177
+ setRootSpan(span: OtelApi.Span): void;
178
+ /**
179
+ * Get the tracer lazily - this allows time for the NodeSdk layer to register the global provider
180
+ */
181
+ private get tracer();
182
+ /**
183
+ * Returns the current value (void for this supervisor)
184
+ */
185
+ get value(): Effect.Effect<void>;
186
+ /**
187
+ * Called when a fiber starts executing
188
+ */
189
+ onStart<A, E, R>(_context: Context.Context<R>, _effect: Effect.Effect<A, E, R>, parent: Option.Option<Fiber.RuntimeFiber<unknown, unknown>>, fiber: Fiber.RuntimeFiber<A, E>): void;
190
+ /**
191
+ * Called when a fiber completes (success or failure)
192
+ */
193
+ onEnd<A, E>(exit: Exit.Exit<A, E>, fiber: Fiber.RuntimeFiber<A, E>): void;
194
+ /**
195
+ * Check if a span name should be traced based on filter patterns
196
+ */
197
+ private shouldTrace;
198
+ /**
199
+ * Get initial span attributes for a fiber
200
+ */
201
+ private getInitialAttributes;
202
+ /**
203
+ * Parse stack trace to get source info
204
+ */
205
+ private parseStackTrace;
206
+ /**
207
+ * Parse min_duration string to nanoseconds
208
+ */
209
+ private parseMinDuration;
210
+ }
211
+ /**
212
+ * Create a custom AutoTracingSupervisor with the given config
213
+ */
214
+ declare const createAutoTracingSupervisor: (config: AutoInstrumentationConfig) => AutoTracingSupervisor;
215
+ /**
216
+ * Layer that provides auto-tracing with custom configuration
217
+ *
218
+ * @example
219
+ * ```typescript
220
+ * const CustomAutoTracing = createAutoTracingLayer({
221
+ * enabled: true,
222
+ * span_naming: {
223
+ * default: 'app.{function}',
224
+ * rules: [{ match: { file: 'src/services/.*' }, name: 'service.{function}' }]
225
+ * }
226
+ * })
227
+ * ```
228
+ */
229
+ declare const createAutoTracingLayer: (options?: {
230
+ config?: AutoInstrumentationConfig;
231
+ }) => Layer.Layer<never>;
232
+ /**
233
+ * Wrap an Effect with auto-tracing supervision
234
+ *
235
+ * This creates a span for the main effect AND supervises all child fibers.
236
+ * Use this when you need to ensure all fibers in an effect are traced.
237
+ *
238
+ * @example
239
+ * ```typescript
240
+ * const program = Effect.gen(function* () {
241
+ * yield* doWork() // Automatically traced!
242
+ * })
243
+ *
244
+ * const tracedProgram = withAutoTracing(program, { enabled: true, ... })
245
+ * Effect.runPromise(tracedProgram)
246
+ * ```
247
+ */
248
+ declare const withAutoTracing: <A, E, R>(effect: Effect.Effect<A, E, R>, config: AutoInstrumentationConfig, mainSpanName?: string) => Effect.Effect<A, E, R>;
249
+ /**
250
+ * Zero-config auto-tracing layer
251
+ *
252
+ * Loads configuration from instrumentation.yaml and automatically traces
253
+ * all Effect fibers based on the configuration.
254
+ *
255
+ * @example
256
+ * ```typescript
257
+ * import { AutoTracingLive } from '@atrim/instrument-node/effect/auto'
258
+ *
259
+ * const program = Effect.gen(function* () {
260
+ * yield* doWork() // Automatically traced!
261
+ * }).pipe(Effect.provide(AutoTracingLive))
262
+ * ```
263
+ */
264
+ declare const AutoTracingLive: Layer.Layer<never>;
265
+ /**
266
+ * Disable auto-tracing for a specific Effect
267
+ *
268
+ * @example
269
+ * ```typescript
270
+ * const program = Effect.gen(function* () {
271
+ * yield* publicWork() // Traced
272
+ * yield* withoutAutoTracing(internalWork()) // NOT traced
273
+ * })
274
+ * ```
275
+ */
276
+ declare const withoutAutoTracing: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
277
+ /**
278
+ * Override the auto-generated span name for a specific Effect
279
+ *
280
+ * @example
281
+ * ```typescript
282
+ * const program = Effect.gen(function* () {
283
+ * yield* setSpanName('custom.operation.name')(myEffect)
284
+ * })
285
+ * ```
286
+ */
287
+ declare const setSpanName: (name: string) => <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
288
+ /**
289
+ * Create a fully YAML-driven auto-instrumentation layer
290
+ *
291
+ * This layer reads all configuration from instrumentation.yaml including:
292
+ * - Exporter configuration (type, endpoint, processor)
293
+ * - Auto-tracing configuration (naming rules, filters, performance)
294
+ * - Service metadata (name, version)
295
+ *
296
+ * @example
297
+ * ```typescript
298
+ * import { createFullAutoTracingLayer } from '@atrim/instrument-node/effect/auto'
299
+ *
300
+ * // Everything configured via instrumentation.yaml - no code config needed!
301
+ * const AppLive = createFullAutoTracingLayer()
302
+ *
303
+ * Effect.runPromise(program.pipe(Effect.provide(AppLive)))
304
+ * ```
305
+ */
306
+ declare const createFullAutoTracingLayer: () => Layer.Layer<never>;
307
+ /**
308
+ * Fully YAML-driven auto-instrumentation layer
309
+ *
310
+ * This is the recommended way to use auto-instrumentation. All configuration
311
+ * comes from instrumentation.yaml - no code configuration needed.
312
+ *
313
+ * @example
314
+ * ```yaml
315
+ * # instrumentation.yaml
316
+ * effect:
317
+ * auto_instrumentation:
318
+ * enabled: true
319
+ * span_naming:
320
+ * default: "effect.{function}"
321
+ * rules:
322
+ * - match: { function: "internal.*" }
323
+ * name: "internal.{function}"
324
+ * filter:
325
+ * exclude:
326
+ * - "^internal\\."
327
+ * exporter_config:
328
+ * type: otlp # or 'console' for dev
329
+ * endpoint: http://localhost:4318
330
+ * processor: batch # or 'simple' for dev
331
+ * ```
332
+ *
333
+ * @example
334
+ * ```typescript
335
+ * import { FullAutoTracingLive } from '@atrim/instrument-node/effect/auto'
336
+ *
337
+ * // Just provide the layer - everything else from YAML!
338
+ * Effect.runPromise(program.pipe(Effect.provide(FullAutoTracingLive)))
339
+ * ```
340
+ */
341
+ declare const FullAutoTracingLive: Layer.Layer<never>;
342
+
343
+ /**
344
+ * Node.js configuration loader
345
+ *
346
+ * Provides configuration loading using native Node.js APIs (fs, fetch)
347
+ * This module doesn't require Effect Platform, making it work without Effect installed.
348
+ */
349
+
350
+ /**
351
+ * Legacy options interface for backward compatibility
352
+ */
353
+ interface ConfigLoaderOptions {
354
+ configPath?: string;
355
+ configUrl?: string;
356
+ config?: InstrumentationConfig;
357
+ cacheTimeout?: number;
358
+ }
359
+
360
+ /**
361
+ * Auto-Tracing Configuration Loader
362
+ *
363
+ * Loads auto-tracing configuration from instrumentation.yaml
364
+ * and provides a typed Effect service for accessing it.
365
+ */
366
+
367
+ /**
368
+ * Default auto-tracing configuration when not specified in instrumentation.yaml
369
+ */
370
+ declare const defaultAutoTracingConfig: AutoInstrumentationConfig;
371
+ declare const AutoTracingConfig_base: Context.TagClass<AutoTracingConfig, "AutoTracingConfig", {
372
+ enabled: boolean;
373
+ filter: {
374
+ include: string[];
375
+ exclude: string[];
376
+ };
377
+ granularity: "fiber" | "operator";
378
+ span_naming: {
379
+ default: string;
380
+ infer_from_source: boolean;
381
+ rules: {
382
+ match: {
383
+ function?: string | undefined;
384
+ file?: string | undefined;
385
+ module?: string | undefined;
386
+ };
387
+ name: string;
388
+ }[];
389
+ };
390
+ performance: {
391
+ sampling_rate: number;
392
+ min_duration: string;
393
+ max_concurrent: number;
394
+ };
395
+ metadata: {
396
+ fiber_info: boolean;
397
+ source_location: boolean;
398
+ parent_fiber: boolean;
399
+ };
400
+ }>;
401
+ /**
402
+ * Service tag for auto-tracing configuration
403
+ */
404
+ declare class AutoTracingConfig extends AutoTracingConfig_base {
405
+ }
406
+ /**
407
+ * Load auto-tracing configuration from instrumentation.yaml
408
+ *
409
+ * Returns the config from effect.auto_instrumentation section,
410
+ * or defaults if not specified.
411
+ */
412
+ declare const loadAutoTracingConfig: (options?: ConfigLoaderOptions) => Effect.Effect<AutoInstrumentationConfig, never, never>;
413
+ /**
414
+ * Load auto-tracing configuration synchronously from cache or default
415
+ *
416
+ * Note: This is a synchronous fallback when Effect runtime isn't available.
417
+ * Prefer loadAutoTracingConfig() when possible.
418
+ */
419
+ declare const loadAutoTracingConfigSync: () => AutoInstrumentationConfig;
420
+ /**
421
+ * Layer that provides auto-tracing configuration
422
+ */
423
+ declare const AutoTracingConfigLive: Layer.Layer<AutoTracingConfig, never, never>;
424
+ /**
425
+ * Layer that provides custom auto-tracing configuration
426
+ */
427
+ declare const AutoTracingConfigLayer: (config: AutoInstrumentationConfig) => Layer.Layer<AutoTracingConfig>;
428
+
429
+ /**
430
+ * Span Name Inference for Auto-Tracing
431
+ *
432
+ * Provides intelligent span naming based on source code information
433
+ * and configuration rules from instrumentation.yaml.
434
+ */
435
+
436
+ /**
437
+ * Source code information extracted from stack traces
438
+ */
439
+ interface SourceInfo {
440
+ /** Function name (or 'anonymous') */
441
+ function: string;
442
+ /** Full file path */
443
+ file: string;
444
+ /** Line number */
445
+ line: number;
446
+ /** Column number */
447
+ column: number;
448
+ }
449
+ /**
450
+ * Template variables available for span naming
451
+ */
452
+ interface TemplateVariables {
453
+ fiber_id: string;
454
+ function: string;
455
+ module: string;
456
+ file: string;
457
+ line: string;
458
+ operator: string;
459
+ [key: string]: string;
460
+ }
461
+ /**
462
+ * Infer a span name based on fiber ID, source info, and configuration
463
+ *
464
+ * Priority:
465
+ * 1. Match against naming rules (first match wins)
466
+ * 2. Use default template with source info if available
467
+ * 3. Fallback to default template with fiber ID only
468
+ *
469
+ * @param fiberId - The fiber's numeric ID
470
+ * @param sourceInfo - Optional source code information from stack trace
471
+ * @param config - Auto-instrumentation configuration
472
+ * @returns The inferred span name
473
+ */
474
+ declare function inferSpanName(fiberId: number, sourceInfo: SourceInfo | undefined, config: AutoInstrumentationConfig): string;
475
+ /**
476
+ * Sanitize a span name to be OpenTelemetry compliant
477
+ *
478
+ * - Replaces invalid characters with underscores
479
+ * - Ensures name is not empty
480
+ * - Truncates to reasonable length
481
+ */
482
+ declare function sanitizeSpanName(name: string): string;
483
+
484
+ export { AutoTracingConfig, AutoTracingConfigLayer, AutoTracingConfigLive, AutoTracingEnabled, AutoTracingLive, AutoTracingSpanName, AutoTracingSupervisor, CombinedTracingLive, EffectTracingLive, FullAutoTracingLive, type SourceInfo, type TemplateVariables, createAutoTracingLayer, createAutoTracingSupervisor, createCombinedTracingLayer, createEffectTracingLayer, createFullAutoTracingLayer, defaultAutoTracingConfig, inferSpanName, loadAutoTracingConfig, loadAutoTracingConfigSync, sanitizeSpanName, setSpanName, withAutoTracing, withoutAutoTracing };