@atrim/instrument-node 0.5.2-dev.ac2fbfe.20251221205322 → 0.7.0-b9eaf74-20260108193056
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/README.md +92 -330
- package/package.json +25 -19
- package/target/dist/index.cjs +201 -60
- package/target/dist/index.cjs.map +1 -1
- package/target/dist/index.d.cts +28 -13
- package/target/dist/index.d.ts +28 -13
- package/target/dist/index.js +200 -60
- package/target/dist/index.js.map +1 -1
- package/target/dist/integrations/effect/auto/index.cjs +915 -0
- package/target/dist/integrations/effect/auto/index.cjs.map +1 -0
- package/target/dist/integrations/effect/auto/index.d.cts +278 -0
- package/target/dist/integrations/effect/auto/index.d.ts +278 -0
- package/target/dist/integrations/effect/auto/index.js +875 -0
- package/target/dist/integrations/effect/auto/index.js.map +1 -0
- package/target/dist/integrations/effect/index.cjs +205 -129
- package/target/dist/integrations/effect/index.cjs.map +1 -1
- package/target/dist/integrations/effect/index.d.cts +19 -24
- package/target/dist/integrations/effect/index.d.ts +19 -24
- package/target/dist/integrations/effect/index.js +206 -132
- package/target/dist/integrations/effect/index.js.map +1 -1
package/target/dist/index.cjs
CHANGED
|
@@ -13,9 +13,9 @@ var zod = require('zod');
|
|
|
13
13
|
var exporterTraceOtlpHttp = require('@opentelemetry/exporter-trace-otlp-http');
|
|
14
14
|
var promises = require('fs/promises');
|
|
15
15
|
var path = require('path');
|
|
16
|
-
var
|
|
17
|
-
var platform = require('@effect/platform');
|
|
16
|
+
var module$1 = require('module');
|
|
18
17
|
|
|
18
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
19
19
|
function _interopNamespace(e) {
|
|
20
20
|
if (e && e.__esModule) return e;
|
|
21
21
|
var n = Object.create(null);
|
|
@@ -76,6 +76,69 @@ var AutoIsolationConfigSchema = zod.z.object({
|
|
|
76
76
|
add_metadata: zod.z.boolean().default(true)
|
|
77
77
|
}).default({})
|
|
78
78
|
});
|
|
79
|
+
var SpanNamingRuleSchema = zod.z.object({
|
|
80
|
+
// Match criteria (all specified criteria must match)
|
|
81
|
+
match: zod.z.object({
|
|
82
|
+
// Regex pattern to match file path
|
|
83
|
+
file: zod.z.string().optional(),
|
|
84
|
+
// Regex pattern to match function name
|
|
85
|
+
function: zod.z.string().optional(),
|
|
86
|
+
// Regex pattern to match module name
|
|
87
|
+
module: zod.z.string().optional()
|
|
88
|
+
}),
|
|
89
|
+
// Span name template with variables:
|
|
90
|
+
// {fiber_id} - Fiber ID
|
|
91
|
+
// {function} - Function name
|
|
92
|
+
// {module} - Module name
|
|
93
|
+
// {file} - File path
|
|
94
|
+
// {line} - Line number
|
|
95
|
+
// {operator} - Effect operator (gen, all, forEach, etc.)
|
|
96
|
+
// {match:field:N} - Captured regex group from match
|
|
97
|
+
name: zod.z.string()
|
|
98
|
+
});
|
|
99
|
+
var AutoInstrumentationConfigSchema = zod.z.object({
|
|
100
|
+
// Enable/disable auto-instrumentation
|
|
101
|
+
enabled: zod.z.boolean().default(false),
|
|
102
|
+
// Tracing granularity
|
|
103
|
+
// - 'fiber': Trace at fiber creation (recommended, lower overhead)
|
|
104
|
+
// - 'operator': Trace each Effect operator (higher granularity, more overhead)
|
|
105
|
+
granularity: zod.z.enum(["fiber", "operator"]).default("fiber"),
|
|
106
|
+
// Smart span naming configuration
|
|
107
|
+
span_naming: zod.z.object({
|
|
108
|
+
// Default span name template when no rules match
|
|
109
|
+
default: zod.z.string().default("effect.fiber.{fiber_id}"),
|
|
110
|
+
// Infer span names from source code (requires stack trace parsing)
|
|
111
|
+
// Adds ~50-100μs overhead per fiber
|
|
112
|
+
infer_from_source: zod.z.boolean().default(true),
|
|
113
|
+
// Naming rules (first match wins)
|
|
114
|
+
rules: zod.z.array(SpanNamingRuleSchema).default([])
|
|
115
|
+
}).default({}),
|
|
116
|
+
// Pattern-based filtering
|
|
117
|
+
filter: zod.z.object({
|
|
118
|
+
// Only trace spans matching these patterns (empty = trace all)
|
|
119
|
+
include: zod.z.array(zod.z.string()).default([]),
|
|
120
|
+
// Never trace spans matching these patterns
|
|
121
|
+
exclude: zod.z.array(zod.z.string()).default([])
|
|
122
|
+
}).default({}),
|
|
123
|
+
// Performance controls
|
|
124
|
+
performance: zod.z.object({
|
|
125
|
+
// Sample rate (0.0 - 1.0)
|
|
126
|
+
sampling_rate: zod.z.number().min(0).max(1).default(1),
|
|
127
|
+
// Skip fibers shorter than this duration (e.g., "10ms", "100 millis")
|
|
128
|
+
min_duration: zod.z.string().default("0ms"),
|
|
129
|
+
// Maximum concurrent traced fibers (0 = unlimited)
|
|
130
|
+
max_concurrent: zod.z.number().default(0)
|
|
131
|
+
}).default({}),
|
|
132
|
+
// Automatic metadata extraction
|
|
133
|
+
metadata: zod.z.object({
|
|
134
|
+
// Extract Effect fiber information
|
|
135
|
+
fiber_info: zod.z.boolean().default(true),
|
|
136
|
+
// Extract source location (file:line)
|
|
137
|
+
source_location: zod.z.boolean().default(true),
|
|
138
|
+
// Extract parent fiber information
|
|
139
|
+
parent_fiber: zod.z.boolean().default(true)
|
|
140
|
+
}).default({})
|
|
141
|
+
});
|
|
79
142
|
var HttpFilteringConfigSchema = zod.z.object({
|
|
80
143
|
// Patterns to ignore for outgoing HTTP requests (string patterns only in YAML)
|
|
81
144
|
ignore_outgoing_urls: zod.z.array(zod.z.string()).optional(),
|
|
@@ -107,11 +170,52 @@ var InstrumentationConfigSchema = zod.z.object({
|
|
|
107
170
|
ignore_patterns: zod.z.array(PatternConfigSchema)
|
|
108
171
|
}),
|
|
109
172
|
effect: zod.z.object({
|
|
173
|
+
// Enable/disable Effect tracing entirely
|
|
174
|
+
// When false, EffectInstrumentationLive returns Layer.empty
|
|
175
|
+
enabled: zod.z.boolean().default(true),
|
|
176
|
+
// Exporter mode:
|
|
177
|
+
// - "unified": Use global TracerProvider from Node SDK (recommended, enables filtering)
|
|
178
|
+
// - "standalone": Use Effect's own OTLP exporter (bypasses Node SDK filtering)
|
|
179
|
+
exporter: zod.z.enum(["unified", "standalone"]).default("unified"),
|
|
110
180
|
auto_extract_metadata: zod.z.boolean(),
|
|
111
|
-
auto_isolation: AutoIsolationConfigSchema.optional()
|
|
181
|
+
auto_isolation: AutoIsolationConfigSchema.optional(),
|
|
182
|
+
// Auto-instrumentation: automatic tracing of all Effect fibers
|
|
183
|
+
auto_instrumentation: AutoInstrumentationConfigSchema.optional()
|
|
112
184
|
}).optional(),
|
|
113
185
|
http: HttpFilteringConfigSchema.optional()
|
|
114
186
|
});
|
|
187
|
+
var defaultConfig = {
|
|
188
|
+
version: "1.0",
|
|
189
|
+
instrumentation: {
|
|
190
|
+
enabled: true,
|
|
191
|
+
logging: "on",
|
|
192
|
+
description: "Default instrumentation configuration",
|
|
193
|
+
instrument_patterns: [
|
|
194
|
+
{ pattern: "^app\\.", enabled: true, description: "Application operations" },
|
|
195
|
+
{ pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
|
|
196
|
+
{ pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
|
|
197
|
+
],
|
|
198
|
+
ignore_patterns: [
|
|
199
|
+
{ pattern: "^test\\.", description: "Test utilities" },
|
|
200
|
+
{ pattern: "^internal\\.", description: "Internal operations" },
|
|
201
|
+
{ pattern: "^health\\.", description: "Health checks" }
|
|
202
|
+
]
|
|
203
|
+
},
|
|
204
|
+
effect: {
|
|
205
|
+
enabled: true,
|
|
206
|
+
exporter: "unified",
|
|
207
|
+
auto_extract_metadata: true
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
function parseAndValidateConfig(content) {
|
|
211
|
+
let parsed;
|
|
212
|
+
if (typeof content === "string") {
|
|
213
|
+
parsed = yaml.parse(content);
|
|
214
|
+
} else {
|
|
215
|
+
parsed = content;
|
|
216
|
+
}
|
|
217
|
+
return InstrumentationConfigSchema.parse(parsed);
|
|
218
|
+
}
|
|
115
219
|
(class extends effect.Data.TaggedError("ConfigError") {
|
|
116
220
|
get message() {
|
|
117
221
|
return this.reason;
|
|
@@ -289,7 +393,7 @@ var makeConfigLoader = effect.Effect.gen(function* () {
|
|
|
289
393
|
})
|
|
290
394
|
});
|
|
291
395
|
});
|
|
292
|
-
|
|
396
|
+
effect.Layer.effect(ConfigLoader, makeConfigLoader);
|
|
293
397
|
var PatternMatcher = class {
|
|
294
398
|
constructor(config) {
|
|
295
399
|
__publicField2(this, "ignorePatterns", []);
|
|
@@ -453,8 +557,14 @@ var PatternSpanProcessor = class {
|
|
|
453
557
|
constructor(config, wrappedProcessor) {
|
|
454
558
|
__publicField(this, "matcher");
|
|
455
559
|
__publicField(this, "wrappedProcessor");
|
|
560
|
+
__publicField(this, "httpIgnorePatterns", []);
|
|
456
561
|
this.matcher = new PatternMatcher(config);
|
|
457
562
|
this.wrappedProcessor = wrappedProcessor;
|
|
563
|
+
if (config.http?.ignore_incoming_paths) {
|
|
564
|
+
this.httpIgnorePatterns = config.http.ignore_incoming_paths.map(
|
|
565
|
+
(pattern) => new RegExp(pattern)
|
|
566
|
+
);
|
|
567
|
+
}
|
|
458
568
|
}
|
|
459
569
|
/**
|
|
460
570
|
* Called when a span is started
|
|
@@ -472,12 +582,40 @@ var PatternSpanProcessor = class {
|
|
|
472
582
|
* Called when a span is ended
|
|
473
583
|
*
|
|
474
584
|
* This is where we make the final decision on whether to export the span.
|
|
585
|
+
* We check both span name patterns and HTTP path patterns.
|
|
475
586
|
*/
|
|
476
587
|
onEnd(span) {
|
|
477
588
|
const spanName = span.name;
|
|
478
|
-
if (this.matcher.shouldInstrument(spanName)) {
|
|
479
|
-
|
|
589
|
+
if (!this.matcher.shouldInstrument(spanName)) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
if (this.shouldIgnoreHttpSpan(span)) {
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
this.wrappedProcessor.onEnd(span);
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Check if span should be ignored based on HTTP path attributes
|
|
599
|
+
*
|
|
600
|
+
* This checks the span's url.path, http.route, or http.target attributes
|
|
601
|
+
* against the configured http.ignore_incoming_paths patterns.
|
|
602
|
+
*
|
|
603
|
+
* This enables filtering of Effect HTTP spans (and any other HTTP spans)
|
|
604
|
+
* based on path patterns, which is essential for filtering out OTLP
|
|
605
|
+
* endpoint requests like /v1/traces, /v1/logs, /v1/metrics.
|
|
606
|
+
*/
|
|
607
|
+
shouldIgnoreHttpSpan(span) {
|
|
608
|
+
if (this.httpIgnorePatterns.length === 0) {
|
|
609
|
+
return false;
|
|
480
610
|
}
|
|
611
|
+
const urlPath = span.attributes["url.path"];
|
|
612
|
+
const httpRoute = span.attributes["http.route"];
|
|
613
|
+
const httpTarget = span.attributes["http.target"];
|
|
614
|
+
const pathToCheck = urlPath || httpRoute || httpTarget;
|
|
615
|
+
if (!pathToCheck) {
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
return this.httpIgnorePatterns.some((pattern) => pattern.test(pathToCheck));
|
|
481
619
|
}
|
|
482
620
|
/**
|
|
483
621
|
* Shutdown the processor
|
|
@@ -715,83 +853,55 @@ async function getServiceNameAsync() {
|
|
|
715
853
|
async function getServiceVersionAsync() {
|
|
716
854
|
return effect.Effect.runPromise(getServiceVersion);
|
|
717
855
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
);
|
|
721
|
-
|
|
722
|
-
function getCachedLoader() {
|
|
723
|
-
if (!cachedLoaderPromise) {
|
|
724
|
-
cachedLoaderPromise = effect.Effect.runPromise(
|
|
725
|
-
effect.Effect.gen(function* () {
|
|
726
|
-
return yield* ConfigLoader;
|
|
727
|
-
}).pipe(effect.Effect.provide(NodeConfigLoaderLive))
|
|
728
|
-
);
|
|
729
|
-
}
|
|
730
|
-
return cachedLoaderPromise;
|
|
856
|
+
async function loadFromFile(filePath) {
|
|
857
|
+
const { readFile: readFile2 } = await import('fs/promises');
|
|
858
|
+
const content = await readFile2(filePath, "utf-8");
|
|
859
|
+
return parseAndValidateConfig(content);
|
|
731
860
|
}
|
|
732
|
-
function
|
|
733
|
-
|
|
861
|
+
async function loadFromUrl(url) {
|
|
862
|
+
const response = await fetch(url);
|
|
863
|
+
if (!response.ok) {
|
|
864
|
+
throw new Error(`Failed to fetch config from ${url}: ${response.statusText}`);
|
|
865
|
+
}
|
|
866
|
+
const content = await response.text();
|
|
867
|
+
return parseAndValidateConfig(content);
|
|
734
868
|
}
|
|
735
|
-
async function loadConfig(uri,
|
|
736
|
-
if (
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
return
|
|
869
|
+
async function loadConfig(uri, _options) {
|
|
870
|
+
if (uri.startsWith("http://") || uri.startsWith("https://")) {
|
|
871
|
+
return loadFromUrl(uri);
|
|
872
|
+
}
|
|
873
|
+
if (uri.startsWith("file://")) {
|
|
874
|
+
const filePath = uri.slice(7);
|
|
875
|
+
return loadFromFile(filePath);
|
|
742
876
|
}
|
|
743
|
-
|
|
744
|
-
return effect.Effect.runPromise(loader.loadFromUri(uri));
|
|
877
|
+
return loadFromFile(uri);
|
|
745
878
|
}
|
|
746
879
|
async function loadConfigFromInline(content) {
|
|
747
|
-
|
|
748
|
-
return effect.Effect.runPromise(loader.loadFromInline(content));
|
|
880
|
+
return parseAndValidateConfig(content);
|
|
749
881
|
}
|
|
750
|
-
function
|
|
751
|
-
return {
|
|
752
|
-
version: "1.0",
|
|
753
|
-
instrumentation: {
|
|
754
|
-
enabled: true,
|
|
755
|
-
logging: "on",
|
|
756
|
-
description: "Default instrumentation configuration",
|
|
757
|
-
instrument_patterns: [
|
|
758
|
-
{ pattern: "^app\\.", enabled: true, description: "Application operations" },
|
|
759
|
-
{ pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
|
|
760
|
-
{ pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
|
|
761
|
-
],
|
|
762
|
-
ignore_patterns: [
|
|
763
|
-
{ pattern: "^test\\.", description: "Test utilities" },
|
|
764
|
-
{ pattern: "^internal\\.", description: "Internal operations" },
|
|
765
|
-
{ pattern: "^health\\.", description: "Health checks" }
|
|
766
|
-
]
|
|
767
|
-
},
|
|
768
|
-
effect: {
|
|
769
|
-
auto_extract_metadata: true
|
|
770
|
-
}
|
|
771
|
-
};
|
|
882
|
+
function _resetConfigLoaderCache() {
|
|
772
883
|
}
|
|
773
884
|
async function loadConfigWithOptions(options = {}) {
|
|
774
|
-
const loadOptions = options.cacheTimeout !== void 0 ? { cacheTimeout: options.cacheTimeout } : void 0;
|
|
775
885
|
if (options.config) {
|
|
776
886
|
return loadConfigFromInline(options.config);
|
|
777
887
|
}
|
|
778
888
|
const envConfigPath = process.env.ATRIM_INSTRUMENTATION_CONFIG;
|
|
779
889
|
if (envConfigPath) {
|
|
780
|
-
return loadConfig(envConfigPath
|
|
890
|
+
return loadConfig(envConfigPath);
|
|
781
891
|
}
|
|
782
892
|
if (options.configUrl) {
|
|
783
|
-
return loadConfig(options.configUrl
|
|
893
|
+
return loadConfig(options.configUrl);
|
|
784
894
|
}
|
|
785
895
|
if (options.configPath) {
|
|
786
|
-
return loadConfig(options.configPath
|
|
896
|
+
return loadConfig(options.configPath);
|
|
787
897
|
}
|
|
788
898
|
const { existsSync } = await import('fs');
|
|
789
899
|
const { join: join2 } = await import('path');
|
|
790
900
|
const defaultPath = join2(process.cwd(), "instrumentation.yaml");
|
|
791
901
|
if (existsSync(defaultPath)) {
|
|
792
|
-
return loadConfig(defaultPath
|
|
902
|
+
return loadConfig(defaultPath);
|
|
793
903
|
}
|
|
794
|
-
return
|
|
904
|
+
return defaultConfig;
|
|
795
905
|
}
|
|
796
906
|
|
|
797
907
|
// src/core/sdk-initializer.ts
|
|
@@ -1083,9 +1193,39 @@ function logInitialization(config, serviceName, serviceVersion, options, autoIns
|
|
|
1083
1193
|
logger.log(` - OTLP endpoint: ${endpoint}`);
|
|
1084
1194
|
logger.log("");
|
|
1085
1195
|
}
|
|
1196
|
+
var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
1197
|
+
function validateOpenTelemetryApi() {
|
|
1198
|
+
try {
|
|
1199
|
+
require2.resolve("@opentelemetry/api");
|
|
1200
|
+
} catch {
|
|
1201
|
+
throw new Error(
|
|
1202
|
+
"@atrim/instrument-node requires @opentelemetry/api as a peer dependency.\n\nInstall it with:\n npm install @opentelemetry/api\n\nOr with your preferred package manager:\n pnpm add @opentelemetry/api\n yarn add @opentelemetry/api\n bun add @opentelemetry/api"
|
|
1203
|
+
);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
function validateEffectDependencies() {
|
|
1207
|
+
const packages = ["effect", "@effect/opentelemetry", "@effect/platform"];
|
|
1208
|
+
for (const pkg of packages) {
|
|
1209
|
+
try {
|
|
1210
|
+
require2.resolve(pkg);
|
|
1211
|
+
} catch {
|
|
1212
|
+
return false;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
return true;
|
|
1216
|
+
}
|
|
1217
|
+
var validateDependencies = effect.Effect.try({
|
|
1218
|
+
try: () => validateOpenTelemetryApi(),
|
|
1219
|
+
catch: (error) => new InitializationError2({
|
|
1220
|
+
reason: error instanceof Error ? error.message : "Dependency validation failed",
|
|
1221
|
+
cause: error
|
|
1222
|
+
})
|
|
1223
|
+
});
|
|
1224
|
+
effect.Effect.sync(() => validateEffectDependencies());
|
|
1086
1225
|
|
|
1087
1226
|
// src/api.ts
|
|
1088
1227
|
async function initializeInstrumentation(options = {}) {
|
|
1228
|
+
validateOpenTelemetryApi();
|
|
1089
1229
|
const sdk = await initializeSdk(options);
|
|
1090
1230
|
if (sdk) {
|
|
1091
1231
|
const config = await loadConfigWithOptions(options);
|
|
@@ -1102,6 +1242,7 @@ async function initializePatternMatchingOnly(options = {}) {
|
|
|
1102
1242
|
);
|
|
1103
1243
|
}
|
|
1104
1244
|
var initializeInstrumentationEffect = (options = {}) => effect.Effect.gen(function* () {
|
|
1245
|
+
yield* validateDependencies;
|
|
1105
1246
|
const sdk = yield* effect.Effect.tryPromise({
|
|
1106
1247
|
try: () => initializeSdk(options),
|
|
1107
1248
|
catch: (error) => new InitializationError2({
|