@atrim/instrument-node 0.7.1-dev.14fdea7.20260108232436 → 0.7.1-dev.764c183.20260110203528

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.
@@ -82,7 +82,7 @@ declare const createEffectTracingLayer: () => Layer.Layer<never>;
82
82
  declare const EffectTracingLive: Layer.Layer<never>;
83
83
  /**
84
84
  * Create a combined layer that provides both:
85
- * 1. Effect-native HTTP tracing (via NodeSdk.layer)
85
+ * 1. Effect-native HTTP tracing (via Effect's Tracer + global OTel provider)
86
86
  * 2. Fiber-level auto-tracing (via Supervisor)
87
87
  *
88
88
  * This gives you automatic spans for:
@@ -90,6 +90,14 @@ declare const EffectTracingLive: Layer.Layer<never>;
90
90
  * - Every forked fiber (from our Supervisor)
91
91
  *
92
92
  * No manual Effect.withSpan() calls needed.
93
+ *
94
+ * ARCHITECTURE NOTE:
95
+ * Unlike EffectTracingLive which uses NodeSdk.layer (scoped provider),
96
+ * CombinedTracingLive uses a single GLOBAL TracerProvider so that both
97
+ * Effect's Tracer and our Supervisor use the same provider and exporter.
98
+ *
99
+ * This solves the dual-provider problem where HTTP spans and fiber spans
100
+ * would otherwise go to different exporters.
93
101
  */
94
102
  declare const createCombinedTracingLayer: () => Layer.Layer<never>;
95
103
  /**
@@ -166,17 +174,18 @@ declare class AutoTracingSupervisor extends Supervisor.AbstractSupervisor<void>
166
174
  private readonly fiberSpans;
167
175
  private readonly fiberStartTimes;
168
176
  private _tracer;
177
+ private readonly tracerProvider;
169
178
  private readonly includePatterns;
170
179
  private readonly excludePatterns;
171
180
  private activeFiberCount;
172
181
  private _rootSpan;
173
- constructor(config: AutoInstrumentationConfig);
182
+ constructor(config: AutoInstrumentationConfig, tracerProvider?: OtelApi.TracerProvider);
174
183
  /**
175
184
  * Set the root span for parent context propagation
176
185
  */
177
186
  setRootSpan(span: OtelApi.Span): void;
178
187
  /**
179
- * Get the tracer lazily - this allows time for the NodeSdk layer to register the global provider
188
+ * Get the tracer lazily - uses provided TracerProvider if available, otherwise uses global
180
189
  */
181
190
  private get tracer();
182
191
  /**
@@ -191,6 +200,10 @@ declare class AutoTracingSupervisor extends Supervisor.AbstractSupervisor<void>
191
200
  * Called when a fiber completes (success or failure)
192
201
  */
193
202
  onEnd<A, E>(exit: Exit.Exit<A, E>, fiber: Fiber.RuntimeFiber<A, E>): void;
203
+ /**
204
+ * Get attributes for span links from config
205
+ */
206
+ private getLinkAttributes;
194
207
  /**
195
208
  * Check if a span name should be traced based on filter patterns
196
209
  */
@@ -211,7 +224,7 @@ declare class AutoTracingSupervisor extends Supervisor.AbstractSupervisor<void>
211
224
  /**
212
225
  * Create a custom AutoTracingSupervisor with the given config
213
226
  */
