@atrim/instrument-node 0.5.0 → 0.5.1-21bb978-20260105202350

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,15 +1,17 @@
1
1
  'use strict';
2
2
 
3
3
  var effect = require('effect');
4
+ var Tracer = require('@effect/opentelemetry/Tracer');
5
+ var Resource = require('@effect/opentelemetry/Resource');
4
6
  var Otlp = require('@effect/opentelemetry/Otlp');
5
7
  var platform = require('@effect/platform');
6
8
  var api = require('@opentelemetry/api');
9
+ var semanticConventions = require('@opentelemetry/semantic-conventions');
7
10
  var FileSystem = require('@effect/platform/FileSystem');
8
11
  var HttpClient = require('@effect/platform/HttpClient');
9
12
  var HttpClientRequest = require('@effect/platform/HttpClientRequest');
10
13
  var yaml = require('yaml');
11
14
  var zod = require('zod');
12
- var platformNode = require('@effect/platform-node');
13
15
 
14
16
  function _interopNamespace(e) {
15
17
  if (e && e.__esModule) return e;
@@ -29,6 +31,8 @@ function _interopNamespace(e) {
29
31
  return Object.freeze(n);
30
32
  }
31
33
 
34
+ var Tracer__namespace = /*#__PURE__*/_interopNamespace(Tracer);
35
+ var Resource__namespace = /*#__PURE__*/_interopNamespace(Resource);
32
36
  var Otlp__namespace = /*#__PURE__*/_interopNamespace(Otlp);
33
37
  var HttpClient__namespace = /*#__PURE__*/_interopNamespace(HttpClient);
34
38
  var HttpClientRequest__namespace = /*#__PURE__*/_interopNamespace(HttpClientRequest);
@@ -70,7 +74,20 @@ var HttpFilteringConfigSchema = zod.z.object({
70
74
  // Patterns to ignore for incoming HTTP requests (string patterns only in YAML)
71
75
  ignore_incoming_paths: zod.z.array(zod.z.string()).optional(),
72
76
  // Require parent span for outgoing requests (prevents root spans for HTTP calls)
73
- require_parent_for_outgoing_spans: zod.z.boolean().optional()
77
+ require_parent_for_outgoing_spans: zod.z.boolean().optional(),
78
+ // Trace context propagation configuration
79
+ // Controls which cross-origin requests receive W3C Trace Context headers (traceparent, tracestate)
80
+ propagate_trace_context: zod.z.object({
81
+ // Strategy for trace propagation
82
+ // - "all": Propagate to all cross-origin requests (may cause CORS errors)
83
+ // - "none": Never propagate trace headers
84
+ // - "same-origin": Only propagate to same-origin requests (default, safe)
85
+ // - "patterns": Propagate based on include_urls patterns
86
+ strategy: zod.z.enum(["all", "none", "same-origin", "patterns"]).default("same-origin"),
87
+ // URL patterns to include when strategy is "patterns"
88
+ // Supports regex patterns (e.g., "^https://api\\.myapp\\.com")
89
+ include_urls: zod.z.array(zod.z.string()).optional()
90
+ }).optional()
74
91
  });
75
92
  var InstrumentationConfigSchema = zod.z.object({
76
93
  version: zod.z.string(),
@@ -82,11 +99,56 @@ var InstrumentationConfigSchema = zod.z.object({
82
99
  ignore_patterns: zod.z.array(PatternConfigSchema)
83
100
  }),
84
101
  effect: zod.z.object({
102
+ // Enable/disable Effect tracing entirely
103
+ // When false, EffectInstrumentationLive returns Layer.empty
104
+ enabled: zod.z.boolean().default(true),
105
+ // Exporter mode:
106
+ // - "unified": Use global TracerProvider from Node SDK (recommended, enables filtering)
107
+ // - "standalone": Use Effect's own OTLP exporter (bypasses Node SDK filtering)
108
+ exporter: zod.z.enum(["unified", "standalone"]).default("unified"),
85
109
  auto_extract_metadata: zod.z.boolean(),
110
+ // Auto-bridge OpenTelemetry context to Effect spans
111
+ // When true, Effect spans automatically become children of the active OTel span
112
+ // (e.g., HTTP request span from auto-instrumentation)
113
+ // This is essential for proper trace hierarchy when using Effect with HTTP frameworks
114
+ auto_bridge_context: zod.z.boolean().default(true),
86
115
  auto_isolation: AutoIsolationConfigSchema.optional()
87
116
  }).optional(),
88
117
  http: HttpFilteringConfigSchema.optional()
89
118
  });
119
+ var defaultConfig = {
120
+ version: "1.0",
121
+ instrumentation: {
122
+ enabled: true,
123
+ logging: "on",
124
+ description: "Default instrumentation configuration",
125
+ instrument_patterns: [
126
+ { pattern: "^app\\.", enabled: true, description: "Application operations" },
127
+ { pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
128
+ { pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
129
+ ],
130
+ ignore_patterns: [
131
+ { pattern: "^test\\.", description: "Test utilities" },
132
+ { pattern: "^internal\\.", description: "Internal operations" },
133
+ { pattern: "^health\\.", description: "Health checks" }
134
+ ]
135
+ },
136
+ effect: {
137
+ enabled: true,
138
+ exporter: "unified",
139
+ auto_extract_metadata: true,
140
+ auto_bridge_context: true
141
+ }
142
+ };
143
+ function parseAndValidateConfig(content) {
144
+ let parsed;
145
+ if (typeof content === "string") {
146
+ parsed = yaml.parse(content);
147
+ } else {
148
+ parsed = content;
149
+ }
150
+ return InstrumentationConfigSchema.parse(parsed);
151
+ }
90
152
  (class extends effect.Data.TaggedError("ConfigError") {
91
153
  get message() {
92
154
  return this.reason;
@@ -264,7 +326,7 @@ var makeConfigLoader = effect.Effect.gen(function* () {
264
326
  })
265
327
  });
266
328
  });
267
- var ConfigLoaderLive = effect.Layer.effect(ConfigLoader, makeConfigLoader);
329
+ effect.Layer.effect(ConfigLoader, makeConfigLoader);
268
330
  var PatternMatcher = class {
269
331
  constructor(config) {
270
332
  __publicField(this, "ignorePatterns", []);
@@ -412,83 +474,58 @@ var Logger = class {
412
474
  }
413
475
  };
414
476
  var logger = new Logger();
415
- var NodeConfigLoaderLive = ConfigLoaderLive.pipe(
416
- effect.Layer.provide(effect.Layer.mergeAll(platformNode.NodeContext.layer, platform.FetchHttpClient.layer))
417
- );
418
- var cachedLoaderPromise = null;
419
- function getCachedLoader() {
420
- if (!cachedLoaderPromise) {
421
- cachedLoaderPromise = effect.Effect.runPromise(
422
- effect.Effect.gen(function* () {
423
- return yield* ConfigLoader;
424
- }).pipe(effect.Effect.provide(NodeConfigLoaderLive))
425
- );
477
+ async function loadFromFile(filePath) {
478
+ const { readFile } = await import('fs/promises');
479
+ const content = await readFile(filePath, "utf-8");
480
+ return parseAndValidateConfig(content);
481
+ }
482
+ async function loadFromUrl(url) {
483
+ const response = await fetch(url);
484
+ if (!response.ok) {
485
+ throw new Error(`Failed to fetch config from ${url}: ${response.statusText}`);
426
486
  }
427
- return cachedLoaderPromise;
487
+ const content = await response.text();
488
+ return parseAndValidateConfig(content);
428
489
  }
429
- async function loadConfig(uri, options) {
430
- if (options?.cacheTimeout === 0) {
431
- const program = effect.Effect.gen(function* () {
432
- const loader2 = yield* ConfigLoader;
433
- return yield* loader2.loadFromUri(uri);
434
- });
435
- return effect.Effect.runPromise(program.pipe(effect.Effect.provide(NodeConfigLoaderLive)));
490
+ async function loadConfig(uri, _options) {
491
+ if (uri.startsWith("http://") || uri.startsWith("https://")) {
492
+ return loadFromUrl(uri);
436
493
  }
437
- const loader = await getCachedLoader();
438
- return effect.Effect.runPromise(loader.loadFromUri(uri));
494
+ if (uri.startsWith("file://")) {
495
+ const filePath = uri.slice(7);
496
+ return loadFromFile(filePath);
497
+ }
498
+ return loadFromFile(uri);
439
499
  }
440
500
  async function loadConfigFromInline(content) {
441
- const loader = await getCachedLoader();
442
- return effect.Effect.runPromise(loader.loadFromInline(content));
443
- }
444
- function getDefaultConfig() {
445
- return {
446
- version: "1.0",
447
- instrumentation: {
448
- enabled: true,
449
- logging: "on",
450
- description: "Default instrumentation configuration",
451
- instrument_patterns: [
452
- { pattern: "^app\\.", enabled: true, description: "Application operations" },
453
- { pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
454
- { pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
455
- ],
456
- ignore_patterns: [
457
- { pattern: "^test\\.", description: "Test utilities" },
458
- { pattern: "^internal\\.", description: "Internal operations" },
459
- { pattern: "^health\\.", description: "Health checks" }
460
- ]
461
- },
462
- effect: {
463
- auto_extract_metadata: true
464
- }
465
- };
501
+ return parseAndValidateConfig(content);
466
502
  }
467
503
  async function loadConfigWithOptions(options = {}) {
468
- const loadOptions = options.cacheTimeout !== void 0 ? { cacheTimeout: options.cacheTimeout } : void 0;
469
504
  if (options.config) {
470
505
  return loadConfigFromInline(options.config);
471
506
  }
472
507
  const envConfigPath = process.env.ATRIM_INSTRUMENTATION_CONFIG;
473
508
  if (envConfigPath) {
474
- return loadConfig(envConfigPath, loadOptions);
509
+ return loadConfig(envConfigPath);
475
510
  }
476
511
  if (options.configUrl) {
477
- return loadConfig(options.configUrl, loadOptions);
512
+ return loadConfig(options.configUrl);
478
513
  }
479
514
  if (options.configPath) {
480
- return loadConfig(options.configPath, loadOptions);
515
+ return loadConfig(options.configPath);
481
516
  }
482
517
  const { existsSync } = await import('fs');
483
518
  const { join } = await import('path');
484
519
  const defaultPath = join(process.cwd(), "instrumentation.yaml");
485
520
  if (existsSync(defaultPath)) {
486
- return loadConfig(defaultPath, loadOptions);
521
+ return loadConfig(defaultPath);
487
522
  }
488
- return getDefaultConfig();
523
+ return defaultConfig;
489
524
  }
490
525
 
491
526
  // src/integrations/effect/effect-tracer.ts
527
+ var SDK_NAME = "@effect/opentelemetry";
528
+ var ATTR_TELEMETRY_EXPORTER_MODE = "telemetry.exporter.mode";
492
529
  function createEffectInstrumentation(options = {}) {
493
530
  return effect.Layer.unwrapEffect(
494
531
  effect.Effect.gen(function* () {
@@ -499,106 +536,277 @@ function createEffectInstrumentation(options = {}) {
499
536
  message: error instanceof Error ? error.message : String(error)
500
537
  })
501
538
  });
539
+ const effectEnabled = process.env.OTEL_EFFECT_ENABLED !== "false" && (config.effect?.enabled ?? true);
540
+ if (!effectEnabled) {
541
+ logger.log("@atrim/instrumentation/effect: Effect tracing disabled via config");
542
+ return effect.Layer.empty;
543
+ }
502
544
  yield* effect.Effect.sync(() => {
503
545
  const loggingLevel = config.instrumentation.logging || "on";
504
546
  logger.setLevel(loggingLevel);
505
547
  });
506
548
  yield* effect.Effect.sync(() => initializePatternMatcher(config));
507
- const otlpEndpoint = options.otlpEndpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
508
549
  const serviceName = options.serviceName || process.env.OTEL_SERVICE_NAME || "effect-service";
509
550
  const serviceVersion = options.serviceVersion || process.env.npm_package_version || "1.0.0";
510
- const autoExtractMetadata = options.autoExtractMetadata ?? config.effect?.auto_extract_metadata ?? true;
511
- const continueExistingTraces = options.continueExistingTraces ?? true;
512
- logger.log("\u{1F50D} Effect OpenTelemetry instrumentation");
513
- logger.log(` \u{1F4E1} Endpoint: ${otlpEndpoint}`);
514
- logger.log(` \u{1F3F7}\uFE0F Service: ${serviceName}`);
515
- logger.log(` \u2705 Auto metadata extraction: ${autoExtractMetadata}`);
516
- logger.log(` \u2705 Continue existing traces: ${continueExistingTraces}`);
517
- const otlpLayer = Otlp__namespace.layer({
518
- baseUrl: otlpEndpoint,
519
- resource: {
520
- serviceName,
521
- serviceVersion,
522
- attributes: {
523
- "platform.component": "effect",
524
- "effect.auto_metadata": autoExtractMetadata,
525
- "effect.context_propagation": continueExistingTraces
526
- }
527
- },
528
- // Bridge Effect context to OpenTelemetry global context
529
- // This is essential for context propagation to work properly
530
- tracerContext: (f, span) => {
531
- if (span._tag !== "Span") {
532
- return f();
551
+ const exporterMode = options.exporterMode ?? config.effect?.exporter ?? "unified";
552
+ const resourceAttributes = {
553
+ "platform.component": "effect",
554
+ [semanticConventions.ATTR_TELEMETRY_SDK_LANGUAGE]: semanticConventions.TELEMETRY_SDK_LANGUAGE_VALUE_NODEJS,
555
+ [semanticConventions.ATTR_TELEMETRY_SDK_NAME]: SDK_NAME,
556
+ [ATTR_TELEMETRY_EXPORTER_MODE]: exporterMode
557
+ };
558
+ if (exporterMode === "standalone") {
559
+ const otlpEndpoint = options.otlpEndpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
560
+ logger.log("Effect OpenTelemetry instrumentation (standalone)");
561
+ logger.log(` Service: ${serviceName}`);
562
+ logger.log(` Endpoint: ${otlpEndpoint}`);
563
+ logger.log(" WARNING: Standalone mode bypasses Node SDK filtering");
564
+ return Otlp__namespace.layer({
565
+ baseUrl: otlpEndpoint,
566
+ resource: {
567
+ serviceName,
568
+ serviceVersion,
569
+ attributes: resourceAttributes
570
+ },
571
+ // Bridge Effect context to OpenTelemetry global context
572
+ tracerContext: (f, span) => {
573
+ if (span._tag !== "Span") {
574
+ return f();
575
+ }
576
+ const spanContext = {
577
+ traceId: span.traceId,
578
+ spanId: span.spanId,
579
+ traceFlags: span.sampled ? api.TraceFlags.SAMPLED : api.TraceFlags.NONE
580
+ };
581
+ const otelSpan = api.trace.wrapSpanContext(spanContext);
582
+ return api.context.with(api.trace.setSpan(api.context.active(), otelSpan), f);
533
583
  }
534
- const spanContext = {
535
- traceId: span.traceId,
536
- spanId: span.spanId,
537
- traceFlags: span.sampled ? api.TraceFlags.SAMPLED : api.TraceFlags.NONE
538
- };
539
- const otelSpan = api.trace.wrapSpanContext(spanContext);
540
- return api.context.with(api.trace.setSpan(api.context.active(), otelSpan), f);
541
- }
542
- }).pipe(effect.Layer.provide(platform.FetchHttpClient.layer));
543
- if (autoExtractMetadata) {
544
- return otlpLayer;
584
+ }).pipe(effect.Layer.provide(platform.FetchHttpClient.layer));
585
+ } else {
586
+ logger.log("Effect OpenTelemetry instrumentation (unified)");
587
+ logger.log(` Service: ${serviceName}`);
588
+ logger.log(" Using global TracerProvider for span export");
589
+ return Tracer__namespace.layerGlobal.pipe(
590
+ effect.Layer.provide(
591
+ Resource__namespace.layer({
592
+ serviceName,
593
+ serviceVersion,
594
+ attributes: resourceAttributes
595
+ })
596
+ )
597
+ );
545
598
  }
546
- return otlpLayer;
547
599
  })
548
600
  ).pipe(effect.Layer.orDie);
549
601
  }
550
602
  var EffectInstrumentationLive = effect.Effect.sync(() => {
551
- const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
552
603
  const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
553
604
  const serviceVersion = process.env.npm_package_version || "1.0.0";
554
605
  logger.minimal(`@atrim/instrumentation/effect: Effect tracing enabled (${serviceName})`);
555
- logger.log("\u{1F50D} Effect OpenTelemetry tracer");
556
- logger.log(` \u{1F4E1} Endpoint: ${endpoint}`);
557
- logger.log(` \u{1F3F7}\uFE0F Service: ${serviceName}`);
558
- return Otlp__namespace.layer({
559
- baseUrl: endpoint,
560
- resource: {
561
- serviceName,
562
- serviceVersion,
563
- attributes: {
564
- "platform.component": "effect"
565
- }
566
- },
567
- // CRITICAL: Bridge Effect context to OpenTelemetry global context
568
- // This allows NodeSDK auto-instrumentation to see Effect spans as parent spans
569
- tracerContext: (f, span) => {
570
- if (span._tag !== "Span") {
571
- return f();
572
- }
573
- const spanContext = {
574
- traceId: span.traceId,
575
- spanId: span.spanId,
576
- traceFlags: span.sampled ? api.TraceFlags.SAMPLED : api.TraceFlags.NONE
577
- };
578
- const otelSpan = api.trace.wrapSpanContext(spanContext);
579
- return api.context.with(api.trace.setSpan(api.context.active(), otelSpan), f);
580
- }
581
- }).pipe(effect.Layer.provide(platform.FetchHttpClient.layer));
606
+ logger.log("Effect OpenTelemetry tracer (unified)");
607
+ logger.log(` Service: ${serviceName}`);
608
+ return Tracer__namespace.layerGlobal.pipe(
609
+ effect.Layer.provide(
610
+ Resource__namespace.layer({
611
+ serviceName,
612
+ serviceVersion,
613
+ attributes: {
614
+ "platform.component": "effect",
615
+ [semanticConventions.ATTR_TELEMETRY_SDK_LANGUAGE]: semanticConventions.TELEMETRY_SDK_LANGUAGE_VALUE_NODEJS,
616
+ [semanticConventions.ATTR_TELEMETRY_SDK_NAME]: SDK_NAME,
617
+ [ATTR_TELEMETRY_EXPORTER_MODE]: "unified"
618
+ }
619
+ })
620
+ )
621
+ );
582
622
  }).pipe(effect.Layer.unwrapEffect);
583
-
584
- // src/integrations/effect/effect-helpers.ts
585
- function annotateUser(_userId, _email) {
623
+ var withOtelParentSpan = (effect) => {
624
+ const currentSpan = api.trace.getSpan(api.context.active());
625
+ if (currentSpan) {
626
+ const spanContext = currentSpan.spanContext();
627
+ return Tracer__namespace.withSpanContext(spanContext)(effect);
628
+ }
629
+ return effect;
630
+ };
631
+ var getCurrentOtelSpanContext = effect.Effect.sync(() => {
632
+ const currentSpan = api.trace.getSpan(api.context.active());
633
+ return currentSpan?.spanContext();
634
+ });
635
+ var getOtelParentSpan = effect.Effect.sync(
636
+ () => {
637
+ const currentSpan = api.trace.getSpan(api.context.active());
638
+ if (!currentSpan) {
639
+ return void 0;
640
+ }
641
+ const spanContext = currentSpan.spanContext();
642
+ return Tracer__namespace.makeExternalSpan({
643
+ traceId: spanContext.traceId,
644
+ spanId: spanContext.spanId,
645
+ traceFlags: spanContext.traceFlags,
646
+ traceState: spanContext.traceState?.serialize()
647
+ });
648
+ }
649
+ );
650
+ var runWithOtelContext = (effect$1) => {
651
+ return effect.Effect.flatMap(
652
+ Tracer__namespace.currentOtelSpan,
653
+ (otelSpan) => effect.Effect.async((resume) => {
654
+ api.context.with(api.trace.setSpan(api.context.active(), otelSpan), () => {
655
+ effect.Effect.runPromiseExit(effect$1).then((exit) => {
656
+ if (exit._tag === "Success") {
657
+ resume(effect.Effect.succeed(exit.value));
658
+ } else {
659
+ resume(effect.Effect.failCause(exit.cause));
660
+ }
661
+ });
662
+ });
663
+ })
664
+ ).pipe(
665
+ // If no OTel span is available, just run the effect normally
666
+ effect.Effect.catchAll(() => effect$1)
667
+ );
668
+ };
669
+ var withFullOtelBridging = (effect) => {
670
+ return withOtelParentSpan(effect);
671
+ };
672
+ function annotateUser(userId, email, username) {
673
+ const attributes = {
674
+ "user.id": userId
675
+ };
676
+ if (email) attributes["user.email"] = email;
677
+ if (username) attributes["user.name"] = username;
678
+ return effect.Effect.annotateCurrentSpan(attributes);
586
679
  }
587
- function annotateDataSize(_bytes, _count) {
680
+ function annotateDataSize(bytes, items, compressionRatio) {
681
+ const attributes = {
682
+ "data.size.bytes": bytes,
683
+ "data.size.items": items
684
+ };
685
+ if (compressionRatio !== void 0) {
686
+ attributes["data.compression.ratio"] = compressionRatio;
687
+ }
688
+ return effect.Effect.annotateCurrentSpan(attributes);
588
689
  }
589
- function annotateBatch(_size, _batchSize) {
690
+ function annotateBatch(totalItems, batchSize, successCount, failureCount) {
691
+ const attributes = {
692
+ "batch.size": batchSize,
693
+ "batch.total_items": totalItems,
694
+ "batch.count": Math.ceil(totalItems / batchSize)
695
+ };
696
+ if (successCount !== void 0) {
697
+ attributes["batch.success_count"] = successCount;
698
+ }
699
+ if (failureCount !== void 0) {
700
+ attributes["batch.failure_count"] = failureCount;
701
+ }
702
+ return effect.Effect.annotateCurrentSpan(attributes);
590
703
  }
591
- function annotateLLM(_model, _operation, _inputTokens, _outputTokens) {
704
+ function annotateLLM(model, provider, tokens) {
705
+ const attributes = {
706
+ "llm.model": model,
707
+ "llm.provider": provider
708
+ };
709
+ if (tokens) {
710
+ if (tokens.prompt !== void 0) attributes["llm.tokens.prompt"] = tokens.prompt;
711
+ if (tokens.completion !== void 0) attributes["llm.tokens.completion"] = tokens.completion;
712
+ if (tokens.total !== void 0) attributes["llm.tokens.total"] = tokens.total;
713
+ }
714
+ return effect.Effect.annotateCurrentSpan(attributes);
592
715
  }
593
- function annotateQuery(_query, _database) {
716
+ function annotateQuery(query, duration, rowCount, database) {
717
+ const attributes = {
718
+ "db.statement": query.length > 1e3 ? query.substring(0, 1e3) + "..." : query
719
+ };
720
+ if (duration !== void 0) attributes["db.duration.ms"] = duration;
721
+ if (rowCount !== void 0) attributes["db.row_count"] = rowCount;
722
+ if (database) attributes["db.name"] = database;
723
+ return effect.Effect.annotateCurrentSpan(attributes);
594
724
  }
595
- function annotateHttpRequest(_method, _url, _statusCode) {
725
+ function annotateHttpRequest(method, url, statusCode, contentLength) {
726
+ const attributes = {
727
+ "http.method": method,
728
+ "http.url": url
729
+ };
730
+ if (statusCode !== void 0) attributes["http.status_code"] = statusCode;
731
+ if (contentLength !== void 0) attributes["http.response.content_length"] = contentLength;
732
+ return effect.Effect.annotateCurrentSpan(attributes);
596
733
  }
597
- function annotateError(_error, _context) {
734
+ function annotateError(error, recoverable, errorType) {
735
+ const errorMessage = typeof error === "string" ? error : error.message;
736
+ const errorStack = typeof error === "string" ? void 0 : error.stack;
737
+ const attributes = {
738
+ "error.message": errorMessage,
739
+ "error.recoverable": recoverable
740
+ };
741
+ if (errorType) attributes["error.type"] = errorType;
742
+ if (errorStack) attributes["error.stack"] = errorStack;
743
+ return effect.Effect.annotateCurrentSpan(attributes);
598
744
  }
599
- function annotatePriority(_priority) {
745
+ function annotatePriority(priority, reason) {
746
+ const attributes = {
747
+ "operation.priority": priority
748
+ };
749
+ if (reason) attributes["operation.priority.reason"] = reason;
750
+ return effect.Effect.annotateCurrentSpan(attributes);
600
751
  }
601
- function annotateCache(_operation, _hit) {
752
+ function annotateCache(hit, key, ttl) {
753
+ const attributes = {
754
+ "cache.hit": hit,
755
+ "cache.key": key
756
+ };
757
+ if (ttl !== void 0) attributes["cache.ttl.seconds"] = ttl;
758
+ return effect.Effect.annotateCurrentSpan(attributes);
759
+ }
760
+ function extractEffectMetadata() {
761
+ return effect.Effect.gen(function* () {
762
+ const metadata = {};
763
+ const currentFiber = effect.Fiber.getCurrentFiber();
764
+ if (effect.Option.isSome(currentFiber)) {
765
+ const fiber = currentFiber.value;
766
+ const fiberId = fiber.id();
767
+ metadata["effect.fiber.id"] = effect.FiberId.threadName(fiberId);
768
+ const status = yield* effect.Fiber.status(fiber);
769
+ if (status._tag) {
770
+ metadata["effect.fiber.status"] = status._tag;
771
+ }
772
+ }
773
+ const parentSpanResult = yield* effect.Effect.currentSpan.pipe(
774
+ effect.Effect.option
775
+ // Convert NoSuchElementException to Option
776
+ );
777
+ if (effect.Option.isSome(parentSpanResult)) {
778
+ const parentSpan = parentSpanResult.value;
779
+ metadata["effect.operation.nested"] = true;
780
+ metadata["effect.operation.root"] = false;
781
+ if (parentSpan.spanId) {
782
+ metadata["effect.parent.span.id"] = parentSpan.spanId;
783
+ }
784
+ if (parentSpan.name) {
785
+ metadata["effect.parent.span.name"] = parentSpan.name;
786
+ }
787
+ if (parentSpan.traceId) {
788
+ metadata["effect.parent.trace.id"] = parentSpan.traceId;
789
+ }
790
+ } else {
791
+ metadata["effect.operation.nested"] = false;
792
+ metadata["effect.operation.root"] = true;
793
+ }
794
+ return metadata;
795
+ });
796
+ }
797
+ function autoEnrichSpan() {
798
+ return effect.Effect.gen(function* () {
799
+ const metadata = yield* extractEffectMetadata();
800
+ yield* effect.Effect.annotateCurrentSpan(metadata);
801
+ });
802
+ }
803
+ function withAutoEnrichedSpan(spanName, options) {
804
+ return (self) => {
805
+ return effect.Effect.gen(function* () {
806
+ yield* autoEnrichSpan();
807
+ return yield* self;
808
+ }).pipe(effect.Effect.withSpan(spanName, options));
809
+ };
602
810
  }
603
811
  var createLogicalParentLink = (parentSpan, useSpanLinks) => {
604
812
  if (!useSpanLinks) {
@@ -737,8 +945,16 @@ exports.annotatePriority = annotatePriority;
737
945
  exports.annotateQuery = annotateQuery;
738
946
  exports.annotateSpawnedTasks = annotateSpawnedTasks;
739
947
  exports.annotateUser = annotateUser;
948
+ exports.autoEnrichSpan = autoEnrichSpan;
740
949
  exports.createEffectInstrumentation = createEffectInstrumentation;
950
+ exports.extractEffectMetadata = extractEffectMetadata;
951
+ exports.getCurrentOtelSpanContext = getCurrentOtelSpanContext;
952
+ exports.getOtelParentSpan = getOtelParentSpan;
741
953
  exports.runIsolated = runIsolated;
954
+ exports.runWithOtelContext = runWithOtelContext;
742
955
  exports.runWithSpan = runWithSpan;
956
+ exports.withAutoEnrichedSpan = withAutoEnrichedSpan;
957
+ exports.withFullOtelBridging = withFullOtelBridging;
958
+ exports.withOtelParentSpan = withOtelParentSpan;
743
959
  //# sourceMappingURL=index.cjs.map
744
960
  //# sourceMappingURL=index.cjs.map