@atrim/instrument-node 0.5.1 → 0.5.2-dev.5f953c6.20251221220614

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.
@@ -1,4 +1,5 @@
1
1
  import { Layer, Effect, FiberSet as FiberSet$1 } from 'effect';
2
+ import * as Tracer from '@effect/opentelemetry/Tracer';
2
3
  import { InstrumentationConfig } from '@atrim/instrument-core';
3
4
  import * as effect_Runtime from 'effect/Runtime';
4
5
  import * as effect_FiberId from 'effect/FiberId';
@@ -46,17 +47,12 @@ interface ConfigLoaderOptions {
46
47
  */
47
48
  interface EffectInstrumentationOptions extends ConfigLoaderOptions {
48
49
  /**
49
- * OTLP endpoint URL
50
- * @default process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318'
51
- */
52
- otlpEndpoint?: string;
53
- /**
54
- * Service name
50
+ * Service name for Effect spans
55
51
  * @default process.env.OTEL_SERVICE_NAME || 'effect-service'
56
52
  */
57
53
  serviceName?: string;
58
54
  /**
59
- * Service version
55
+ * Service version for Effect spans
60
56
  * @default process.env.npm_package_version || '1.0.0'
61
57
  */
62
58
  serviceVersion?: string;
@@ -66,20 +62,17 @@ interface EffectInstrumentationOptions extends ConfigLoaderOptions {
66
62
  */
67
63
  autoExtractMetadata?: boolean;
68
64
  /**
69
- * Whether to continue existing traces from NodeSDK auto-instrumentation
70
- *
71
- * When true (default):
72
- * - Effect spans become children of existing NodeSDK spans
73
- * - Example: HTTP request span → Effect business logic span
74
- * - Uses OpenTelemetry Context API for propagation
75
- *
76
- * When false:
77
- * - Effect operations always create new root spans
78
- * - Not recommended unless you have specific requirements
79
- *
80
- * @default true
65
+ * OTLP endpoint URL (only used when exporter mode is 'standalone')
66
+ * @default process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318'
67
+ */
68
+ otlpEndpoint?: string;
69
+ /**
70
+ * Exporter mode:
71
+ * - 'unified': Use global TracerProvider from Node SDK (recommended, enables filtering)
72
+ * - 'standalone': Use Effect's own OTLP exporter (bypasses Node SDK filtering)
73
+ * @default 'unified'
81
74
  */
82
- continueExistingTraces?: boolean;
75
+ exporterMode?: 'unified' | 'standalone';
83
76
  }
84
77
  /**
85
78
  * Create Effect instrumentation layer with custom options
@@ -118,11 +111,12 @@ declare function createEffectInstrumentation(options?: EffectInstrumentationOpti
118
111
  *
119
112
  * Uses the global OpenTelemetry tracer provider that was set up by
120
113
  * initializeInstrumentation(). This ensures all traces (Express, Effect, etc.)
121
- * go to the same OTLP endpoint.
114
+ * go through the same TracerProvider and PatternSpanProcessor.
122
115
  *
123
116
  * Context Propagation:
124
117
  * - Automatically continues traces from NodeSDK auto-instrumentation
125
118
  * - Effect spans become children of HTTP request spans
119
+ * - Respects http.ignore_incoming_paths and other filtering patterns
126
120
  * - No configuration needed
127
121
  *
128
122
  * @example
@@ -138,7 +132,7 @@ declare function createEffectInstrumentation(options?: EffectInstrumentationOpti
138
132
  * )
139
133
  * ```
140
134
  */
141
- declare const EffectInstrumentationLive: Layer.Layer<never, never, never>;
135
+ declare const EffectInstrumentationLive: Layer.Layer<Tracer.OtelTracer, never, never>;
142
136
 
143
137
  /**
144
138
  * Effect-specific span annotation helpers
@@ -1,4 +1,5 @@
1
1
  import { Layer, Effect, FiberSet as FiberSet$1 } from 'effect';
2
+ import * as Tracer from '@effect/opentelemetry/Tracer';
2
3
  import { InstrumentationConfig } from '@atrim/instrument-core';
3
4
  import * as effect_Runtime from 'effect/Runtime';
4
5
  import * as effect_FiberId from 'effect/FiberId';
@@ -46,17 +47,12 @@ interface ConfigLoaderOptions {
46
47
  */
47
48
  interface EffectInstrumentationOptions extends ConfigLoaderOptions {
48
49
  /**
49
- * OTLP endpoint URL
50
- * @default process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318'
51
- */
52
- otlpEndpoint?: string;
53
- /**
54
- * Service name
50
+ * Service name for Effect spans
55
51
  * @default process.env.OTEL_SERVICE_NAME || 'effect-service'
56
52
  */
57
53
  serviceName?: string;
58
54
  /**
59
- * Service version
55
+ * Service version for Effect spans
60
56
  * @default process.env.npm_package_version || '1.0.0'
61
57
  */
62
58
  serviceVersion?: string;
@@ -66,20 +62,17 @@ interface EffectInstrumentationOptions extends ConfigLoaderOptions {
66
62
  */
67
63
  autoExtractMetadata?: boolean;
68
64
  /**
69
- * Whether to continue existing traces from NodeSDK auto-instrumentation
70
- *
71
- * When true (default):
72
- * - Effect spans become children of existing NodeSDK spans
73
- * - Example: HTTP request span → Effect business logic span
74
- * - Uses OpenTelemetry Context API for propagation
75
- *
76
- * When false:
77
- * - Effect operations always create new root spans
78
- * - Not recommended unless you have specific requirements
79
- *
80
- * @default true
65
+ * OTLP endpoint URL (only used when exporter mode is 'standalone')
66
+ * @default process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318'
67
+ */
68
+ otlpEndpoint?: string;
69
+ /**
70
+ * Exporter mode:
71
+ * - 'unified': Use global TracerProvider from Node SDK (recommended, enables filtering)
72
+ * - 'standalone': Use Effect's own OTLP exporter (bypasses Node SDK filtering)
73
+ * @default 'unified'
81
74
  */
82
- continueExistingTraces?: boolean;
75
+ exporterMode?: 'unified' | 'standalone';
83
76
  }
84
77
  /**
85
78
  * Create Effect instrumentation layer with custom options
@@ -118,11 +111,12 @@ declare function createEffectInstrumentation(options?: EffectInstrumentationOpti
118
111
  *
119
112
  * Uses the global OpenTelemetry tracer provider that was set up by
120
113
  * initializeInstrumentation(). This ensures all traces (Express, Effect, etc.)
121
- * go to the same OTLP endpoint.
114
+ * go through the same TracerProvider and PatternSpanProcessor.
122
115
  *
123
116
  * Context Propagation:
124
117
  * - Automatically continues traces from NodeSDK auto-instrumentation
125
118
  * - Effect spans become children of HTTP request spans
119
+ * - Respects http.ignore_incoming_paths and other filtering patterns
126
120
  * - No configuration needed
127
121
  *
128
122
  * @example
@@ -138,7 +132,7 @@ declare function createEffectInstrumentation(options?: EffectInstrumentationOpti
138
132
  * )
139
133
  * ```
140
134
  */
141
- declare const EffectInstrumentationLive: Layer.Layer<never, never, never>;
135
+ declare const EffectInstrumentationLive: Layer.Layer<Tracer.OtelTracer, never, never>;
142
136
 
143
137
  /**
144
138
  * Effect-specific span annotation helpers
@@ -1,4 +1,6 @@
1
- import { Data, Context, Effect, Layer, FiberSet as FiberSet$1, Fiber, Option, FiberId, Tracer } from 'effect';
1
+ import { Data, Context, Effect, Layer, FiberSet as FiberSet$1, Fiber, Option, FiberId, Tracer as Tracer$1 } from 'effect';
2
+ import * as Tracer from '@effect/opentelemetry/Tracer';
3
+ import * as Resource from '@effect/opentelemetry/Resource';
2
4
  import * as Otlp from '@effect/opentelemetry/Otlp';
3
5
  import { FetchHttpClient } from '@effect/platform';
4
6
  import { TraceFlags, trace, context } from '@opentelemetry/api';
@@ -76,6 +78,13 @@ var InstrumentationConfigSchema = z.object({
76
78
  ignore_patterns: z.array(PatternConfigSchema)
77
79
  }),
78
80
  effect: z.object({
81
+ // Enable/disable Effect tracing entirely
82
+ // When false, EffectInstrumentationLive returns Layer.empty
83
+ enabled: z.boolean().default(true),
84
+ // Exporter mode:
85
+ // - "unified": Use global TracerProvider from Node SDK (recommended, enables filtering)
86
+ // - "standalone": Use Effect's own OTLP exporter (bypasses Node SDK filtering)
87
+ exporter: z.enum(["unified", "standalone"]).default("unified"),
79
88
  auto_extract_metadata: z.boolean(),
80
89
  auto_isolation: AutoIsolationConfigSchema.optional()
81
90
  }).optional(),
@@ -454,6 +463,8 @@ function getDefaultConfig() {
454
463
  ]
455
464
  },
456
465
  effect: {
466
+ enabled: true,
467
+ exporter: "unified",
457
468
  auto_extract_metadata: true
458
469
  }
459
470
  };
@@ -483,7 +494,8 @@ async function loadConfigWithOptions(options = {}) {
483
494
  }
484
495
 
485
496
  // src/integrations/effect/effect-tracer.ts
486
- var SDK_NAME = "@effect/opentelemetry-otlp";
497
+ var SDK_NAME = "@effect/opentelemetry";
498
+ var ATTR_TELEMETRY_EXPORTER_MODE = "telemetry.exporter.mode";
487
499
  function createEffectInstrumentation(options = {}) {
488
500
  return Layer.unwrapEffect(
489
501
  Effect.gen(function* () {
@@ -494,90 +506,89 @@ function createEffectInstrumentation(options = {}) {
494
506
  message: error instanceof Error ? error.message : String(error)
495
507
  })
496
508
  });
509
+ const effectEnabled = process.env.OTEL_EFFECT_ENABLED !== "false" && (config.effect?.enabled ?? true);
510
+ if (!effectEnabled) {
511
+ logger.log("@atrim/instrumentation/effect: Effect tracing disabled via config");
512
+ return Layer.empty;
513
+ }
497
514
  yield* Effect.sync(() => {
498
515
  const loggingLevel = config.instrumentation.logging || "on";
499
516
  logger.setLevel(loggingLevel);
500
517
  });
501
518
  yield* Effect.sync(() => initializePatternMatcher(config));
502
- const otlpEndpoint = options.otlpEndpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
503
519
  const serviceName = options.serviceName || process.env.OTEL_SERVICE_NAME || "effect-service";
504
520
  const serviceVersion = options.serviceVersion || process.env.npm_package_version || "1.0.0";
505
- const autoExtractMetadata = options.autoExtractMetadata ?? config.effect?.auto_extract_metadata ?? true;
506
- const continueExistingTraces = options.continueExistingTraces ?? true;
507
- logger.log("\u{1F50D} Effect OpenTelemetry instrumentation");
508
- logger.log(` \u{1F4E1} Endpoint: ${otlpEndpoint}`);
509
- logger.log(` \u{1F3F7}\uFE0F Service: ${serviceName}`);
510
- logger.log(` \u2705 Auto metadata extraction: ${autoExtractMetadata}`);
511
- logger.log(` \u2705 Continue existing traces: ${continueExistingTraces}`);
512
- const otlpLayer = Otlp.layer({
513
- baseUrl: otlpEndpoint,
514
- resource: {
515
- serviceName,
516
- serviceVersion,
517
- attributes: {
518
- "platform.component": "effect",
519
- "effect.auto_metadata": autoExtractMetadata,
520
- "effect.context_propagation": continueExistingTraces,
521
- [ATTR_TELEMETRY_SDK_LANGUAGE]: TELEMETRY_SDK_LANGUAGE_VALUE_NODEJS,
522
- [ATTR_TELEMETRY_SDK_NAME]: SDK_NAME
523
- }
524
- },
525
- // Bridge Effect context to OpenTelemetry global context
526
- // This is essential for context propagation to work properly
527
- tracerContext: (f, span) => {
528
- if (span._tag !== "Span") {
529
- return f();
521
+ const exporterMode = options.exporterMode ?? config.effect?.exporter ?? "unified";
522
+ const resourceAttributes = {
523
+ "platform.component": "effect",
524
+ [ATTR_TELEMETRY_SDK_LANGUAGE]: TELEMETRY_SDK_LANGUAGE_VALUE_NODEJS,
525
+ [ATTR_TELEMETRY_SDK_NAME]: SDK_NAME,
526
+ [ATTR_TELEMETRY_EXPORTER_MODE]: exporterMode
527
+ };
528
+ if (exporterMode === "standalone") {
529
+ const otlpEndpoint = options.otlpEndpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
530
+ logger.log("Effect OpenTelemetry instrumentation (standalone)");
531
+ logger.log(` Service: ${serviceName}`);
532
+ logger.log(` Endpoint: ${otlpEndpoint}`);
533
+ logger.log(" WARNING: Standalone mode bypasses Node SDK filtering");
534
+ return Otlp.layer({
535
+ baseUrl: otlpEndpoint,
536
+ resource: {
537
+ serviceName,
538
+ serviceVersion,
539
+ attributes: resourceAttributes
540
+ },
541
+ // Bridge Effect context to OpenTelemetry global context
542
+ tracerContext: (f, span) => {
543
+ if (span._tag !== "Span") {
544
+ return f();
545
+ }
546
+ const spanContext = {
547
+ traceId: span.traceId,
548
+ spanId: span.spanId,
549
+ traceFlags: span.sampled ? TraceFlags.SAMPLED : TraceFlags.NONE
550
+ };
551
+ const otelSpan = trace.wrapSpanContext(spanContext);
552
+ return context.with(trace.setSpan(context.active(), otelSpan), f);
530
553
  }
531
- const spanContext = {
532
- traceId: span.traceId,
533
- spanId: span.spanId,
534
- traceFlags: span.sampled ? TraceFlags.SAMPLED : TraceFlags.NONE
535
- };
536
- const otelSpan = trace.wrapSpanContext(spanContext);
537
- return context.with(trace.setSpan(context.active(), otelSpan), f);
538
- }
539
- }).pipe(Layer.provide(FetchHttpClient.layer));
540
- if (autoExtractMetadata) {
541
- return otlpLayer;
554
+ }).pipe(Layer.provide(FetchHttpClient.layer));
555
+ } else {
556
+ logger.log("Effect OpenTelemetry instrumentation (unified)");
557
+ logger.log(` Service: ${serviceName}`);
558
+ logger.log(" Using global TracerProvider for span export");
559
+ return Tracer.layerGlobal.pipe(
560
+ Layer.provide(
561
+ Resource.layer({
562
+ serviceName,
563
+ serviceVersion,
564
+ attributes: resourceAttributes
565
+ })
566
+ )
567
+ );
542
568
  }
543
- return otlpLayer;
544
569
  })
545
570
  ).pipe(Layer.orDie);
546
571
  }
547
572
  var EffectInstrumentationLive = Effect.sync(() => {
548
- const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
549
573
  const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
550
574
  const serviceVersion = process.env.npm_package_version || "1.0.0";
551
575
  logger.minimal(`@atrim/instrumentation/effect: Effect tracing enabled (${serviceName})`);
552
- logger.log("\u{1F50D} Effect OpenTelemetry tracer");
553
- logger.log(` \u{1F4E1} Endpoint: ${endpoint}`);
554
- logger.log(` \u{1F3F7}\uFE0F Service: ${serviceName}`);
555
- return Otlp.layer({
556
- baseUrl: endpoint,
557
- resource: {
558
- serviceName,
559
- serviceVersion,
560
- attributes: {
561
- "platform.component": "effect",
562
- [ATTR_TELEMETRY_SDK_LANGUAGE]: TELEMETRY_SDK_LANGUAGE_VALUE_NODEJS,
563
- [ATTR_TELEMETRY_SDK_NAME]: SDK_NAME
564
- }
565
- },
566
- // CRITICAL: Bridge Effect context to OpenTelemetry global context
567
- // This allows NodeSDK auto-instrumentation to see Effect spans as parent spans
568
- tracerContext: (f, span) => {
569
- if (span._tag !== "Span") {
570
- return f();
571
- }
572
- const spanContext = {
573
- traceId: span.traceId,
574
- spanId: span.spanId,
575
- traceFlags: span.sampled ? TraceFlags.SAMPLED : TraceFlags.NONE
576
- };
577
- const otelSpan = trace.wrapSpanContext(spanContext);
578
- return context.with(trace.setSpan(context.active(), otelSpan), f);
579
- }
580
- }).pipe(Layer.provide(FetchHttpClient.layer));
576
+ logger.log("Effect OpenTelemetry tracer (unified)");
577
+ logger.log(` Service: ${serviceName}`);
578
+ return Tracer.layerGlobal.pipe(
579
+ Layer.provide(
580
+ Resource.layer({
581
+ serviceName,
582
+ serviceVersion,
583
+ attributes: {
584
+ "platform.component": "effect",
585
+ [ATTR_TELEMETRY_SDK_LANGUAGE]: TELEMETRY_SDK_LANGUAGE_VALUE_NODEJS,
586
+ [ATTR_TELEMETRY_SDK_NAME]: SDK_NAME,
587
+ [ATTR_TELEMETRY_EXPORTER_MODE]: "unified"
588
+ }
589
+ })
590
+ )
591
+ );
581
592
  }).pipe(Layer.unwrapEffect);
582
593
  function annotateUser(userId, email, username) {
583
594
  const attributes = {
@@ -766,7 +777,7 @@ var runIsolated = (set, effect, name, options) => {
766
777
  return FiberSet$1.run(set, effect, { propagateInterruption });
767
778
  }
768
779
  return Effect.gen(function* () {
769
- const maybeParent = yield* Effect.serviceOption(Tracer.ParentSpan);
780
+ const maybeParent = yield* Effect.serviceOption(Tracer$1.ParentSpan);
770
781
  if (maybeParent._tag === "None" || !captureLogicalParent) {
771
782
  const isolated2 = effect.pipe(
772
783
  Effect.withSpan(name, {