214
- declare const createAutoTracingSupervisor: (config: AutoInstrumentationConfig) => AutoTracingSupervisor;
227
+ declare const createAutoTracingSupervisor: (config: AutoInstrumentationConfig, tracerProvider?: OtelApi.TracerProvider) => AutoTracingSupervisor;
215
228
  /**
216
229
  * Layer that provides auto-tracing with custom configuration
217
230
  *
@@ -387,6 +400,13 @@ declare const AutoTracingConfig_base: Context.TagClass<AutoTracingConfig, "AutoT
387
400
  name: string;
388
401
  }[];
389
402
  };
403
+ span_relationships: {
404
+ type: "parent-child" | "span-links" | "both";
405
+ link_attributes?: {
406
+ "link.type": string;
407
+ custom?: Record<string, string> | undefined;
408
+ } | undefined;
409
+ };
390
410
  performance: {
391
411
  sampling_rate: number;
392
412
  min_duration: string;
@@ -82,7 +82,7 @@ declare const createEffectTracingLayer: () => Layer.Layer<never>;
82
82
  declare const EffectTracingLive: Layer.Layer<never>;
83
83
  /**
84
84
  * Create a combined layer that provides both:
85
- * 1. Effect-native HTTP tracing (via NodeSdk.layer)
85
+ * 1. Effect-native HTTP tracing (via Effect's Tracer + global OTel provider)
86
86
  * 2. Fiber-level auto-tracing (via Supervisor)
87
87
  *
88
88
  * This gives you automatic spans for:
@@ -90,6 +90,14 @@ declare const EffectTracingLive: Layer.Layer<never>;
90
90
  * - Every forked fiber (from our Supervisor)
91
91
  *
92
92
  * No manual Effect.withSpan() calls needed.
93
+ *
94
+ * ARCHITECTURE NOTE:
95
+ * Unlike EffectTracingLive which uses NodeSdk.layer (scoped provider),
96
+ * CombinedTracingLive uses a single GLOBAL TracerProvider so that both
97
+ * Effect's Tracer and our Supervisor use the same provider and exporter.
98
+ *
99
+ * This solves the dual-provider problem where HTTP spans and fiber spans
100
+ * would otherwise go to different exporters.
93
101
  */
94
102
  declare const createCombinedTracingLayer: () => Layer.Layer<never>;
95
103
  /**
@@ -166,17 +174,18 @@ declare class AutoTracingSupervisor extends Supervisor.AbstractSupervisor<void>
166
174
  private readonly fiberSpans;
167
175
  private readonly fiberStartTimes;
168
176
  private _tracer;
177
+ private readonly tracerProvider;
169
178
  private readonly includePatterns;
170
179
  private readonly excludePatterns;
171
180
  private activeFiberCount;
172
181
  private _rootSpan;
173
- constructor(config: AutoInstrumentationConfig);
182
+ constructor(config: AutoInstrumentationConfig, tracerProvider?: OtelApi.TracerProvider);
174
183
  /**
175
184
  * Set the root span for parent context propagation
176
185
  */
177
186
  setRootSpan(span: OtelApi.Span): void;
178
187
  /**
179
- * Get the tracer lazily - this allows time for the NodeSdk layer to register the global provider
188
+ * Get the tracer lazily - uses provided TracerProvider if available, otherwise uses global
180
189
  */
181
190
  private get tracer();
182
191
  /**
@@ -191,6 +200,10 @@ declare class AutoTracingSupervisor extends Supervisor.AbstractSupervisor<void>
191
200
  * Called when a fiber completes (success or failure)
192
201
  */
193
202
  onEnd<A, E>(exit: Exit.Exit<A, E>, fiber: Fiber.RuntimeFiber<A, E>): void;
203
+ /**
204
+ * Get attributes for span links from config
205
+ */
206
+ private getLinkAttributes;
194
207
  /**
195
208
  * Check if a span name should be traced based on filter patterns
196
209
  */
@@ -211,7 +224,7 @@ declare class AutoTracingSupervisor extends Supervisor.AbstractSupervisor<void>
211
224
  /**
212
225
  * Create a custom AutoTracingSupervisor with the given config
213
226
  */
