@atrim/instrument-node 0.1.3-fea6398-20251118005809 → 0.4.0-08fae61-20251118193009
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 +328 -7
- package/package.json +6 -5
- package/target/dist/index.cjs +177 -531
- package/target/dist/index.cjs.map +1 -1
- package/target/dist/index.d.cts +57 -3
- package/target/dist/index.d.ts +57 -3
- package/target/dist/index.js +104 -469
- package/target/dist/index.js.map +1 -1
- package/target/dist/integrations/effect/index.cjs +54 -409
- package/target/dist/integrations/effect/index.cjs.map +1 -1
- package/target/dist/integrations/effect/index.d.cts +17 -1
- package/target/dist/integrations/effect/index.d.ts +17 -1
- package/target/dist/integrations/effect/index.js +43 -398
- package/target/dist/integrations/effect/index.js.map +1 -1
package/target/dist/index.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { Data, Effect,
|
|
1
|
+
import { Data, Effect, Layer } from 'effect';
|
|
2
2
|
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
3
3
|
import { SimpleSpanProcessor, BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
|
|
4
4
|
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
|
|
5
5
|
import { trace } from '@opentelemetry/api';
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
import { parse } from 'yaml';
|
|
9
|
-
import { z } from 'zod';
|
|
6
|
+
import { ConfigLoaderLive, PatternMatcher, ConfigLoader, initializePatternMatcher, logger } from '@atrim/instrument-core';
|
|
7
|
+
export { PatternMatcher, getPatternMatcher, shouldInstrumentSpan } from '@atrim/instrument-core';
|
|
10
8
|
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
|
11
9
|
import { readFile } from 'fs/promises';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { NodeContext } from '@effect/platform-node';
|
|
12
|
+
import { FetchHttpClient } from '@effect/platform';
|
|
12
13
|
|
|
13
14
|
var __defProp = Object.defineProperty;
|
|
14
15
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -19,446 +20,6 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
19
20
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
20
21
|
});
|
|
21
22
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
22
|
-
var __defProp2 = Object.defineProperty;
|
|
23
|
-
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
24
|
-
var __publicField2 = (obj, key, value) => __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
25
|
-
var PatternConfigSchema = z.object({
|
|
26
|
-
pattern: z.string(),
|
|
27
|
-
enabled: z.boolean().optional(),
|
|
28
|
-
description: z.string().optional()
|
|
29
|
-
});
|
|
30
|
-
var AutoIsolationConfigSchema = z.object({
|
|
31
|
-
// Global enable/disable for auto-isolation
|
|
32
|
-
enabled: z.boolean().default(false),
|
|
33
|
-
// Which operators to auto-isolate
|
|
34
|
-
operators: z.object({
|
|
35
|
-
fiberset_run: z.boolean().default(true),
|
|
36
|
-
effect_fork: z.boolean().default(true),
|
|
37
|
-
effect_fork_daemon: z.boolean().default(true),
|
|
38
|
-
effect_fork_in: z.boolean().default(false)
|
|
39
|
-
}).default({}),
|
|
40
|
-
// Virtual parent tracking configuration
|
|
41
|
-
tracking: z.object({
|
|
42
|
-
use_span_links: z.boolean().default(true),
|
|
43
|
-
use_attributes: z.boolean().default(true),
|
|
44
|
-
capture_logical_parent: z.boolean().default(true)
|
|
45
|
-
}).default({}),
|
|
46
|
-
// Span categorization
|
|
47
|
-
attributes: z.object({
|
|
48
|
-
category: z.string().default("background_task"),
|
|
49
|
-
add_metadata: z.boolean().default(true)
|
|
50
|
-
}).default({})
|
|
51
|
-
});
|
|
52
|
-
var HttpFilteringConfigSchema = z.object({
|
|
53
|
-
// Patterns to ignore for outgoing HTTP requests (string patterns only in YAML)
|
|
54
|
-
ignore_outgoing_urls: z.array(z.string()).optional(),
|
|
55
|
-
// Patterns to ignore for incoming HTTP requests (string patterns only in YAML)
|
|
56
|
-
ignore_incoming_paths: z.array(z.string()).optional(),
|
|
57
|
-
// Require parent span for outgoing requests (prevents root spans for HTTP calls)
|
|
58
|
-
require_parent_for_outgoing_spans: z.boolean().optional()
|
|
59
|
-
});
|
|
60
|
-
var InstrumentationConfigSchema = z.object({
|
|
61
|
-
version: z.string(),
|
|
62
|
-
instrumentation: z.object({
|
|
63
|
-
enabled: z.boolean(),
|
|
64
|
-
description: z.string().optional(),
|
|
65
|
-
logging: z.enum(["on", "off", "minimal"]).optional().default("on"),
|
|
66
|
-
instrument_patterns: z.array(PatternConfigSchema),
|
|
67
|
-
ignore_patterns: z.array(PatternConfigSchema)
|
|
68
|
-
}),
|
|
69
|
-
effect: z.object({
|
|
70
|
-
auto_extract_metadata: z.boolean(),
|
|
71
|
-
auto_isolation: AutoIsolationConfigSchema.optional()
|
|
72
|
-
}).optional(),
|
|
73
|
-
http: HttpFilteringConfigSchema.optional()
|
|
74
|
-
});
|
|
75
|
-
(class extends Data.TaggedError("ConfigError") {
|
|
76
|
-
});
|
|
77
|
-
var ConfigUrlError = class extends Data.TaggedError("ConfigUrlError") {
|
|
78
|
-
};
|
|
79
|
-
var ConfigValidationError = class extends Data.TaggedError("ConfigValidationError") {
|
|
80
|
-
};
|
|
81
|
-
var ConfigFileError = class extends Data.TaggedError("ConfigFileError") {
|
|
82
|
-
};
|
|
83
|
-
(class extends Data.TaggedError("ServiceDetectionError") {
|
|
84
|
-
});
|
|
85
|
-
(class extends Data.TaggedError("InitializationError") {
|
|
86
|
-
});
|
|
87
|
-
(class extends Data.TaggedError("ExportError") {
|
|
88
|
-
});
|
|
89
|
-
(class extends Data.TaggedError("ShutdownError") {
|
|
90
|
-
});
|
|
91
|
-
var SECURITY_DEFAULTS = {
|
|
92
|
-
maxConfigSize: 1e6,
|
|
93
|
-
// 1MB
|
|
94
|
-
requestTimeout: 5e3,
|
|
95
|
-
allowedProtocols: ["https:"],
|
|
96
|
-
// Only HTTPS for remote configs
|
|
97
|
-
cacheTimeout: 3e5
|
|
98
|
-
// 5 minutes
|
|
99
|
-
};
|
|
100
|
-
function getDefaultConfig() {
|
|
101
|
-
return {
|
|
102
|
-
version: "1.0",
|
|
103
|
-
instrumentation: {
|
|
104
|
-
enabled: true,
|
|
105
|
-
logging: "on",
|
|
106
|
-
description: "Default instrumentation configuration",
|
|
107
|
-
instrument_patterns: [
|
|
108
|
-
{ pattern: "^app\\.", enabled: true, description: "Application operations" },
|
|
109
|
-
{ pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
|
|
110
|
-
{ pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
|
|
111
|
-
],
|
|
112
|
-
ignore_patterns: [
|
|
113
|
-
{ pattern: "^test\\.", description: "Test utilities" },
|
|
114
|
-
{ pattern: "^internal\\.", description: "Internal operations" },
|
|
115
|
-
{ pattern: "^health\\.", description: "Health checks" }
|
|
116
|
-
]
|
|
117
|
-
},
|
|
118
|
-
effect: {
|
|
119
|
-
auto_extract_metadata: true
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
var validateConfigEffect = (rawConfig) => Effect.try({
|
|
124
|
-
try: () => InstrumentationConfigSchema.parse(rawConfig),
|
|
125
|
-
catch: (error) => new ConfigValidationError({
|
|
126
|
-
reason: "Invalid configuration schema",
|
|
127
|
-
cause: error
|
|
128
|
-
})
|
|
129
|
-
});
|
|
130
|
-
var loadConfigFromFileEffect = (filePath) => Effect.gen(function* () {
|
|
131
|
-
const fileContents = yield* Effect.try({
|
|
132
|
-
try: () => readFileSync(filePath, "utf8"),
|
|
133
|
-
catch: (error) => new ConfigFileError({
|
|
134
|
-
reason: `Failed to read config file at ${filePath}`,
|
|
135
|
-
cause: error
|
|
136
|
-
})
|
|
137
|
-
});
|
|
138
|
-
if (fileContents.length > SECURITY_DEFAULTS.maxConfigSize) {
|
|
139
|
-
return yield* Effect.fail(
|
|
140
|
-
new ConfigFileError({
|
|
141
|
-
reason: `Config file exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
|
|
142
|
-
})
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
let rawConfig;
|
|
146
|
-
try {
|
|
147
|
-
rawConfig = parse(fileContents);
|
|
148
|
-
} catch (error) {
|
|
149
|
-
return yield* Effect.fail(
|
|
150
|
-
new ConfigValidationError({
|
|
151
|
-
reason: "Invalid YAML syntax",
|
|
152
|
-
cause: error
|
|
153
|
-
})
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
return yield* validateConfigEffect(rawConfig);
|
|
157
|
-
});
|
|
158
|
-
var fetchAndParseConfig = (url) => Effect.gen(function* () {
|
|
159
|
-
let urlObj;
|
|
160
|
-
try {
|
|
161
|
-
urlObj = new URL(url);
|
|
162
|
-
} catch (error) {
|
|
163
|
-
return yield* Effect.fail(
|
|
164
|
-
new ConfigUrlError({
|
|
165
|
-
reason: `Invalid URL: ${url}`,
|
|
166
|
-
cause: error
|
|
167
|
-
})
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
if (!SECURITY_DEFAULTS.allowedProtocols.includes(urlObj.protocol)) {
|
|
171
|
-
return yield* Effect.fail(
|
|
172
|
-
new ConfigUrlError({
|
|
173
|
-
reason: `Insecure protocol ${urlObj.protocol}. Only ${SECURITY_DEFAULTS.allowedProtocols.join(", ")} are allowed`
|
|
174
|
-
})
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
const response = yield* Effect.tryPromise({
|
|
178
|
-
try: () => fetch(url, {
|
|
179
|
-
redirect: "follow",
|
|
180
|
-
headers: {
|
|
181
|
-
Accept: "application/yaml, text/yaml, text/x-yaml"
|
|
182
|
-
}
|
|
183
|
-
}),
|
|
184
|
-
catch: (error) => new ConfigUrlError({
|
|
185
|
-
reason: `Failed to load config from URL ${url}`,
|
|
186
|
-
cause: error
|
|
187
|
-
})
|
|
188
|
-
}).pipe(
|
|
189
|
-
Effect.timeout(Duration.millis(SECURITY_DEFAULTS.requestTimeout)),
|
|
190
|
-
Effect.retry({
|
|
191
|
-
times: 3,
|
|
192
|
-
schedule: Schedule.exponential(Duration.millis(100))
|
|
193
|
-
}),
|
|
194
|
-
Effect.catchAll((error) => {
|
|
195
|
-
if (error._tag === "TimeoutException") {
|
|
196
|
-
return Effect.fail(
|
|
197
|
-
new ConfigUrlError({
|
|
198
|
-
reason: `Config fetch timeout after ${SECURITY_DEFAULTS.requestTimeout}ms`
|
|
199
|
-
})
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
return Effect.fail(error);
|
|
203
|
-
})
|
|
204
|
-
);
|
|
205
|
-
if (!response.ok) {
|
|
206
|
-
return yield* Effect.fail(
|
|
207
|
-
new ConfigUrlError({
|
|
208
|
-
reason: `HTTP ${response.status}: ${response.statusText}`
|
|
209
|
-
})
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
const contentLength = response.headers.get("content-length");
|
|
213
|
-
if (contentLength && parseInt(contentLength) > SECURITY_DEFAULTS.maxConfigSize) {
|
|
214
|
-
return yield* Effect.fail(
|
|
215
|
-
new ConfigUrlError({
|
|
216
|
-
reason: `Config exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
|
|
217
|
-
})
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
const text = yield* Effect.tryPromise({
|
|
221
|
-
try: () => response.text(),
|
|
222
|
-
catch: (error) => new ConfigUrlError({
|
|
223
|
-
reason: "Failed to read response body",
|
|
224
|
-
cause: error
|
|
225
|
-
})
|
|
226
|
-
});
|
|
227
|
-
if (text.length > SECURITY_DEFAULTS.maxConfigSize) {
|
|
228
|
-
return yield* Effect.fail(
|
|
229
|
-
new ConfigUrlError({
|
|
230
|
-
reason: `Config exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
|
|
231
|
-
})
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
let rawConfig;
|
|
235
|
-
try {
|
|
236
|
-
rawConfig = parse(text);
|
|
237
|
-
} catch (error) {
|
|
238
|
-
return yield* Effect.fail(
|
|
239
|
-
new ConfigValidationError({
|
|
240
|
-
reason: "Invalid YAML syntax",
|
|
241
|
-
cause: error
|
|
242
|
-
})
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
return yield* validateConfigEffect(rawConfig);
|
|
246
|
-
});
|
|
247
|
-
var makeConfigCache = () => Cache.make({
|
|
248
|
-
capacity: 100,
|
|
249
|
-
timeToLive: Duration.minutes(5),
|
|
250
|
-
lookup: (url) => fetchAndParseConfig(url)
|
|
251
|
-
});
|
|
252
|
-
var cacheInstance = null;
|
|
253
|
-
var getCache = Effect.gen(function* () {
|
|
254
|
-
if (!cacheInstance) {
|
|
255
|
-
cacheInstance = yield* makeConfigCache();
|
|
256
|
-
}
|
|
257
|
-
return cacheInstance;
|
|
258
|
-
});
|
|
259
|
-
var loadConfigFromUrlEffect = (url, cacheTimeout = SECURITY_DEFAULTS.cacheTimeout) => Effect.gen(function* () {
|
|
260
|
-
if (cacheTimeout === 0) {
|
|
261
|
-
return yield* fetchAndParseConfig(url);
|
|
262
|
-
}
|
|
263
|
-
const cache = yield* getCache;
|
|
264
|
-
return yield* cache.get(url);
|
|
265
|
-
});
|
|
266
|
-
var loadConfigEffect = (options = {}) => Effect.gen(function* () {
|
|
267
|
-
if (options.config) {
|
|
268
|
-
return yield* validateConfigEffect(options.config);
|
|
269
|
-
}
|
|
270
|
-
const envConfigPath = process.env.ATRIM_INSTRUMENTATION_CONFIG;
|
|
271
|
-
if (envConfigPath) {
|
|
272
|
-
if (envConfigPath.startsWith("http://") || envConfigPath.startsWith("https://")) {
|
|
273
|
-
return yield* loadConfigFromUrlEffect(envConfigPath, options.cacheTimeout);
|
|
274
|
-
}
|
|
275
|
-
return yield* loadConfigFromFileEffect(envConfigPath);
|
|
276
|
-
}
|
|
277
|
-
if (options.configUrl) {
|
|
278
|
-
return yield* loadConfigFromUrlEffect(options.configUrl, options.cacheTimeout);
|
|
279
|
-
}
|
|
280
|
-
if (options.configPath) {
|
|
281
|
-
return yield* loadConfigFromFileEffect(options.configPath);
|
|
282
|
-
}
|
|
283
|
-
const defaultPath = join(process.cwd(), "instrumentation.yaml");
|
|
284
|
-
const exists = yield* Effect.sync(() => existsSync(defaultPath));
|
|
285
|
-
if (exists) {
|
|
286
|
-
return yield* loadConfigFromFileEffect(defaultPath);
|
|
287
|
-
}
|
|
288
|
-
return getDefaultConfig();
|
|
289
|
-
});
|
|
290
|
-
async function loadConfig(options = {}) {
|
|
291
|
-
return Effect.runPromise(
|
|
292
|
-
loadConfigEffect(options).pipe(
|
|
293
|
-
// Convert typed errors to regular Error with reason message for backward compatibility
|
|
294
|
-
Effect.mapError((error) => {
|
|
295
|
-
const message = error.reason;
|
|
296
|
-
const newError = new Error(message);
|
|
297
|
-
newError.cause = error.cause;
|
|
298
|
-
return newError;
|
|
299
|
-
})
|
|
300
|
-
)
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
var PatternMatcher = class {
|
|
304
|
-
constructor(config) {
|
|
305
|
-
__publicField2(this, "ignorePatterns", []);
|
|
306
|
-
__publicField2(this, "instrumentPatterns", []);
|
|
307
|
-
__publicField2(this, "enabled", true);
|
|
308
|
-
this.enabled = config.instrumentation.enabled;
|
|
309
|
-
this.ignorePatterns = config.instrumentation.ignore_patterns.map((p) => this.compilePattern(p));
|
|
310
|
-
this.instrumentPatterns = config.instrumentation.instrument_patterns.filter((p) => p.enabled !== false).map((p) => this.compilePattern(p));
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Compile a pattern configuration into a RegExp
|
|
314
|
-
*/
|
|
315
|
-
compilePattern(pattern) {
|
|
316
|
-
try {
|
|
317
|
-
const compiled = {
|
|
318
|
-
regex: new RegExp(pattern.pattern),
|
|
319
|
-
enabled: pattern.enabled !== false
|
|
320
|
-
};
|
|
321
|
-
if (pattern.description !== void 0) {
|
|
322
|
-
compiled.description = pattern.description;
|
|
323
|
-
}
|
|
324
|
-
return compiled;
|
|
325
|
-
} catch (error) {
|
|
326
|
-
throw new Error(
|
|
327
|
-
`Failed to compile pattern "${pattern.pattern}": ${error instanceof Error ? error.message : String(error)}`
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Check if a span should be instrumented
|
|
333
|
-
*
|
|
334
|
-
* Returns true if the span should be created, false otherwise.
|
|
335
|
-
*
|
|
336
|
-
* Logic:
|
|
337
|
-
* 1. If instrumentation disabled globally, return false
|
|
338
|
-
* 2. Check ignore patterns - if any match, return false
|
|
339
|
-
* 3. Check instrument patterns - if any match, return true
|
|
340
|
-
* 4. Default: return true (fail-open - create span if no patterns match)
|
|
341
|
-
*/
|
|
342
|
-
shouldInstrument(spanName) {
|
|
343
|
-
if (!this.enabled) {
|
|
344
|
-
return false;
|
|
345
|
-
}
|
|
346
|
-
for (const pattern of this.ignorePatterns) {
|
|
347
|
-
if (pattern.regex.test(spanName)) {
|
|
348
|
-
return false;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
for (const pattern of this.instrumentPatterns) {
|
|
352
|
-
if (pattern.enabled && pattern.regex.test(spanName)) {
|
|
353
|
-
return true;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
return true;
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* Get statistics about pattern matching (for debugging/monitoring)
|
|
360
|
-
*/
|
|
361
|
-
getStats() {
|
|
362
|
-
return {
|
|
363
|
-
enabled: this.enabled,
|
|
364
|
-
ignorePatternCount: this.ignorePatterns.length,
|
|
365
|
-
instrumentPatternCount: this.instrumentPatterns.filter((p) => p.enabled).length
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
var globalMatcher = null;
|
|
370
|
-
function initializePatternMatcher(config) {
|
|
371
|
-
globalMatcher = new PatternMatcher(config);
|
|
372
|
-
}
|
|
373
|
-
function shouldInstrumentSpan(spanName) {
|
|
374
|
-
if (!globalMatcher) {
|
|
375
|
-
return true;
|
|
376
|
-
}
|
|
377
|
-
return globalMatcher.shouldInstrument(spanName);
|
|
378
|
-
}
|
|
379
|
-
function getPatternMatcher() {
|
|
380
|
-
return globalMatcher;
|
|
381
|
-
}
|
|
382
|
-
var Logger = class {
|
|
383
|
-
constructor() {
|
|
384
|
-
__publicField2(this, "level", "on");
|
|
385
|
-
__publicField2(this, "hasLoggedMinimal", false);
|
|
386
|
-
}
|
|
387
|
-
/**
|
|
388
|
-
* Set the logging level
|
|
389
|
-
*/
|
|
390
|
-
setLevel(level) {
|
|
391
|
-
this.level = level;
|
|
392
|
-
this.hasLoggedMinimal = false;
|
|
393
|
-
}
|
|
394
|
-
/**
|
|
395
|
-
* Get the current logging level
|
|
396
|
-
*/
|
|
397
|
-
getLevel() {
|
|
398
|
-
return this.level;
|
|
399
|
-
}
|
|
400
|
-
/**
|
|
401
|
-
* Log a minimal initialization message (only shown once in minimal mode)
|
|
402
|
-
*/
|
|
403
|
-
minimal(message) {
|
|
404
|
-
if (this.level === "off") {
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
if (this.level === "minimal" && !this.hasLoggedMinimal) {
|
|
408
|
-
console.log(message);
|
|
409
|
-
this.hasLoggedMinimal = true;
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
if (this.level === "on") {
|
|
413
|
-
console.log(message);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
/**
|
|
417
|
-
* Log an informational message
|
|
418
|
-
*/
|
|
419
|
-
log(...args) {
|
|
420
|
-
if (this.level === "on") {
|
|
421
|
-
console.log(...args);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
/**
|
|
425
|
-
* Log a warning message (shown in minimal mode)
|
|
426
|
-
*/
|
|
427
|
-
warn(...args) {
|
|
428
|
-
if (this.level !== "off") {
|
|
429
|
-
console.warn(...args);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
/**
|
|
433
|
-
* Log an error message (shown in minimal mode)
|
|
434
|
-
*/
|
|
435
|
-
error(...args) {
|
|
436
|
-
if (this.level !== "off") {
|
|
437
|
-
console.error(...args);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
/**
|
|
441
|
-
* Check if full logging is enabled
|
|
442
|
-
*/
|
|
443
|
-
isEnabled() {
|
|
444
|
-
return this.level === "on";
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* Check if minimal logging is enabled
|
|
448
|
-
*/
|
|
449
|
-
isMinimal() {
|
|
450
|
-
return this.level === "minimal";
|
|
451
|
-
}
|
|
452
|
-
/**
|
|
453
|
-
* Check if logging is completely disabled
|
|
454
|
-
*/
|
|
455
|
-
isDisabled() {
|
|
456
|
-
return this.level === "off";
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
var logger = new Logger();
|
|
460
|
-
|
|
461
|
-
// src/core/span-processor.ts
|
|
462
23
|
var PatternSpanProcessor = class {
|
|
463
24
|
constructor(config, wrappedProcessor) {
|
|
464
25
|
__publicField(this, "matcher");
|
|
@@ -536,8 +97,6 @@ function getOtlpEndpoint(options = {}) {
|
|
|
536
97
|
const endpoint = options.endpoint || process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || DEFAULT_OTLP_ENDPOINT;
|
|
537
98
|
return normalizeEndpoint(endpoint);
|
|
538
99
|
}
|
|
539
|
-
|
|
540
|
-
// src/core/safe-exporter.ts
|
|
541
100
|
var SafeSpanExporter = class {
|
|
542
101
|
// Log errors max once per minute
|
|
543
102
|
constructor(exporter) {
|
|
@@ -637,21 +196,21 @@ var SafeSpanExporter = class {
|
|
|
637
196
|
return false;
|
|
638
197
|
}
|
|
639
198
|
};
|
|
640
|
-
var
|
|
199
|
+
var ConfigError = class extends Data.TaggedError("ConfigError") {
|
|
641
200
|
};
|
|
642
|
-
var
|
|
201
|
+
var ConfigUrlError = class extends Data.TaggedError("ConfigUrlError") {
|
|
643
202
|
};
|
|
644
|
-
var
|
|
203
|
+
var ConfigValidationError = class extends Data.TaggedError("ConfigValidationError") {
|
|
645
204
|
};
|
|
646
|
-
var
|
|
205
|
+
var ConfigFileError = class extends Data.TaggedError("ConfigFileError") {
|
|
647
206
|
};
|
|
648
|
-
var
|
|
207
|
+
var ServiceDetectionError = class extends Data.TaggedError("ServiceDetectionError") {
|
|
649
208
|
};
|
|
650
|
-
var
|
|
209
|
+
var InitializationError = class extends Data.TaggedError("InitializationError") {
|
|
651
210
|
};
|
|
652
|
-
var
|
|
211
|
+
var ExportError = class extends Data.TaggedError("ExportError") {
|
|
653
212
|
};
|
|
654
|
-
var
|
|
213
|
+
var ShutdownError = class extends Data.TaggedError("ShutdownError") {
|
|
655
214
|
};
|
|
656
215
|
|
|
657
216
|
// src/core/service-detector.ts
|
|
@@ -668,7 +227,7 @@ var detectServiceInfo = Effect.gen(
|
|
|
668
227
|
const packageJsonPath = join(process.cwd(), "package.json");
|
|
669
228
|
const packageJsonContent = yield* Effect.tryPromise({
|
|
670
229
|
try: () => readFile(packageJsonPath, "utf-8"),
|
|
671
|
-
catch: (error) => new
|
|
230
|
+
catch: (error) => new ServiceDetectionError({
|
|
672
231
|
reason: `Failed to read package.json at ${packageJsonPath}`,
|
|
673
232
|
cause: error
|
|
674
233
|
})
|
|
@@ -678,7 +237,7 @@ var detectServiceInfo = Effect.gen(
|
|
|
678
237
|
parsed = JSON.parse(packageJsonContent);
|
|
679
238
|
} catch (error) {
|
|
680
239
|
yield* Effect.fail(
|
|
681
|
-
new
|
|
240
|
+
new ServiceDetectionError({
|
|
682
241
|
reason: "Invalid JSON in package.json",
|
|
683
242
|
cause: error
|
|
684
243
|
})
|
|
@@ -694,7 +253,7 @@ var detectServiceInfo = Effect.gen(
|
|
|
694
253
|
}
|
|
695
254
|
}
|
|
696
255
|
return yield* Effect.fail(
|
|
697
|
-
new
|
|
256
|
+
new ServiceDetectionError({
|
|
698
257
|
reason: 'package.json exists but has no "name" field'
|
|
699
258
|
})
|
|
700
259
|
);
|
|
@@ -725,6 +284,84 @@ async function getServiceNameAsync() {
|
|
|
725
284
|
async function getServiceVersionAsync() {
|
|
726
285
|
return Effect.runPromise(getServiceVersion);
|
|
727
286
|
}
|
|
287
|
+
var NodeConfigLoaderLive = ConfigLoaderLive.pipe(
|
|
288
|
+
Layer.provide(Layer.mergeAll(NodeContext.layer, FetchHttpClient.layer))
|
|
289
|
+
);
|
|
290
|
+
var cachedLoaderPromise = null;
|
|
291
|
+
function getCachedLoader() {
|
|
292
|
+
if (!cachedLoaderPromise) {
|
|
293
|
+
cachedLoaderPromise = Effect.runPromise(
|
|
294
|
+
Effect.gen(function* () {
|
|
295
|
+
return yield* ConfigLoader;
|
|
296
|
+
}).pipe(Effect.provide(NodeConfigLoaderLive))
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
return cachedLoaderPromise;
|
|
300
|
+
}
|
|
301
|
+
function _resetConfigLoaderCache() {
|
|
302
|
+
cachedLoaderPromise = null;
|
|
303
|
+
}
|
|
304
|
+
async function loadConfig(uri, options) {
|
|
305
|
+
if (options?.cacheTimeout === 0) {
|
|
306
|
+
const program = Effect.gen(function* () {
|
|
307
|
+
const loader2 = yield* ConfigLoader;
|
|
308
|
+
return yield* loader2.loadFromUri(uri);
|
|
309
|
+
});
|
|
310
|
+
return Effect.runPromise(program.pipe(Effect.provide(NodeConfigLoaderLive)));
|
|
311
|
+
}
|
|
312
|
+
const loader = await getCachedLoader();
|
|
313
|
+
return Effect.runPromise(loader.loadFromUri(uri));
|
|
314
|
+
}
|
|
315
|
+
async function loadConfigFromInline(content) {
|
|
316
|
+
const loader = await getCachedLoader();
|
|
317
|
+
return Effect.runPromise(loader.loadFromInline(content));
|
|
318
|
+
}
|
|
319
|
+
function getDefaultConfig() {
|
|
320
|
+
return {
|
|
321
|
+
version: "1.0",
|
|
322
|
+
instrumentation: {
|
|
323
|
+
enabled: true,
|
|
324
|
+
logging: "on",
|
|
325
|
+
description: "Default instrumentation configuration",
|
|
326
|
+
instrument_patterns: [
|
|
327
|
+
{ pattern: "^app\\.", enabled: true, description: "Application operations" },
|
|
328
|
+
{ pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
|
|
329
|
+
{ pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
|
|
330
|
+
],
|
|
331
|
+
ignore_patterns: [
|
|
332
|
+
{ pattern: "^test\\.", description: "Test utilities" },
|
|
333
|
+
{ pattern: "^internal\\.", description: "Internal operations" },
|
|
334
|
+
{ pattern: "^health\\.", description: "Health checks" }
|
|
335
|
+
]
|
|
336
|
+
},
|
|
337
|
+
effect: {
|
|
338
|
+
auto_extract_metadata: true
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
async function loadConfigWithOptions(options = {}) {
|
|
343
|
+
const loadOptions = options.cacheTimeout !== void 0 ? { cacheTimeout: options.cacheTimeout } : void 0;
|
|
344
|
+
if (options.config) {
|
|
345
|
+
return loadConfigFromInline(options.config);
|
|
346
|
+
}
|
|
347
|
+
const envConfigPath = process.env.ATRIM_INSTRUMENTATION_CONFIG;
|
|
348
|
+
if (envConfigPath) {
|
|
349
|
+
return loadConfig(envConfigPath, loadOptions);
|
|
350
|
+
}
|
|
351
|
+
if (options.configUrl) {
|
|
352
|
+
return loadConfig(options.configUrl, loadOptions);
|
|
353
|
+
}
|
|
354
|
+
if (options.configPath) {
|
|
355
|
+
return loadConfig(options.configPath, loadOptions);
|
|
356
|
+
}
|
|
357
|
+
const { existsSync } = await import('fs');
|
|
358
|
+
const { join: join2 } = await import('path');
|
|
359
|
+
const defaultPath = join2(process.cwd(), "instrumentation.yaml");
|
|
360
|
+
if (existsSync(defaultPath)) {
|
|
361
|
+
return loadConfig(defaultPath, loadOptions);
|
|
362
|
+
}
|
|
363
|
+
return getDefaultConfig();
|
|
364
|
+
}
|
|
728
365
|
|
|
729
366
|
// src/core/sdk-initializer.ts
|
|
730
367
|
var sdkInstance = null;
|
|
@@ -864,7 +501,7 @@ async function initializeSdk(options = {}) {
|
|
|
864
501
|
}
|
|
865
502
|
}
|
|
866
503
|
async function performInitialization(options) {
|
|
867
|
-
const config = await
|
|
504
|
+
const config = await loadConfigWithOptions(options);
|
|
868
505
|
const loggingLevel = config.instrumentation.logging || "on";
|
|
869
506
|
logger.setLevel(loggingLevel);
|
|
870
507
|
const alreadyInitialized = isTracingAlreadyInitialized();
|
|
@@ -1015,18 +652,16 @@ function logInitialization(config, serviceName, serviceVersion, options, autoIns
|
|
|
1015
652
|
logger.log(` - OTLP endpoint: ${endpoint}`);
|
|
1016
653
|
logger.log("");
|
|
1017
654
|
}
|
|
1018
|
-
|
|
1019
|
-
// src/api.ts
|
|
1020
655
|
async function initializeInstrumentation(options = {}) {
|
|
1021
656
|
const sdk = await initializeSdk(options);
|
|
1022
657
|
if (sdk) {
|
|
1023
|
-
const config = await
|
|
658
|
+
const config = await loadConfigWithOptions(options);
|
|
1024
659
|
initializePatternMatcher(config);
|
|
1025
660
|
}
|
|
1026
661
|
return sdk;
|
|
1027
662
|
}
|
|
1028
663
|
async function initializePatternMatchingOnly(options = {}) {
|
|
1029
|
-
const config = await
|
|
664
|
+
const config = await loadConfigWithOptions(options);
|
|
1030
665
|
initializePatternMatcher(config);
|
|
1031
666
|
logger.log("@atrim/instrumentation: Pattern matching initialized (legacy mode)");
|
|
1032
667
|
logger.log(
|
|
@@ -1036,15 +671,15 @@ async function initializePatternMatchingOnly(options = {}) {
|
|
|
1036
671
|
var initializeInstrumentationEffect = (options = {}) => Effect.gen(function* () {
|
|
1037
672
|
const sdk = yield* Effect.tryPromise({
|
|
1038
673
|
try: () => initializeSdk(options),
|
|
1039
|
-
catch: (error) => new
|
|
674
|
+
catch: (error) => new InitializationError({
|
|
1040
675
|
reason: "SDK initialization failed",
|
|
1041
676
|
cause: error
|
|
1042
677
|
})
|
|
1043
678
|
});
|
|
1044
679
|
if (sdk) {
|
|
1045
680
|
yield* Effect.tryPromise({
|
|
1046
|
-
try: () =>
|
|
1047
|
-
catch: (error) => new
|
|
681
|
+
try: () => loadConfigWithOptions(options),
|
|
682
|
+
catch: (error) => new ConfigError({
|
|
1048
683
|
reason: "Failed to load config for pattern matcher",
|
|
1049
684
|
cause: error
|
|
1050
685
|
})
|
|
@@ -1060,8 +695,8 @@ var initializeInstrumentationEffect = (options = {}) => Effect.gen(function* ()
|
|
|
1060
695
|
});
|
|
1061
696
|
var initializePatternMatchingOnlyEffect = (options = {}) => Effect.gen(function* () {
|
|
1062
697
|
const config = yield* Effect.tryPromise({
|
|
1063
|
-
try: () =>
|
|
1064
|
-
catch: (error) => new
|
|
698
|
+
try: () => loadConfigWithOptions(options),
|
|
699
|
+
catch: (error) => new ConfigError({
|
|
1065
700
|
reason: "Failed to load configuration",
|
|
1066
701
|
cause: error
|
|
1067
702
|
})
|
|
@@ -1169,6 +804,6 @@ function suppressShutdownErrors() {
|
|
|
1169
804
|
});
|
|
1170
805
|
}
|
|
1171
806
|
|
|
1172
|
-
export {
|
|
807
|
+
export { ConfigError, ConfigFileError, ConfigUrlError, ConfigValidationError, ExportError, InitializationError, PatternSpanProcessor, ServiceDetectionError, ShutdownError, annotateCacheOperation, annotateDbQuery, annotateHttpRequest, _resetConfigLoaderCache as clearConfigCache, createOtlpExporter, detectServiceInfoAsync as detectServiceInfo, detectServiceInfo as detectServiceInfoEffect, getOtlpEndpoint, getSdkInstance, getServiceInfoWithFallback, getServiceNameAsync as getServiceName, getServiceName as getServiceNameEffect, getServiceVersionAsync as getServiceVersion, getServiceVersion as getServiceVersionEffect, initializeInstrumentation, initializeInstrumentationEffect, initializePatternMatchingOnly, initializePatternMatchingOnlyEffect, loadConfig, loadConfigFromInline, loadConfigWithOptions, markSpanError, markSpanSuccess, recordException, resetSdk, setSpanAttributes, shutdownSdk, suppressShutdownErrors };
|
|
1173
808
|
//# sourceMappingURL=index.js.map
|
|
1174
809
|
//# sourceMappingURL=index.js.map
|