@atrim/instrument-node 0.4.1 → 0.5.0-c05e3a1-20251119131235

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atrim/instrument-node",
3
- "version": "0.4.1",
3
+ "version": "0.5.0-c05e3a1-20251119131235",
4
4
  "description": "OpenTelemetry instrumentation for Node.js with centralized YAML configuration",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -82,7 +82,7 @@
82
82
  "tsx": "^4.7.0",
83
83
  "typescript": "^5.7.2",
84
84
  "vitest": "^4.0.8",
85
- "@atrim/instrument-core": "0.4.1"
85
+ "@atrim/instrument-core": "0.5.0"
86
86
  },
87
87
  "peerDependencies": {
88
88
  "@effect/opentelemetry": ">=0.40.0",
@@ -122,10 +122,10 @@
122
122
  "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
123
123
  "format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\"",
124
124
  "clean": "rm -rf target",
125
- "publish:dev:version": "npm version $(git describe --tags --abbrev=0 | sed 's/^.*@//' | sed 's/^v//')-$(git rev-parse --short HEAD)-$(date -u +%Y%m%d%H%M%S) --no-git-tag-version",
125
+ "publish:dev:version": "pnpm version $(node -p \"require('./package.json').version\")-$(git rev-parse --short HEAD)-$(date -u +%Y%m%d%H%M%S) --no-git-tag-version",
126
126
  "publish:dev:save": "node -p \"require('./package.json').version\" > .version",
127
127
  "publish:dev:publish": "pnpm build && pnpm publish --tag dev --access public --no-git-checks",
128
- "publish:dev:reset": "npm version 1.0.0 --no-git-tag-version",
128
+ "publish:dev:reset": "pnpm version 0.5.0 --no-git-tag-version",
129
129
  "publish:dev": "pnpm publish:dev:version && pnpm publish:dev:save && pnpm publish:dev:publish && pnpm publish:dev:reset"
130
130
  }
131
131
  }
@@ -82,7 +82,20 @@ var HttpFilteringConfigSchema = zod.z.object({
82
82
  // Patterns to ignore for incoming HTTP requests (string patterns only in YAML)
83
83
  ignore_incoming_paths: zod.z.array(zod.z.string()).optional(),
84
84
  // Require parent span for outgoing requests (prevents root spans for HTTP calls)
85
- require_parent_for_outgoing_spans: zod.z.boolean().optional()
85
+ require_parent_for_outgoing_spans: zod.z.boolean().optional(),
86
+ // Trace context propagation configuration
87
+ // Controls which cross-origin requests receive W3C Trace Context headers (traceparent, tracestate)
88
+ propagate_trace_context: zod.z.object({
89
+ // Strategy for trace propagation
90
+ // - "all": Propagate to all cross-origin requests (may cause CORS errors)
91
+ // - "none": Never propagate trace headers
92
+ // - "same-origin": Only propagate to same-origin requests (default, safe)
93
+ // - "patterns": Propagate based on include_urls patterns
94
+ strategy: zod.z.enum(["all", "none", "same-origin", "patterns"]).default("same-origin"),
95
+ // URL patterns to include when strategy is "patterns"
96
+ // Supports regex patterns (e.g., "^https://api\\.myapp\\.com")
97
+ include_urls: zod.z.array(zod.z.string()).optional()
98
+ }).optional()
86
99
  });
87
100
  var InstrumentationConfigSchema = zod.z.object({
88
101
  version: zod.z.string(),
@@ -693,15 +706,6 @@ var getServiceInfoWithFallback = detectServiceInfo.pipe(
693
706
  })
694
707
  )
695
708
  );
696
- async function detectServiceInfoAsync() {
697
- return effect.Effect.runPromise(getServiceInfoWithFallback);
698
- }
699
- async function getServiceNameAsync() {
700
- return effect.Effect.runPromise(getServiceName);
701
- }
702
- async function getServiceVersionAsync() {
703
- return effect.Effect.runPromise(getServiceVersion);
704
- }
705
709
  var NodeConfigLoaderLive = ConfigLoaderLive.pipe(
706
710
  effect.Layer.provide(effect.Layer.mergeAll(platformNode.NodeContext.layer, platform.FetchHttpClient.layer))
707
711
  );