214
- declare const createAutoTracingSupervisor: (config: AutoInstrumentationConfig) => AutoTracingSupervisor;
227
+ declare const createAutoTracingSupervisor: (config: AutoInstrumentationConfig, tracerProvider?: OtelApi.TracerProvider) => AutoTracingSupervisor;
215
228
  /**
216
229
  * Layer that provides auto-tracing with custom configuration
217
230
  *
@@ -387,6 +400,13 @@ declare const AutoTracingConfig_base: Context.TagClass<AutoTracingConfig, "AutoT
387
400
  name: string;
388
401
  }[];
389
402
  };
403
+ span_relationships: {
404
+ type: "parent-child" | "span-links" | "both";
405
+ link_attributes?: {
406
+ "link.type": string;
407
+ custom?: Record<string, string> | undefined;
408
+ } | undefined;
409
+ };
390
410
  performance: {
391
411
  sampling_rate: number;
392
412
  min_duration: string;
@@ -1,15 +1,15 @@
1
- import { Data, Context, Effect, Layer, FiberRef, Option, Supervisor, FiberRefs, Exit } from 'effect';
2
- import { NodeSdk } from '@effect/opentelemetry';
1
+ import { Data, Context, Effect, Layer, FiberRef, Option, Supervisor, FiberRefs, Tracer, Exit } from 'effect';
2
+ import { NodeSdk, Resource, Tracer as Tracer$1 } from '@effect/opentelemetry';
3
3
  import { BasicTracerProvider, SimpleSpanProcessor, ConsoleSpanExporter, BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
4
4
  import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
5
+ import { resourceFromAttributes } from '@opentelemetry/resources';
6
+ import { ATTR_SERVICE_VERSION, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
7
+ import * as OtelApi from '@opentelemetry/api';
5
8
  import { FileSystem } from '@effect/platform/FileSystem';
6
9
  import * as HttpClient from '@effect/platform/HttpClient';
7
10
  import * as HttpClientRequest from '@effect/platform/HttpClientRequest';
8
11
  import { parse } from 'yaml';
9
12
  import { z } from 'zod';
10
- import * as OtelApi from '@opentelemetry/api';
11
- import { resourceFromAttributes } from '@opentelemetry/resources';
12
- import { ATTR_SERVICE_VERSION, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
13
13
  import * as path from 'path';
14
14
 
15
15
  var __defProp = Object.defineProperty;
@@ -82,6 +82,25 @@ var AutoInstrumentationConfigSchema = z.object({
82
82
  // Naming rules (first match wins)
83
83
  rules: z.array(SpanNamingRuleSchema).default([])
84
84
  }).default({}),
85
+ // Span relationship configuration for forked fibers
86
+ // Controls how child fiber spans relate to their parent/forking context
87
+ span_relationships: z.object({
88
+ // Relationship type between forked fiber spans and their parent context
89
+ // - 'parent-child': Use parent-child relationship (default, traditional tracing)
90
+ // Parent span shows child as nested. Works well with most observability tools.
91
+ // - 'span-links': Use span links (semantically correct for async forks per OTel spec)
92
+ // Fibers get independent traces linked to parent. Better for long-running fibers.
93
+ // - 'both': Create parent-child AND add span links
94
+ // Maximum visibility but may create redundant data.
95
+ type: z.enum(["parent-child", "span-links", "both"]).default("parent-child"),
96
+ // Custom attributes to add to span links (only used when type includes links)
97
+ link_attributes: z.object({
98
+ // Link type identifier
99
+ "link.type": z.string().default("fork"),
100
+ // Custom attributes (key-value pairs)
101
+ custom: z.record(z.string()).optional()
102
+ }).optional()
103
+ }).default({}),
85
104
  // Pattern-based filtering
86
105
  filter: z.object({
87
106
  // Only trace spans matching these patterns (empty = trace all)
@@ -525,6 +544,9 @@ var defaultAutoTracingConfig = {
525
544
  infer_from_source: true,
526
545
  rules: []
527
546
  },
547
+ span_relationships: {
548
+ type: "parent-child"
549
+ },
528
550
  filter: {
529
551
  include: [],
530
552
  exclude: []
@@ -706,7 +728,7 @@ function sanitizeSpanName(name) {
706
728
  var AutoTracingEnabled = FiberRef.unsafeMake(true);
707
729
  var AutoTracingSpanName = FiberRef.unsafeMake(Option.none());
708
730
  var AutoTracingSupervisor = class extends Supervisor.AbstractSupervisor {
709
- constructor(config) {
731
+ constructor(config, tracerProvider) {
710
732
  super();
711
733
  this.config = config;
712
734
  // WeakMap to associate fibers with their OTel spans
@@ -715,6 +737,8 @@ var AutoTracingSupervisor = class extends Supervisor.AbstractSupervisor {
715
737
  __publicField(this, "fiberStartTimes", /* @__PURE__ */ new WeakMap());
716
738
  // OpenTelemetry tracer - lazily initialized
717
739
  __publicField(this, "_tracer", null);
740
+ // Optional TracerProvider (if provided, use this instead of global)
741
+ __publicField(this, "tracerProvider", null);
718
742
  // Compiled filter patterns
