@atrim/instrument-node 0.7.0-b9eaf74-20260108193056 → 0.7.1-dev.14fdea7.20260108220010

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.
@@ -2,6 +2,10 @@
2
2
 
3
3
  var effect = require('effect');
4
4
  var OtelApi = require('@opentelemetry/api');
5
+ var sdkTraceBase = require('@opentelemetry/sdk-trace-base');
6
+ var exporterTraceOtlpHttp = require('@opentelemetry/exporter-trace-otlp-http');
7
+ var resources = require('@opentelemetry/resources');
8
+ var semanticConventions = require('@opentelemetry/semantic-conventions');
5
9
  var FileSystem = require('@effect/platform/FileSystem');
6
10
  var HttpClient = require('@effect/platform/HttpClient');
7
11
  var HttpClientRequest = require('@effect/platform/HttpClientRequest');
@@ -149,6 +153,27 @@ var HttpFilteringConfigSchema = zod.z.object({
149
153
  include_urls: zod.z.array(zod.z.string()).optional()
150
154
  }).optional()
151
155
  });
156
+ var ExporterConfigSchema = zod.z.object({
157
+ // Exporter type: 'otlp' | 'console' | 'none'
158
+ // - 'otlp': Export to OTLP endpoint (production)
159
+ // - 'console': Log spans to console (development)
160
+ // - 'none': No export (disable tracing)
161
+ type: zod.z.enum(["otlp", "console", "none"]).default("otlp"),
162
+ // OTLP endpoint URL (for type: otlp)
163
+ // Defaults to OTEL_EXPORTER_OTLP_ENDPOINT env var or http://localhost:4318
164
+ endpoint: zod.z.string().optional(),
165
+ // Span processor type
166
+ // - 'batch': Batch spans for export (production, lower overhead)
167
+ // - 'simple': Export immediately (development, no batching delay)
168
+ processor: zod.z.enum(["batch", "simple"]).default("batch"),
169
+ // Batch processor settings (for processor: batch)
170
+ batch: zod.z.object({
171
+ // Max time to wait before exporting (milliseconds)
172
+ scheduled_delay_millis: zod.z.number().default(1e3),
173
+ // Max batch size
174
+ max_export_batch_size: zod.z.number().default(100)
175
+ }).optional()
176
+ });
152
177
  var InstrumentationConfigSchema = zod.z.object({
153
178
  version: zod.z.string(),
154
179
  instrumentation: zod.z.object({
@@ -162,10 +187,12 @@ var InstrumentationConfigSchema = zod.z.object({
162
187
  // Enable/disable Effect tracing entirely
163
188
  // When false, EffectInstrumentationLive returns Layer.empty
164
189
  enabled: zod.z.boolean().default(true),
165
- // Exporter mode:
190
+ // Exporter mode (legacy - use exporter.type instead):
166
191
  // - "unified": Use global TracerProvider from Node SDK (recommended, enables filtering)
167
192
  // - "standalone": Use Effect's own OTLP exporter (bypasses Node SDK filtering)
168
193
  exporter: zod.z.enum(["unified", "standalone"]).default("unified"),
194
+ // Exporter configuration (for auto-instrumentation)
195
+ exporter_config: ExporterConfigSchema.optional(),
169
196
  auto_extract_metadata: zod.z.boolean(),
170
197
  auto_isolation: AutoIsolationConfigSchema.optional(),
171
198
  // Auto-instrumentation: automatic tracing of all Effect fibers
@@ -681,6 +708,20 @@ var loadAutoTracingConfigSync = () => {
681
708
  };
682
709
  var AutoTracingConfigLive = effect.Layer.effect(AutoTracingConfig, loadAutoTracingConfig());
683
710
  var AutoTracingConfigLayer = (config) => effect.Layer.succeed(AutoTracingConfig, config);
711
+ var loadFullConfig = (options) => effect.Effect.gen(function* () {
712
+ const config = yield* effect.Effect.tryPromise({
713
+ try: () => loadConfigWithOptions(options),
714
+ catch: (error) => {
715
+ logger.log(`@atrim/auto-trace: Failed to load config: ${error}`);
716
+ return error;
717
+ }
718
+ }).pipe(effect.Effect.catchAll(() => effect.Effect.succeed(null)));
719
+ if (!config) {
720
+ logger.log("@atrim/auto-trace: No config found, using defaults");
721
+ return defaultConfig;
722
+ }
723
+ return config;
724
+ });
684
725
 
685
726
  // src/integrations/effect/auto/supervisor.ts
686
727
  var AutoTracingEnabled = effect.FiberRef.unsafeMake(true);
@@ -693,14 +734,15 @@ var AutoTracingSupervisor = class extends effect.Supervisor.AbstractSupervisor {
693
734
  __publicField(this, "fiberSpans", /* @__PURE__ */ new WeakMap());
694
735
  // WeakMap for fiber start times (for min_duration filtering)
695
736
  __publicField(this, "fiberStartTimes", /* @__PURE__ */ new WeakMap());
696
- // OpenTelemetry tracer
697
- __publicField(this, "tracer");
737
+ // OpenTelemetry tracer - lazily initialized
738
+ __publicField(this, "_tracer", null);
698
739
  // Compiled filter patterns
699
740
  __publicField(this, "includePatterns");
700
741
  __publicField(this, "excludePatterns");
701
742
  // Active fiber count (for max_concurrent limiting)
702
743
  __publicField(this, "activeFiberCount", 0);
703
- this.tracer = OtelApi__namespace.trace.getTracer("@atrim/auto-trace", "1.0.0");
744
+ // Root span for parent context (set by withAutoTracing)
745
+ __publicField(this, "_rootSpan", null);
704
746
  this.includePatterns = (config.filter?.include || []).map((p) => new RegExp(p));
705
747
  this.excludePatterns = (config.filter?.exclude || []).map((p) => new RegExp(p));
706
748
  logger.log("@atrim/auto-trace: Supervisor initialized");
@@ -708,6 +750,21 @@ var AutoTracingSupervisor = class extends effect.Supervisor.AbstractSupervisor {
708
750
  logger.log(` Sampling rate: ${config.performance?.sampling_rate ?? 1}`);
709
751
  logger.log(` Infer from source: ${config.span_naming?.infer_from_source ?? true}`);
710
752
  }
753
+ /**
754
+ * Set the root span for parent context propagation
755
+ */
756
+ setRootSpan(span) {
757
+ this._rootSpan = span;
758
+ }
759
+ /**
760
+ * Get the tracer lazily - this allows time for the NodeSdk layer to register the global provider
761
+ */
762
+ get tracer() {
763
+ if (!this._tracer) {
764
+ this._tracer = OtelApi__namespace.trace.getTracer("@atrim/auto-trace", "1.0.0");
765
+ }
766
+ return this._tracer;
767
+ }
711
768
  /**
712
769
  * Returns the current value (void for this supervisor)
713
770
  */
@@ -732,28 +789,34 @@ var AutoTracingSupervisor = class extends effect.Supervisor.AbstractSupervisor {
732
789
  return;
733
790
  }
734
791
  const nameOverride = effect.FiberRefs.getOrDefault(fiberRefsValue, AutoTracingSpanName);
792
+ const sourceInfo = this.config.span_naming?.infer_from_source ? this.parseStackTrace() : void 0;
735
793
  let spanName;
736
794
  if (effect.Option.isSome(nameOverride)) {
737
795
  spanName = nameOverride.value;
738
796
  } else {
739
- const sourceInfo = this.config.span_naming?.infer_from_source ? this.parseStackTrace() : void 0;
740
797
  spanName = inferSpanName(fiber.id().id, sourceInfo, this.config);
741
798
  }
742
799
  if (!this.shouldTrace(spanName)) {
743
800
  return;
744
801
  }
745
- let parentContext = OtelApi__namespace.context.active();
802
+ let parentContext = OtelApi__namespace.ROOT_CONTEXT;
803
+ let parentFiberId;
746
804
  if (effect.Option.isSome(parent)) {
805
+ parentFiberId = parent.value.id().id;
747
806
  const parentSpan = this.fiberSpans.get(parent.value);
748
807
  if (parentSpan) {
749
- parentContext = OtelApi__namespace.trace.setSpan(parentContext, parentSpan);
808
+ parentContext = OtelApi__namespace.trace.setSpan(OtelApi__namespace.ROOT_CONTEXT, parentSpan);
809
+ } else if (this._rootSpan) {
810
+ parentContext = OtelApi__namespace.trace.setSpan(OtelApi__namespace.ROOT_CONTEXT, this._rootSpan);
750
811
  }
812
+ } else if (this._rootSpan) {
813
+ parentContext = OtelApi__namespace.trace.setSpan(OtelApi__namespace.ROOT_CONTEXT, this._rootSpan);
751
814
  }
752
815
  const span = this.tracer.startSpan(
753
816
  spanName,
754
817
  {
755
818
  kind: OtelApi__namespace.SpanKind.INTERNAL,
756
- attributes: this.getInitialAttributes(fiber)
819
+ attributes: this.getInitialAttributes(fiber, sourceInfo, parentFiberId)
757
820
  },
758
821
  parentContext
759
822
  );
@@ -816,13 +879,22 @@ var AutoTracingSupervisor = class extends effect.Supervisor.AbstractSupervisor {
816
879
  /**
817
880
  * Get initial span attributes for a fiber
818
881
  */
819
- getInitialAttributes(fiber) {
882
+ getInitialAttributes(fiber, sourceInfo, parentFiberId) {
820
883
  const attrs = {
821
884
  "effect.auto_traced": true
822
885
  };
823
886
  if (this.config.metadata?.fiber_info !== false) {
824
887
  attrs["effect.fiber.id"] = fiber.id().id;
825
888
  }
889
+ if (this.config.metadata?.source_location !== false && sourceInfo) {
890
+ attrs["code.function"] = sourceInfo.function;
891
+ attrs["code.filepath"] = sourceInfo.file;
892
+ attrs["code.lineno"] = sourceInfo.line;
893
+ attrs["code.column"] = sourceInfo.column;
894
+ }
895
+ if (this.config.metadata?.parent_fiber !== false && parentFiberId !== void 0) {
896
+ attrs["effect.fiber.parent_id"] = parentFiberId;
897
+ }
826
898
  return attrs;
827
899
  }
828
900
  /**
@@ -891,9 +963,128 @@ var createAutoTracingLayer = (options) => {
891
963
  })
892
964
  );
893
965
  };
966
+ var withAutoTracing = (effect$1, config, mainSpanName) => {
967
+ if (!config.enabled) {
968
+ logger.log("@atrim/auto-trace: Auto-tracing disabled via config");
969
+ return effect$1;
970
+ }
971
+ const supervisor = createAutoTracingSupervisor(config);
972
+ const tracer = OtelApi__namespace.trace.getTracer("@atrim/auto-trace", "1.0.0");
973
+ const spanName = mainSpanName ?? inferSpanName(0, void 0, config);
974
+ const mainSpan = tracer.startSpan(spanName, {
975
+ kind: OtelApi__namespace.SpanKind.INTERNAL,
976
+ attributes: {
977
+ "effect.auto_traced": true,
978
+ "effect.fiber.id": 0,
979
+ // Main fiber
980
+ "effect.main_fiber": true
981
+ }
982
+ });
983
+ supervisor.setRootSpan(mainSpan);
984
+ return effect.Effect.acquireUseRelease(
985
+ // Acquire: return the span (already started)
986
+ effect.Effect.succeed(mainSpan),
987
+ // Use: run the supervised effect
988
+ () => effect.Effect.supervised(supervisor)(effect$1),
989
+ // Release: end the span
990
+ (span, exit) => effect.Effect.sync(() => {
991
+ if (effect.Exit.isSuccess(exit)) {
992
+ span.setStatus({ code: OtelApi__namespace.SpanStatusCode.OK });
993
+ } else {
994
+ span.setStatus({
995
+ code: OtelApi__namespace.SpanStatusCode.ERROR,
996
+ message: "Effect failed"
997
+ });
998
+ }
999
+ span.end();
1000
+ })
1001
+ );
1002
+ };
894
1003
  var AutoTracingLive = createAutoTracingLayer();
895
1004
  var withoutAutoTracing = (effect$1) => effect$1.pipe(effect.Effect.locally(AutoTracingEnabled, false));
896
1005
  var setSpanName = (name) => (effect$1) => effect$1.pipe(effect.Effect.locally(AutoTracingSpanName, effect.Option.some(name)));
1006
+ var createExporterLayer = (exporterConfig, serviceName, serviceVersion) => {
1007
+ const config = exporterConfig ?? {
1008
+ type: "otlp",
1009
+ processor: "batch",
1010
+ batch: {
1011
+ scheduled_delay_millis: 1e3,
1012
+ max_export_batch_size: 100
1013
+ }
1014
+ };
1015
+ if (config.type === "none") {
1016
+ logger.log('@atrim/auto-trace: Exporter type is "none", no spans will be exported');
1017
+ return effect.Layer.empty;
1018
+ }
1019
+ const createSpanExporter = () => {
1020
+ if (config.type === "console") {
1021
+ logger.log("@atrim/auto-trace: Using ConsoleSpanExporter");
1022
+ return new sdkTraceBase.ConsoleSpanExporter();
1023
+ }
1024
+ const endpoint = config.endpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
1025
+ logger.log(`@atrim/auto-trace: Using OTLPTraceExporter (${endpoint})`);
1026
+ return new exporterTraceOtlpHttp.OTLPTraceExporter({
1027
+ url: `${endpoint}/v1/traces`
1028
+ });
1029
+ };
1030
+ const createSpanProcessor = () => {
1031
+ const exporter = createSpanExporter();
1032
+ if (config.processor === "simple" || config.type === "console") {
1033
+ logger.log("@atrim/auto-trace: Using SimpleSpanProcessor");
1034
+ return new sdkTraceBase.SimpleSpanProcessor(exporter);
1035
+ }
1036
+ const batchConfig = config.batch ?? {
1037
+ scheduled_delay_millis: 1e3,
1038
+ max_export_batch_size: 100
1039
+ };
1040
+ logger.log("@atrim/auto-trace: Using BatchSpanProcessor");
1041
+ return new sdkTraceBase.BatchSpanProcessor(exporter, {
1042
+ scheduledDelayMillis: batchConfig.scheduled_delay_millis,
1043
+ maxExportBatchSize: batchConfig.max_export_batch_size
1044
+ });
1045
+ };
1046
+ return effect.Layer.effectDiscard(
1047
+ effect.Effect.sync(() => {
1048
+ const provider = new sdkTraceBase.BasicTracerProvider({
1049
+ resource: resources.resourceFromAttributes({
1050
+ [semanticConventions.ATTR_SERVICE_NAME]: serviceName,
1051
+ [semanticConventions.ATTR_SERVICE_VERSION]: serviceVersion
1052
+ }),
1053
+ spanProcessors: [createSpanProcessor()]
1054
+ });
1055
+ OtelApi__namespace.trace.setGlobalTracerProvider(provider);
1056
+ logger.log("@atrim/auto-trace: Global TracerProvider registered");
1057
+ return () => {
1058
+ provider.shutdown().catch((err) => {
1059
+ logger.log(`@atrim/auto-trace: Error shutting down provider: ${err}`);
1060
+ });
1061
+ };
1062
+ })
1063
+ );
1064
+ };
1065
+ var createFullAutoTracingLayer = () => {
1066
+ return effect.Layer.unwrapEffect(
1067
+ effect.Effect.gen(function* () {
1068
+ const config = yield* loadFullConfig();
1069
+ const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
1070
+ const serviceVersion = process.env.npm_package_version || "1.0.0";
1071
+ const autoConfig = config.effect?.auto_instrumentation;
1072
+ if (!autoConfig?.enabled) {
1073
+ logger.log("@atrim/auto-trace: Auto-instrumentation disabled via config");
1074
+ return effect.Layer.empty;
1075
+ }
1076
+ const exporterConfig = config.effect?.exporter_config;
1077
+ logger.log("@atrim/auto-trace: Full auto-instrumentation enabled");
1078
+ logger.log(` Service: ${serviceName}`);
1079
+ logger.log(` Exporter: ${exporterConfig?.type ?? "otlp"}`);
1080
+ const exporterLayer = createExporterLayer(exporterConfig, serviceName, serviceVersion);
1081
+ const supervisor = createAutoTracingSupervisor(autoConfig);
1082
+ const supervisorLayer = effect.Supervisor.addSupervisor(supervisor);
1083
+ return effect.Layer.mergeAll(exporterLayer, supervisorLayer);
1084
+ })
1085
+ );
1086
+ };
1087
+ var FullAutoTracingLive = createFullAutoTracingLayer();
897
1088
 
898
1089
  exports.AutoTracingConfig = AutoTracingConfig;
899
1090
  exports.AutoTracingConfigLayer = AutoTracingConfigLayer;
@@ -902,14 +1093,17 @@ exports.AutoTracingEnabled = AutoTracingEnabled;
902
1093
  exports.AutoTracingLive = AutoTracingLive;
903
1094
  exports.AutoTracingSpanName = AutoTracingSpanName;
904
1095
  exports.AutoTracingSupervisor = AutoTracingSupervisor;
1096
+ exports.FullAutoTracingLive = FullAutoTracingLive;
905
1097
  exports.createAutoTracingLayer = createAutoTracingLayer;
906
1098
  exports.createAutoTracingSupervisor = createAutoTracingSupervisor;
1099
+ exports.createFullAutoTracingLayer = createFullAutoTracingLayer;
907
1100
  exports.defaultAutoTracingConfig = defaultAutoTracingConfig;
908
1101
  exports.inferSpanName = inferSpanName;
909
1102
  exports.loadAutoTracingConfig = loadAutoTracingConfig;
910
1103
  exports.loadAutoTracingConfigSync = loadAutoTracingConfigSync;
911
1104
  exports.sanitizeSpanName = sanitizeSpanName;
912
1105
  exports.setSpanName = setSpanName;
1106
+ exports.withAutoTracing = withAutoTracing;
913
1107
  exports.withoutAutoTracing = withoutAutoTracing;
914
1108
  //# sourceMappingURL=index.cjs.map
915
1109
  //# sourceMappingURL=index.cjs.map