@@ -783,7 +787,7 @@ async function loadConfigWithOptions(options = {}) {
783
787
 
784
788
  // src/core/sdk-initializer.ts
785
789
  var sdkInstance = null;
786
- var initializationPromise = null;
790
+ var initializationDeferred = null;
787
791
  function buildHttpInstrumentationConfig(options, config, _otlpEndpoint) {
788
792
  const httpConfig = { enabled: true };
789
793
  const programmaticPatterns = options.http?.ignoreOutgoingUrls || [];
@@ -899,27 +903,38 @@ function isTracingAlreadyInitialized() {
899
903
  return false;
900
904
  }
901
905
  }
902
- async function initializeSdk(options = {}) {
906
+ var initializeSdkEffect = (options = {}) => effect.Effect.gen(function* () {
903
907
  if (sdkInstance) {
904
908
  logger.warn("@atrim/instrumentation: SDK already initialized. Returning existing instance.");
905
909
  return sdkInstance;
906
910
  }
907
- if (initializationPromise) {
911
+ if (initializationDeferred) {
908
912
  logger.log(
909
- "@atrim/instrumentation: SDK already initialized, waiting for initialization to complete..."
913
+ "@atrim/instrumentation: SDK initialization in progress, waiting for completion..."
910
914
  );
911
- return initializationPromise;
912
- }
913
- initializationPromise = performInitialization(options);
914
- try {
915
- const result = await initializationPromise;
916
- return result;
917
- } finally {
918
- initializationPromise = null;
919
- }
920
- }
921
- async function performInitialization(options) {
922
- const config = await loadConfigWithOptions(options);
915
+ return yield* effect.Deferred.await(initializationDeferred);
916
+ }
917
+ const deferred = yield* effect.Deferred.make();
918
+ initializationDeferred = deferred;
919
+ const result = yield* performInitializationEffect(options).pipe(
920
+ effect.Effect.tap((sdk) => effect.Deferred.succeed(deferred, sdk)),
921
+ effect.Effect.tapError((error) => effect.Deferred.fail(deferred, error)),
922
+ effect.Effect.ensuring(
923
+ effect.Effect.sync(() => {
924
+ initializationDeferred = null;
925
+ })
926
+ )
927
+ );
928
+ return result;
929
+ });
930
+ var performInitializationEffect = (options) => effect.Effect.gen(function* () {
931
+ const config = yield* effect.Effect.tryPromise({
932
+ try: () => loadConfigWithOptions(options),
933
+ catch: (error) => new InitializationError2({
934
+ reason: "Failed to load configuration",
935
+ cause: error
936
+ })
937
+ });
923
938
  const loggingLevel = config.instrumentation.logging || "on";
924
939
  logger.setLevel(loggingLevel);
925
940
  const alreadyInitialized = isTracingAlreadyInitialized();
@@ -935,14 +950,25 @@ async function performInitialization(options) {
935
950
  logger.log("");
936
951
  return null;
937
952
  }
938
- const serviceInfo = await detectServiceInfoAsync();
953
+ const serviceInfo = yield* detectServiceInfo.pipe(
954
+ effect.Effect.catchAll(
955
+ () => effect.Effect.succeed({
956
+ name: "unknown-service",
957
+ version: void 0
958
+ })
959
+ )
960
+ );
939
961
  const serviceName = options.serviceName || serviceInfo.name;
940
962
  const serviceVersion = options.serviceVersion || serviceInfo.version;
941
- const rawExporter = createOtlpExporter(options.otlp);
942
- const exporter = new SafeSpanExporter(rawExporter);
963
+ const rawExporter = yield* effect.Effect.sync(() => createOtlpExporter(options.otlp));
964
+ const exporter = yield* effect.Effect.sync(() => new SafeSpanExporter(rawExporter));
943
965
  const useSimpleProcessor = process.env.NODE_ENV === "test" || process.env.OTEL_USE_SIMPLE_PROCESSOR === "true";
944
- const baseProcessor = useSimpleProcessor ? new sdkTraceBase.SimpleSpanProcessor(exporter) : new sdkTraceBase.BatchSpanProcessor(exporter);
945
- const patternProcessor = new PatternSpanProcessor(config, baseProcessor);
966
+ const baseProcessor = yield* effect.Effect.sync(
967
+ () => useSimpleProcessor ? new sdkTraceBase.SimpleSpanProcessor(exporter) : new sdkTraceBase.BatchSpanProcessor(exporter)
968
+ );
969
+ const patternProcessor = yield* effect.Effect.sync(
970
+ () => new PatternSpanProcessor(config, baseProcessor)
971
+ );
946
972
  const instrumentations = [];
947
973
  const hasWebFramework = hasWebFrameworkInstalled();
948
974
  const enableAutoInstrumentation = shouldEnableAutoInstrumentation(
@@ -955,15 +981,11 @@ async function performInitialization(options) {
955
981
  const undiciConfig = buildUndiciInstrumentationConfig(options, config);
956
982
  instrumentations.push(
957
983
  ...autoInstrumentationsNode.getNodeAutoInstrumentations({
958
- // Enable HTTP instrumentation with filtering (for http/https modules)
959
984
  "@opentelemetry/instrumentation-http": httpConfig,
960
- // Enable undici instrumentation with filtering (for fetch API)
961
985
  "@opentelemetry/instrumentation-undici": undiciConfig,
962
- // Enable web framework instrumentations
963
986
  "@opentelemetry/instrumentation-express": { enabled: true },
964
987
  "@opentelemetry/instrumentation-fastify": { enabled: true },
965
988
  "@opentelemetry/instrumentation-koa": { enabled: true },
966
- // Disable noisy instrumentations by default
967
989
  "@opentelemetry/instrumentation-fs": { enabled: false },
968
990
  "@opentelemetry/instrumentation-dns": { enabled: false }
969
991
  })
@@ -991,18 +1013,20 @@ async function performInitialization(options) {
991
1013
  serviceName,
992
1014
  ...serviceVersion && { serviceVersion },
993
1015
  instrumentations,
994
- // Allow advanced overrides
995
1016
  ...options.sdk
996
1017
  };
997
- const sdk = new sdkNode.NodeSDK(sdkConfig);
998
- sdk.start();
1018
+ const sdk = yield* effect.Effect.sync(() => {
1019
+ const s = new sdkNode.NodeSDK(sdkConfig);
1020
+ s.start();
1021
+ return s;
1022
+ });
999
1023
  sdkInstance = sdk;
1000
1024
  if (!options.disableAutoShutdown) {
1001
- registerShutdownHandlers(sdk);
1025
+ yield* effect.Effect.sync(() => registerShutdownHandlers(sdk));
1002
1026
  }
1003
1027
  logInitialization(config, serviceName, serviceVersion, options, enableAutoInstrumentation);
1004
1028
  return sdk;
1005
- }
1029
+ });
1006
1030
  function getSdkInstance() {
1007
1031
  return sdkInstance;
1008
1032
  }
@@ -1015,7 +1039,7 @@ async function shutdownSdk() {
1015
1039
  }
1016
1040
  function resetSdk() {
1017
1041
  sdkInstance = null;
1018
- initializationPromise = null;
1042
+ initializationDeferred = null;
1019
1043
  }
1020
1044
  function registerShutdownHandlers(sdk) {
1021
1045
  const shutdown = async (signal) => {
@@ -1072,30 +1096,8 @@ function logInitialization(config, serviceName, serviceVersion, options, autoIns
1072
1096
  }
1073
1097
 
1074
1098
  // src/api.ts
1075
- async function initializeInstrumentation(options = {}) {
1076
- const sdk = await initializeSdk(options);
1077
- if (sdk) {
1078
- const config = await loadConfigWithOptions(options);
1079
- initializePatternMatcher(config);
1080
- }
1081
- return sdk;
1082
- }
1083
- async function initializePatternMatchingOnly(options = {}) {
1084
- const config = await loadConfigWithOptions(options);
1085
- initializePatternMatcher(config);
1086
- logger.log("@atrim/instrumentation: Pattern matching initialized (legacy mode)");
1087
- logger.log(
1088
- " Note: NodeSDK is not initialized. Use initializeInstrumentation() for complete setup."
1089
- );
1090
- }
1091
- var initializeInstrumentationEffect = (options = {}) => effect.Effect.gen(function* () {
1092
- const sdk = yield* effect.Effect.tryPromise({
1093
- try: () => initializeSdk(options),
1094
- catch: (error) => new InitializationError2({
1095
- reason: "SDK initialization failed",
1096
- cause: error
1097
- })
1098
- });
1099
+ var initializeInstrumentation = (options = {}) => effect.Effect.gen(function* () {
1100
+ const sdk = yield* initializeSdkEffect(options);
1099
1101
  if (sdk) {
1100
1102
  yield* effect.Effect.tryPromise({
1101
1103
  try: () => loadConfigWithOptions(options),
@@ -1113,7 +1115,7 @@ var initializeInstrumentationEffect = (options = {}) => effect.Effect.gen(functi
1113
1115
  }
1114
1116
  return sdk;
1115
1117
  });
1116
- var initializePatternMatchingOnlyEffect = (options = {}) => effect.Effect.gen(function* () {
1118
+ var initializePatternMatchingOnly = (options = {}) => effect.Effect.gen(function* () {
1117
1119
  const config = yield* effect.Effect.tryPromise({
1118
1120
  try: () => loadConfigWithOptions(options),
1119
1121
  catch: (error) => new ConfigError2({
@@ -1123,7 +1125,7 @@ var initializePatternMatchingOnlyEffect = (options = {}) => effect.Effect.gen(fu
1123
1125
  });
1124
1126
  yield* effect.Effect.sync(() => {
1125
1127
  initializePatternMatcher(config);
1126
- logger.log("@atrim/instrumentation: Pattern matching initialized (legacy mode)");
1128
+ logger.log("@atrim/instrumentation: Pattern matching initialized (pattern-only mode)");
1127
1129
  logger.log(
1128
1130
  " Note: NodeSDK is not initialized. Use initializeInstrumentation() for complete setup."
1129
1131
  );
@@ -1239,20 +1241,15 @@ exports.annotateDbQuery = annotateDbQuery;
1239
1241
  exports.annotateHttpRequest = annotateHttpRequest;
1240
1242
  exports.clearConfigCache = _resetConfigLoaderCache;
1241
1243
  exports.createOtlpExporter = createOtlpExporter;
1242
- exports.detectServiceInfo = detectServiceInfoAsync;
1243
- exports.detectServiceInfoEffect = detectServiceInfo;
1244
+ exports.detectServiceInfo = detectServiceInfo;
1244
1245
  exports.getOtlpEndpoint = getOtlpEndpoint;
1245
1246
  exports.getPatternMatcher = getPatternMatcher;
1246
1247
  exports.getSdkInstance = getSdkInstance;
1247
1248
  exports.getServiceInfoWithFallback = getServiceInfoWithFallback;
1248
- exports.getServiceName = getServiceNameAsync;
1249
- exports.getServiceNameEffect = getServiceName;
1250
- exports.getServiceVersion = getServiceVersionAsync;
1251
- exports.getServiceVersionEffect = getServiceVersion;
1249
+ exports.getServiceName = getServiceName;
1250
+ exports.getServiceVersion = getServiceVersion;
1252
1251
  exports.initializeInstrumentation = initializeInstrumentation;
1253
- exports.initializeInstrumentationEffect = initializeInstrumentationEffect;
1254
1252
  exports.initializePatternMatchingOnly = initializePatternMatchingOnly;
1255
- exports.initializePatternMatchingOnlyEffect = initializePatternMatchingOnlyEffect;
1256
1253
  exports.loadConfig = loadConfig;
1257
1254
  exports.loadConfigFromInline = loadConfigFromInline;
1258
1255
  exports.loadConfigWithOptions = loadConfigWithOptions;