719
743
  __publicField(this, "includePatterns");
720
744
  __publicField(this, "excludePatterns");
@@ -722,6 +746,10 @@ var AutoTracingSupervisor = class extends Supervisor.AbstractSupervisor {
722
746
  __publicField(this, "activeFiberCount", 0);
723
747
  // Root span for parent context (set by withAutoTracing)
724
748
  __publicField(this, "_rootSpan", null);
749
+ if (tracerProvider) {
750
+ this.tracerProvider = tracerProvider;
751
+ logger.log("@atrim/auto-trace: Using provided TracerProvider");
752
+ }
725
753
  this.includePatterns = (config.filter?.include || []).map((p) => new RegExp(p));
726
754
  this.excludePatterns = (config.filter?.exclude || []).map((p) => new RegExp(p));
727
755
  logger.log("@atrim/auto-trace: Supervisor initialized");
@@ -736,11 +764,17 @@ var AutoTracingSupervisor = class extends Supervisor.AbstractSupervisor {
736
764
  this._rootSpan = span;
737
765
  }
738
766
  /**
739
- * Get the tracer lazily - this allows time for the NodeSdk layer to register the global provider
767
+ * Get the tracer lazily - uses provided TracerProvider if available, otherwise uses global
740
768
  */
741
769
  get tracer() {
742
770
  if (!this._tracer) {
743
- this._tracer = OtelApi.trace.getTracer("@atrim/auto-trace", "1.0.0");
771
+ if (this.tracerProvider) {
772
+ logger.log("@atrim/auto-trace: Getting tracer from provided TracerProvider");
773
+ this._tracer = this.tracerProvider.getTracer("@atrim/auto-trace", "1.0.0");
774
+ } else {
775
+ logger.log("@atrim/auto-trace: Getting tracer from global API");
776
+ this._tracer = OtelApi.trace.getTracer("@atrim/auto-trace", "1.0.0");
777
+ }
744
778
  }
745
779
  return this._tracer;
746
780
  }
@@ -754,9 +788,11 @@ var AutoTracingSupervisor = class extends Supervisor.AbstractSupervisor {
754
788
  * Called when a fiber starts executing
755
789
  */
756
790
  onStart(_context, _effect, parent, fiber) {
791
+ logger.log(`@atrim/auto-trace: onStart called for fiber ${fiber.id().id}`);
757
792
  const fiberRefsValue = fiber.getFiberRefs();
758
793
  const enabled = FiberRefs.getOrDefault(fiberRefsValue, AutoTracingEnabled);
759
794
  if (!enabled) {
795
+ logger.log(`@atrim/auto-trace: Auto-tracing disabled for fiber ${fiber.id().id}`);
760
796
  return;
761
797
  }
762
798
  const samplingRate = this.config.performance?.sampling_rate ?? 1;
@@ -778,37 +814,86 @@ var AutoTracingSupervisor = class extends Supervisor.AbstractSupervisor {
778
814
  if (!this.shouldTrace(spanName)) {
779
815
  return;
780
816
  }
817
+ const relationshipType = this.config.span_relationships?.type ?? "parent-child";
818
+ const useParentChild = relationshipType === "parent-child" || relationshipType === "both";
819
+ const useSpanLinks = relationshipType === "span-links" || relationshipType === "both";
781
820
  let parentContext = OtelApi.ROOT_CONTEXT;
782
821
  let parentFiberId;
783
- if (Option.isSome(parent)) {
822
+ let spanLinks = [];
823
+ const maybeEffectParentSpan = Context.getOption(_context, Tracer.ParentSpan);
824
+ if (Option.isSome(maybeEffectParentSpan)) {
825
+ const effectSpan = maybeEffectParentSpan.value;
826
+ logger.log(
827
+ `@atrim/auto-trace: Found ParentSpan - traceId=${effectSpan.traceId.slice(0, 8)}..., spanId=${effectSpan.spanId.slice(0, 8)}...`
828
+ );
829
+ const otelSpanContext = {
830
+ traceId: effectSpan.traceId,
831
+ spanId: effectSpan.spanId,
832
+ traceFlags: effectSpan.sampled ? OtelApi.TraceFlags.SAMPLED : OtelApi.TraceFlags.NONE,
833
+ isRemote: false
834
+ };
835
+ if (useParentChild) {
836
+ const wrappedSpan = OtelApi.trace.wrapSpanContext(otelSpanContext);
837
+ parentContext = OtelApi.trace.setSpan(OtelApi.ROOT_CONTEXT, wrappedSpan);
838
+ }
839
+ if (useSpanLinks) {
840
+ const linkAttributes = this.getLinkAttributes();
841
+ spanLinks.push({
842
+ context: otelSpanContext,
843
+ attributes: linkAttributes
844
+ });
845
+ logger.log(`@atrim/auto-trace: Added span link to parent (${relationshipType})`);
846
+ }
847
+ } else if (Option.isSome(parent)) {
784
848
  parentFiberId = parent.value.id().id;
785
849
  const parentSpan = this.fiberSpans.get(parent.value);
786
850
  if (parentSpan) {
787
- parentContext = OtelApi.trace.setSpan(OtelApi.ROOT_CONTEXT, parentSpan);
788
- } else {
789
- const activeSpan = OtelApi.trace.getSpan(OtelApi.context.active());
790
- if (activeSpan) {
791
- parentContext = OtelApi.context.active();
792
- } else if (this._rootSpan) {
851
+ if (useParentChild) {
852
+ parentContext = OtelApi.trace.setSpan(OtelApi.ROOT_CONTEXT, parentSpan);
853
+ }
854
+ if (useSpanLinks) {
855
+ const linkAttributes = this.getLinkAttributes();
856
+ spanLinks.push({
857
+ context: parentSpan.spanContext(),
858
+ attributes: linkAttributes
859
+ });
860
+ }
861
+ } else if (this._rootSpan) {
862
+ if (useParentChild) {
793
863
  parentContext = OtelApi.trace.setSpan(OtelApi.ROOT_CONTEXT, this._rootSpan);
794
864
  }
865
+ if (useSpanLinks) {
866
+ const linkAttributes = this.getLinkAttributes();
867
+ spanLinks.push({
868
+ context: this._rootSpan.spanContext(),
869
+ attributes: linkAttributes
870
+ });
871
+ }
795
872
  }
796
- } else {
797
- const activeSpan = OtelApi.trace.getSpan(OtelApi.context.active());
798
- if (activeSpan) {
799
- parentContext = OtelApi.context.active();
800
- } else if (this._rootSpan) {
873
+ } else if (this._rootSpan) {
874
+ if (useParentChild) {
801
875
  parentContext = OtelApi.trace.setSpan(OtelApi.ROOT_CONTEXT, this._rootSpan);
802
876
  }
877
+ if (useSpanLinks) {
878
+ const linkAttributes = this.getLinkAttributes();
879
+ spanLinks.push({
880
+ context: this._rootSpan.spanContext(),
881
+ attributes: linkAttributes
882
+ });
883
+ }
803
884
  }
804
- const span = this.tracer.startSpan(
805
- spanName,
806
- {
807
- kind: OtelApi.SpanKind.INTERNAL,
808
- attributes: this.getInitialAttributes(fiber, sourceInfo, parentFiberId)
809
- },
810
- parentContext
811
- );
885
+ if (Option.isSome(parent)) {
886
+ parentFiberId = parent.value.id().id;
887
+ }
888
+ const spanOptions = {
889
+ kind: OtelApi.SpanKind.INTERNAL,
890
+ attributes: this.getInitialAttributes(fiber, sourceInfo, parentFiberId)
891
+ };
892
+ if (spanLinks.length > 0) {
893
+ spanOptions.links = spanLinks;
894
+ }
895
+ const span = this.tracer.startSpan(spanName, spanOptions, parentContext);
896
+ logger.log(`@atrim/auto-trace: Created span "${spanName}" for fiber ${fiber.id().id}`);
812
897
  this.fiberSpans.set(fiber, span);
813
898
  this.fiberStartTimes.set(fiber, process.hrtime.bigint());
814
899
  this.activeFiberCount++;
@@ -817,8 +902,12 @@ var AutoTracingSupervisor = class extends Supervisor.AbstractSupervisor {
817
902
  * Called when a fiber completes (success or failure)
818
903
  */
819
904
  onEnd(exit, fiber) {
905
+ logger.log(`@atrim/auto-trace: onEnd called for fiber ${fiber.id().id}`);
820
906
  const span = this.fiberSpans.get(fiber);
821
907
  if (!span) {
908
+ logger.log(
909
+ `@atrim/auto-trace: No span found for fiber ${fiber.id().id} (skipped or filtered)`
910
+ );
822
911
  return;
823
912
  }
824
913
  const startTime = this.fiberStartTimes.get(fiber);
@@ -842,10 +931,26 @@ var AutoTracingSupervisor = class extends Supervisor.AbstractSupervisor {
842
931
  span.setAttribute("effect.fiber.failed", true);
843
932
  }
844
933
  span.end();
934
+ logger.log(`@atrim/auto-trace: Ended span for fiber ${fiber.id().id}`);
845
935
  this.fiberSpans.delete(fiber);
846
936
  this.fiberStartTimes.delete(fiber);
847
937
  this.activeFiberCount--;
848
938
  }
939
+ /**
940
+ * Get attributes for span links from config
941
+ */
942
+ getLinkAttributes() {
943
+ const linkConfig = this.config.span_relationships?.link_attributes;
944
+ const attrs = {
945
+ "link.type": linkConfig?.["link.type"] ?? "fork"
946
+ };
947
+ if (linkConfig?.custom) {
948
+ for (const [key, value] of Object.entries(linkConfig.custom)) {
949
+ attrs[key] = value;
950
+ }
951
+ }
952
+ return attrs;
953
+ }
849
954
  /**
850
955
  * Check if a span name should be traced based on filter patterns
851
956
  */
@@ -936,8 +1041,8 @@ var AutoTracingSupervisor = class extends Supervisor.AbstractSupervisor {
936
1041
  }
937
1042
  }
938
1043
  };
939
- var createAutoTracingSupervisor = (config) => {
940
- return new AutoTracingSupervisor(config);
1044
+ var createAutoTracingSupervisor = (config, tracerProvider) => {
1045
+ return new AutoTracingSupervisor(config, tracerProvider);
941
1046
  };
942
1047
  var createAutoTracingLayer = (options) => {
943
1048
  return Layer.unwrapEffect(
@@ -1017,7 +1122,9 @@ var createExporterLayer = (exporterConfig, serviceName, serviceVersion) => {
1017
1122
  };
1018
1123
  if (config.headers) {
1019
1124
  exporterConfig2.headers = config.headers;
1020
- logger.log(`@atrim/auto-trace: Using custom headers: ${Object.keys(config.headers).join(", ")}`);
1125
+ logger.log(
1126
+ `@atrim/auto-trace: Using custom headers: ${Object.keys(config.headers).join(", ")}`
1127
+ );
1021
1128
  }
1022
1129
  return new OTLPTraceExporter(exporterConfig2);
1023
1130
  };
@@ -1110,7 +1217,9 @@ var createEffectTracingLayer = () => {
1110
1217
  };
1111
1218
  if (exporterConfig.headers) {
1112
1219
  otlpConfig.headers = exporterConfig.headers;
1113
- logger.log(`@atrim/auto-trace: Using custom headers: ${Object.keys(exporterConfig.headers).join(", ")}`);
1220
+ logger.log(
1221
+ `@atrim/auto-trace: Using custom headers: ${Object.keys(exporterConfig.headers).join(", ")}`
1222
+ );
1114
1223
  }
1115
1224
  const exporter = new OTLPTraceExporter(otlpConfig);
1116
1225
  if (exporterConfig.processor === "simple") {
@@ -1158,6 +1267,9 @@ var createCombinedTracingLayer = () => {
1158
1267
  infer_from_source: true,
1159
1268
  rules: []
1160
1269
  },
1270
+ span_relationships: {
1271
+ type: "parent-child"
1272
+ },
1161
1273
  filter: { include: [], exclude: [] },
1162
1274
  performance: { sampling_rate: 1, min_duration: "0ms", max_concurrent: 0 },
1163
1275
  metadata: { fiber_info: true, source_location: true, parent_fiber: true }
@@ -1169,10 +1281,10 @@ var createCombinedTracingLayer = () => {
1169
1281
  logger.log('@atrim/auto-trace: Exporter type is "none", using empty layer');
1170
1282
  return Layer.empty;
1171
1283
  }
1172
- const createSpanProcessor = () => {
1284
+ const createSpanExporter = () => {
1173
1285
  if (exporterConfig.type === "console") {
1174
- logger.log("@atrim/auto-trace: Using ConsoleSpanExporter with SimpleSpanProcessor");
1175
- return new SimpleSpanProcessor(new ConsoleSpanExporter());
1286
+ logger.log("@atrim/auto-trace: Using ConsoleSpanExporter");
1287
+ return new ConsoleSpanExporter();
1176
1288
  }
1177
1289
  const endpoint = exporterConfig.endpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
1178
1290
  logger.log(`@atrim/auto-trace: Using OTLPTraceExporter (${endpoint})`);
@@ -1181,10 +1293,15 @@ var createCombinedTracingLayer = () => {
1181
1293
  };
1182
1294
  if (exporterConfig.headers) {
1183
1295
  otlpConfig.headers = exporterConfig.headers;
1184
- logger.log(`@atrim/auto-trace: Using custom headers: ${Object.keys(exporterConfig.headers).join(", ")}`);
1296
+ logger.log(
1297
+ `@atrim/auto-trace: Using custom headers: ${Object.keys(exporterConfig.headers).join(", ")}`
1298
+ );
1185
1299
  }
1186
- const exporter = new OTLPTraceExporter(otlpConfig);
1187
- if (exporterConfig.processor === "simple") {
1300
+ return new OTLPTraceExporter(otlpConfig);
1301
+ };
1302
+ const createSpanProcessor = () => {
1303
+ const exporter = createSpanExporter();
1304
+ if (exporterConfig.processor === "simple" || exporterConfig.type === "console") {
1188
1305
  logger.log("@atrim/auto-trace: Using SimpleSpanProcessor");
1189
1306
  return new SimpleSpanProcessor(exporter);
1190
1307
  }
@@ -1198,19 +1315,31 @@ var createCombinedTracingLayer = () => {
1198
1315
  maxExportBatchSize: batchConfig.max_export_batch_size
1199
1316
  });
1200
1317
  };
1201
- const sdkLayer = NodeSdk.layer(() => ({
1202
- resource: {
1203
- serviceName,
1204
- serviceVersion
1205
- },
1206
- spanProcessor: createSpanProcessor()
1207
- }));
1318
+ const globalProviderLayer = Layer.effectDiscard(
1319
+ Effect.sync(() => {
1320
+ const provider = new BasicTracerProvider({
1321
+ resource: resourceFromAttributes({
1322
+ [ATTR_SERVICE_NAME]: serviceName,
1323
+ [ATTR_SERVICE_VERSION]: serviceVersion
1324
+ }),
1325
+ spanProcessors: [createSpanProcessor()]
1326
+ });
1327
+ OtelApi.trace.setGlobalTracerProvider(provider);
1328
+ logger.log("@atrim/auto-trace: Global TracerProvider registered");
1329
+ })
1330
+ );
1331
+ const resourceLayer = Resource.layer({
1332
+ serviceName,
1333
+ serviceVersion
1334
+ });
1335
+ const effectTracerLayer = Tracer$1.layerGlobal;
1208
1336
  const supervisor = createAutoTracingSupervisor(autoConfig);
1209
1337
  const supervisorLayer = Supervisor.addSupervisor(supervisor);
1210
1338
  logger.log("@atrim/auto-trace: Combined layer created");
1211
- logger.log(" - HTTP requests: auto-traced via Effect platform");
1212
- logger.log(" - Forked fibers: auto-traced via Supervisor");
1213
- return supervisorLayer.pipe(Layer.provideMerge(Layer.discard(sdkLayer)));
1339
+ logger.log(" - HTTP requests: auto-traced via Effect platform (global provider)");
1340
+ logger.log(" - Forked fibers: auto-traced via Supervisor (global provider)");
1341
+ const tracerWithResource = effectTracerLayer.pipe(Layer.provide(resourceLayer));
1342
+ return Layer.mergeAll(globalProviderLayer, Layer.discard(tracerWithResource), supervisorLayer);
1214
1343
  })
1215
1344
  );
1216
1345
  };