@atrim/instrument-node 0.4.0 → 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 +8 -7
- package/target/dist/index.cjs +317 -264
- package/target/dist/index.cjs.map +1 -1
- package/target/dist/index.d.cts +163 -142
- package/target/dist/index.d.ts +163 -142
- package/target/dist/index.js +292 -258
- package/target/dist/index.js.map +1 -1
- package/target/dist/integrations/effect/index.cjs +234 -192
- 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 +233 -193
- package/target/dist/integrations/effect/index.js.map +1 -1
package/target/dist/index.cjs
CHANGED
|
@@ -5,12 +5,37 @@ var sdkNode = require('@opentelemetry/sdk-node');
|
|
|
5
5
|
var sdkTraceBase = require('@opentelemetry/sdk-trace-base');
|
|
6
6
|
var autoInstrumentationsNode = require('@opentelemetry/auto-instrumentations-node');
|
|
7
7
|
var api = require('@opentelemetry/api');
|
|
8
|
-
var
|
|
9
|
-
var
|
|
8
|
+
var FileSystem = require('@effect/platform/FileSystem');
|
|
9
|
+
var HttpClient = require('@effect/platform/HttpClient');
|
|
10
|
+
var HttpClientRequest = require('@effect/platform/HttpClientRequest');
|
|
10
11
|
var yaml = require('yaml');
|
|
11
12
|
var zod = require('zod');
|
|
12
13
|
var exporterTraceOtlpHttp = require('@opentelemetry/exporter-trace-otlp-http');
|
|
13
14
|
var promises = require('fs/promises');
|
|
15
|
+
var path = require('path');
|
|
16
|
+
var platformNode = require('@effect/platform-node');
|
|
17
|
+
var platform = require('@effect/platform');
|
|
18
|
+
|
|
19
|
+
function _interopNamespace(e) {
|
|
20
|
+
if (e && e.__esModule) return e;
|
|
21
|
+
var n = Object.create(null);
|
|
22
|
+
if (e) {
|
|
23
|
+
Object.keys(e).forEach(function (k) {
|
|
24
|
+
if (k !== 'default') {
|
|
25
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
26
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
get: function () { return e[k]; }
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
n.default = e;
|
|
34
|
+
return Object.freeze(n);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
var HttpClient__namespace = /*#__PURE__*/_interopNamespace(HttpClient);
|
|
38
|
+
var HttpClientRequest__namespace = /*#__PURE__*/_interopNamespace(HttpClientRequest);
|
|
14
39
|
|
|
15
40
|
var __defProp = Object.defineProperty;
|
|
16
41
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -57,7 +82,20 @@ var HttpFilteringConfigSchema = zod.z.object({
|
|
|
57
82
|
// Patterns to ignore for incoming HTTP requests (string patterns only in YAML)
|
|
58
83
|
ignore_incoming_paths: zod.z.array(zod.z.string()).optional(),
|
|
59
84
|
// Require parent span for outgoing requests (prevents root spans for HTTP calls)
|
|
60
|
-
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()
|
|
61
99
|
});
|
|
62
100
|
var InstrumentationConfigSchema = zod.z.object({
|
|
63
101
|
version: zod.z.string(),
|
|
@@ -75,233 +113,183 @@ var InstrumentationConfigSchema = zod.z.object({
|
|
|
75
113
|
http: HttpFilteringConfigSchema.optional()
|
|
76
114
|
});
|
|
77
115
|
(class extends effect.Data.TaggedError("ConfigError") {
|
|
116
|
+
get message() {
|
|
117
|
+
return this.reason;
|
|
118
|
+
}
|
|
78
119
|
});
|
|
79
120
|
var ConfigUrlError = class extends effect.Data.TaggedError("ConfigUrlError") {
|
|
121
|
+
get message() {
|
|
122
|
+
return this.reason;
|
|
123
|
+
}
|
|
80
124
|
};
|
|
81
125
|
var ConfigValidationError = class extends effect.Data.TaggedError("ConfigValidationError") {
|
|
126
|
+
get message() {
|
|
127
|
+
return this.reason;
|
|
128
|
+
}
|
|
82
129
|
};
|
|
83
130
|
var ConfigFileError = class extends effect.Data.TaggedError("ConfigFileError") {
|
|
131
|
+
get message() {
|
|
132
|
+
return this.reason;
|
|
133
|
+
}
|
|
84
134
|
};
|
|
85
135
|
(class extends effect.Data.TaggedError("ServiceDetectionError") {
|
|
136
|
+
get message() {
|
|
137
|
+
return this.reason;
|
|
138
|
+
}
|
|
86
139
|
});
|
|
87
140
|
(class extends effect.Data.TaggedError("InitializationError") {
|
|
141
|
+
get message() {
|
|
142
|
+
return this.reason;
|
|
143
|
+
}
|
|
88
144
|
});
|
|
89
145
|
(class extends effect.Data.TaggedError("ExportError") {
|
|
146
|
+
get message() {
|
|
147
|
+
return this.reason;
|
|
148
|
+
}
|
|
90
149
|
});
|
|
91
150
|
(class extends effect.Data.TaggedError("ShutdownError") {
|
|
151
|
+
get message() {
|
|
152
|
+
return this.reason;
|
|
153
|
+
}
|
|
92
154
|
});
|
|
93
155
|
var SECURITY_DEFAULTS = {
|
|
94
156
|
maxConfigSize: 1e6,
|
|
95
157
|
// 1MB
|
|
96
|
-
requestTimeout: 5e3
|
|
97
|
-
|
|
98
|
-
// Only HTTPS for remote configs
|
|
99
|
-
cacheTimeout: 3e5
|
|
100
|
-
// 5 minutes
|
|
158
|
+
requestTimeout: 5e3
|
|
159
|
+
// 5 seconds
|
|
101
160
|
};
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
instrument_patterns: [
|
|
110
|
-
{ pattern: "^app\\.", enabled: true, description: "Application operations" },
|
|
111
|
-
{ pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
|
|
112
|
-
{ pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
|
|
113
|
-
],
|
|
114
|
-
ignore_patterns: [
|
|
115
|
-
{ pattern: "^test\\.", description: "Test utilities" },
|
|
116
|
-
{ pattern: "^internal\\.", description: "Internal operations" },
|
|
117
|
-
{ pattern: "^health\\.", description: "Health checks" }
|
|
118
|
-
]
|
|
119
|
-
},
|
|
120
|
-
effect: {
|
|
121
|
-
auto_extract_metadata: true
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
var validateConfigEffect = (rawConfig) => effect.Effect.try({
|
|
126
|
-
try: () => InstrumentationConfigSchema.parse(rawConfig),
|
|
127
|
-
catch: (error) => new ConfigValidationError({
|
|
128
|
-
reason: "Invalid configuration schema",
|
|
129
|
-
cause: error
|
|
130
|
-
})
|
|
131
|
-
});
|
|
132
|
-
var loadConfigFromFileEffect = (filePath) => effect.Effect.gen(function* () {
|
|
133
|
-
const fileContents = yield* effect.Effect.try({
|
|
134
|
-
try: () => fs.readFileSync(filePath, "utf8"),
|
|
135
|
-
catch: (error) => new ConfigFileError({
|
|
136
|
-
reason: `Failed to read config file at ${filePath}`,
|
|
161
|
+
var ConfigLoader = class extends effect.Context.Tag("ConfigLoader")() {
|
|
162
|
+
};
|
|
163
|
+
var parseYamlContent = (content, uri) => effect.Effect.gen(function* () {
|
|
164
|
+
const parsed = yield* effect.Effect.try({
|
|
165
|
+
try: () => yaml.parse(content),
|
|
166
|
+
catch: (error) => new ConfigValidationError({
|
|
167
|
+
reason: uri ? `Failed to parse YAML from ${uri}` : "Failed to parse YAML",
|
|
137
168
|
cause: error
|
|
138
169
|
})
|
|
139
170
|
});
|
|
140
|
-
|
|
171
|
+
return yield* effect.Effect.try({
|
|
172
|
+
try: () => InstrumentationConfigSchema.parse(parsed),
|
|
173
|
+
catch: (error) => new ConfigValidationError({
|
|
174
|
+
reason: uri ? `Invalid configuration schema from ${uri}` : "Invalid configuration schema",
|
|
175
|
+
cause: error
|
|
176
|
+
})
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
var loadFromFileWithFs = (fs, path, uri) => effect.Effect.gen(function* () {
|
|
180
|
+
const content = yield* fs.readFileString(path).pipe(
|
|
181
|
+
effect.Effect.mapError(
|
|
182
|
+
(error) => new ConfigFileError({
|
|
183
|
+
reason: `Failed to read config file at ${uri}`,
|
|
184
|
+
cause: error
|
|
185
|
+
})
|
|
186
|
+
)
|
|
187
|
+
);
|
|
188
|
+
if (content.length > SECURITY_DEFAULTS.maxConfigSize) {
|
|
141
189
|
return yield* effect.Effect.fail(
|
|
142
190
|
new ConfigFileError({
|
|
143
191
|
reason: `Config file exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
|
|
144
192
|
})
|
|
145
193
|
);
|
|
146
194
|
}
|
|
147
|
-
|
|
148
|
-
try {
|
|
149
|
-
rawConfig = yaml.parse(fileContents);
|
|
150
|
-
} catch (error) {
|
|
151
|
-
return yield* effect.Effect.fail(
|
|
152
|
-
new ConfigValidationError({
|
|
153
|
-
reason: "Invalid YAML syntax",
|
|
154
|
-
cause: error
|
|
155
|
-
})
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
return yield* validateConfigEffect(rawConfig);
|
|
195
|
+
return yield* parseYamlContent(content, uri);
|
|
159
196
|
});
|
|
160
|
-
var
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
197
|
+
var loadFromHttpWithClient = (client, url) => effect.Effect.scoped(
|
|
198
|
+
effect.Effect.gen(function* () {
|
|
199
|
+
if (url.startsWith("http://")) {
|
|
200
|
+
return yield* effect.Effect.fail(
|
|
201
|
+
new ConfigUrlError({
|
|
202
|
+
reason: "Insecure protocol: only HTTPS URLs are allowed"
|
|
203
|
+
})
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
const request = HttpClientRequest__namespace.get(url).pipe(
|
|
207
|
+
HttpClientRequest__namespace.setHeaders({
|
|
208
|
+
Accept: "application/yaml, text/yaml, application/x-yaml"
|
|
169
209
|
})
|
|
170
210
|
);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
211
|
+
const response = yield* client.execute(request).pipe(
|
|
212
|
+
effect.Effect.timeout(`${SECURITY_DEFAULTS.requestTimeout} millis`),
|
|
213
|
+
effect.Effect.mapError((error) => {
|
|
214
|
+
if (error._tag === "TimeoutException") {
|
|
215
|
+
return new ConfigUrlError({
|
|
216
|
+
reason: `Config fetch timeout after ${SECURITY_DEFAULTS.requestTimeout}ms from ${url}`
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
return new ConfigUrlError({
|
|
220
|
+
reason: `Failed to load config from URL: ${url}`,
|
|
221
|
+
cause: error
|
|
222
|
+
});
|
|
176
223
|
})
|
|
177
224
|
);
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
225
|
+
if (response.status >= 400) {
|
|
226
|
+
return yield* effect.Effect.fail(
|
|
227
|
+
new ConfigUrlError({
|
|
228
|
+
reason: `HTTP ${response.status} from ${url}`
|
|
229
|
+
})
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
const text = yield* response.text.pipe(
|
|
233
|
+
effect.Effect.mapError(
|
|
234
|
+
(error) => new ConfigUrlError({
|
|
235
|
+
reason: `Failed to read response body from ${url}`,
|
|
236
|
+
cause: error
|
|
237
|
+
})
|
|
238
|
+
)
|
|
239
|
+
);
|
|
240
|
+
if (text.length > SECURITY_DEFAULTS.maxConfigSize) {
|
|
241
|
+
return yield* effect.Effect.fail(
|
|
242
|
+
new ConfigUrlError({
|
|
243
|
+
reason: `Config exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
|
|
244
|
+
})
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
return yield* parseYamlContent(text, url);
|
|
248
|
+
})
|
|
249
|
+
);
|
|
250
|
+
var makeConfigLoader = effect.Effect.gen(function* () {
|
|
251
|
+
const fs = yield* effect.Effect.serviceOption(FileSystem.FileSystem);
|
|
252
|
+
const http = yield* HttpClient__namespace.HttpClient;
|
|
253
|
+
const loadFromUriUncached = (uri) => effect.Effect.gen(function* () {
|
|
254
|
+
if (uri.startsWith("file://")) {
|
|
255
|
+
const path = uri.slice(7);
|
|
256
|
+
if (fs._tag === "None") {
|
|
257
|
+
return yield* effect.Effect.fail(
|
|
258
|
+
new ConfigFileError({
|
|
259
|
+
reason: "FileSystem not available (browser environment?)",
|
|
260
|
+
cause: { uri }
|
|
201
261
|
})
|
|
202
262
|
);
|
|
203
263
|
}
|
|
204
|
-
return
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
264
|
+
return yield* loadFromFileWithFs(fs.value, path, uri);
|
|
265
|
+
}
|
|
266
|
+
if (uri.startsWith("http://") || uri.startsWith("https://")) {
|
|
267
|
+
return yield* loadFromHttpWithClient(http, uri);
|
|
268
|
+
}
|
|
269
|
+
if (fs._tag === "Some") {
|
|
270
|
+
return yield* loadFromFileWithFs(fs.value, uri, uri);
|
|
271
|
+
} else {
|
|
272
|
+
return yield* loadFromHttpWithClient(http, uri);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
const loadFromUriCached = yield* effect.Effect.cachedFunction(loadFromUriUncached);
|
|
276
|
+
return ConfigLoader.of({
|
|
277
|
+
loadFromUri: loadFromUriCached,
|
|
278
|
+
loadFromInline: (content) => effect.Effect.gen(function* () {
|
|
279
|
+
if (typeof content === "string") {
|
|
280
|
+
return yield* parseYamlContent(content);
|
|
281
|
+
}
|
|
282
|
+
return yield* effect.Effect.try({
|
|
283
|
+
try: () => InstrumentationConfigSchema.parse(content),
|
|
284
|
+
catch: (error) => new ConfigValidationError({
|
|
285
|
+
reason: "Invalid configuration schema",
|
|
286
|
+
cause: error
|
|
287
|
+
})
|
|
288
|
+
});
|
|
227
289
|
})
|
|
228
290
|
});
|
|
229
|
-
if (text.length > SECURITY_DEFAULTS.maxConfigSize) {
|
|
230
|
-
return yield* effect.Effect.fail(
|
|
231
|
-
new ConfigUrlError({
|
|
232
|
-
reason: `Config exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
|
|
233
|
-
})
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
let rawConfig;
|
|
237
|
-
try {
|
|
238
|
-
rawConfig = yaml.parse(text);
|
|
239
|
-
} catch (error) {
|
|
240
|
-
return yield* effect.Effect.fail(
|
|
241
|
-
new ConfigValidationError({
|
|
242
|
-
reason: "Invalid YAML syntax",
|
|
243
|
-
cause: error
|
|
244
|
-
})
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
return yield* validateConfigEffect(rawConfig);
|
|
248
|
-
});
|
|
249
|
-
var makeConfigCache = () => effect.Cache.make({
|
|
250
|
-
capacity: 100,
|
|
251
|
-
timeToLive: effect.Duration.minutes(5),
|
|
252
|
-
lookup: (url) => fetchAndParseConfig(url)
|
|
253
|
-
});
|
|
254
|
-
var cacheInstance = null;
|
|
255
|
-
var getCache = effect.Effect.gen(function* () {
|
|
256
|
-
if (!cacheInstance) {
|
|
257
|
-
cacheInstance = yield* makeConfigCache();
|
|
258
|
-
}
|
|
259
|
-
return cacheInstance;
|
|
260
291
|
});
|
|
261
|
-
var
|
|
262
|
-
if (cacheTimeout === 0) {
|
|
263
|
-
return yield* fetchAndParseConfig(url);
|
|
264
|
-
}
|
|
265
|
-
const cache = yield* getCache;
|
|
266
|
-
return yield* cache.get(url);
|
|
267
|
-
});
|
|
268
|
-
var loadConfigEffect = (options = {}) => effect.Effect.gen(function* () {
|
|
269
|
-
if (options.config) {
|
|
270
|
-
return yield* validateConfigEffect(options.config);
|
|
271
|
-
}
|
|
272
|
-
const envConfigPath = process.env.ATRIM_INSTRUMENTATION_CONFIG;
|
|
273
|
-
if (envConfigPath) {
|
|
274
|
-
if (envConfigPath.startsWith("http://") || envConfigPath.startsWith("https://")) {
|
|
275
|
-
return yield* loadConfigFromUrlEffect(envConfigPath, options.cacheTimeout);
|
|
276
|
-
}
|
|
277
|
-
return yield* loadConfigFromFileEffect(envConfigPath);
|
|
278
|
-
}
|
|
279
|
-
if (options.configUrl) {
|
|
280
|
-
return yield* loadConfigFromUrlEffect(options.configUrl, options.cacheTimeout);
|
|
281
|
-
}
|
|
282
|
-
if (options.configPath) {
|
|
283
|
-
return yield* loadConfigFromFileEffect(options.configPath);
|
|
284
|
-
}
|
|
285
|
-
const defaultPath = path.join(process.cwd(), "instrumentation.yaml");
|
|
286
|
-
const exists = yield* effect.Effect.sync(() => fs.existsSync(defaultPath));
|
|
287
|
-
if (exists) {
|
|
288
|
-
return yield* loadConfigFromFileEffect(defaultPath);
|
|
289
|
-
}
|
|
290
|
-
return getDefaultConfig();
|
|
291
|
-
});
|
|
292
|
-
async function loadConfig(options = {}) {
|
|
293
|
-
return effect.Effect.runPromise(
|
|
294
|
-
loadConfigEffect(options).pipe(
|
|
295
|
-
// Convert typed errors to regular Error with reason message for backward compatibility
|
|
296
|
-
effect.Effect.mapError((error) => {
|
|
297
|
-
const message = error.reason;
|
|
298
|
-
const newError = new Error(message);
|
|
299
|
-
newError.cause = error.cause;
|
|
300
|
-
return newError;
|
|
301
|
-
})
|
|
302
|
-
)
|
|
303
|
-
);
|
|
304
|
-
}
|
|
292
|
+
var ConfigLoaderLive = effect.Layer.effect(ConfigLoader, makeConfigLoader);
|
|
305
293
|
var PatternMatcher = class {
|
|
306
294
|
constructor(config) {
|
|
307
295
|
__publicField2(this, "ignorePatterns", []);
|
|
@@ -718,19 +706,88 @@ var getServiceInfoWithFallback = detectServiceInfo.pipe(
|
|
|
718
706
|
})
|
|
719
707
|
)
|
|
720
708
|
);
|
|
721
|
-
|
|
722
|
-
|
|
709
|
+
var NodeConfigLoaderLive = ConfigLoaderLive.pipe(
|
|
710
|
+
effect.Layer.provide(effect.Layer.mergeAll(platformNode.NodeContext.layer, platform.FetchHttpClient.layer))
|
|
711
|
+
);
|
|
712
|
+
var cachedLoaderPromise = null;
|
|
713
|
+
function getCachedLoader() {
|
|
714
|
+
if (!cachedLoaderPromise) {
|
|
715
|
+
cachedLoaderPromise = effect.Effect.runPromise(
|
|
716
|
+
effect.Effect.gen(function* () {
|
|
717
|
+
return yield* ConfigLoader;
|
|
718
|
+
}).pipe(effect.Effect.provide(NodeConfigLoaderLive))
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
return cachedLoaderPromise;
|
|
723
722
|
}
|
|
724
|
-
|
|
725
|
-
|
|
723
|
+
function _resetConfigLoaderCache() {
|
|
724
|
+
cachedLoaderPromise = null;
|
|
726
725
|
}
|
|
727
|
-
async function
|
|
728
|
-
|
|
726
|
+
async function loadConfig(uri, options) {
|
|
727
|
+
if (options?.cacheTimeout === 0) {
|
|
728
|
+
const program = effect.Effect.gen(function* () {
|
|
729
|
+
const loader2 = yield* ConfigLoader;
|
|
730
|
+
return yield* loader2.loadFromUri(uri);
|
|
731
|
+
});
|
|
732
|
+
return effect.Effect.runPromise(program.pipe(effect.Effect.provide(NodeConfigLoaderLive)));
|
|
733
|
+
}
|
|
734
|
+
const loader = await getCachedLoader();
|
|
735
|
+
return effect.Effect.runPromise(loader.loadFromUri(uri));
|
|
736
|
+
}
|
|
737
|
+
async function loadConfigFromInline(content) {
|
|
738
|
+
const loader = await getCachedLoader();
|
|
739
|
+
return effect.Effect.runPromise(loader.loadFromInline(content));
|
|
740
|
+
}
|
|
741
|
+
function getDefaultConfig() {
|
|
742
|
+
return {
|
|
743
|
+
version: "1.0",
|
|
744
|
+
instrumentation: {
|
|
745
|
+
enabled: true,
|
|
746
|
+
logging: "on",
|
|
747
|
+
description: "Default instrumentation configuration",
|
|
748
|
+
instrument_patterns: [
|
|
749
|
+
{ pattern: "^app\\.", enabled: true, description: "Application operations" },
|
|
750
|
+
{ pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
|
|
751
|
+
{ pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
|
|
752
|
+
],
|
|
753
|
+
ignore_patterns: [
|
|
754
|
+
{ pattern: "^test\\.", description: "Test utilities" },
|
|
755
|
+
{ pattern: "^internal\\.", description: "Internal operations" },
|
|
756
|
+
{ pattern: "^health\\.", description: "Health checks" }
|
|
757
|
+
]
|
|
758
|
+
},
|
|
759
|
+
effect: {
|
|
760
|
+
auto_extract_metadata: true
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
async function loadConfigWithOptions(options = {}) {
|
|
765
|
+
const loadOptions = options.cacheTimeout !== void 0 ? { cacheTimeout: options.cacheTimeout } : void 0;
|
|
766
|
+
if (options.config) {
|
|
767
|
+
return loadConfigFromInline(options.config);
|
|
768
|
+
}
|
|
769
|
+
const envConfigPath = process.env.ATRIM_INSTRUMENTATION_CONFIG;
|
|
770
|
+
if (envConfigPath) {
|
|
771
|
+
return loadConfig(envConfigPath, loadOptions);
|
|
772
|
+
}
|
|
773
|
+
if (options.configUrl) {
|
|
774
|
+
return loadConfig(options.configUrl, loadOptions);
|
|
775
|
+
}
|
|
776
|
+
if (options.configPath) {
|
|
777
|
+
return loadConfig(options.configPath, loadOptions);
|
|
778
|
+
}
|
|
779
|
+
const { existsSync } = await import('fs');
|
|
780
|
+
const { join: join2 } = await import('path');
|
|
781
|
+
const defaultPath = join2(process.cwd(), "instrumentation.yaml");
|
|
782
|
+
if (existsSync(defaultPath)) {
|
|
783
|
+
return loadConfig(defaultPath, loadOptions);
|
|
784
|
+
}
|
|
785
|
+
return getDefaultConfig();
|
|
729
786
|
}
|
|
730
787
|
|
|
731
788
|
// src/core/sdk-initializer.ts
|
|
732
789
|
var sdkInstance = null;
|
|
733
|
-
var
|
|
790
|
+
var initializationDeferred = null;
|
|
734
791
|
function buildHttpInstrumentationConfig(options, config, _otlpEndpoint) {
|
|
735
792
|
const httpConfig = { enabled: true };
|
|
736
793
|
const programmaticPatterns = options.http?.ignoreOutgoingUrls || [];
|
|
@@ -846,27 +903,38 @@ function isTracingAlreadyInitialized() {
|
|
|
846
903
|
return false;
|
|
847
904
|
}
|
|
848
905
|
}
|
|
849
|
-
|
|
906
|
+
var initializeSdkEffect = (options = {}) => effect.Effect.gen(function* () {
|
|
850
907
|
if (sdkInstance) {
|
|
851
908
|
logger.warn("@atrim/instrumentation: SDK already initialized. Returning existing instance.");
|
|
852
909
|
return sdkInstance;
|
|
853
910
|
}
|
|
854
|
-
if (
|
|
911
|
+
if (initializationDeferred) {
|
|
855
912
|
logger.log(
|
|
856
|
-
"@atrim/instrumentation: SDK
|
|
913
|
+
"@atrim/instrumentation: SDK initialization in progress, waiting for completion..."
|
|
857
914
|
);
|
|
858
|
-
return
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
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
|
+
});
|
|
870
938
|
const loggingLevel = config.instrumentation.logging || "on";
|
|
871
939
|
logger.setLevel(loggingLevel);
|
|
872
940
|
const alreadyInitialized = isTracingAlreadyInitialized();
|
|
@@ -882,14 +950,25 @@ async function performInitialization(options) {
|
|
|
882
950
|
logger.log("");
|
|
883
951
|
return null;
|
|
884
952
|
}
|
|
885
|
-
const serviceInfo =
|
|
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
|
+
);
|
|
886
961
|
const serviceName = options.serviceName || serviceInfo.name;
|
|
887
962
|
const serviceVersion = options.serviceVersion || serviceInfo.version;
|
|
888
|
-
const rawExporter = createOtlpExporter(options.otlp);
|
|
889
|
-
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));
|
|
890
965
|
const useSimpleProcessor = process.env.NODE_ENV === "test" || process.env.OTEL_USE_SIMPLE_PROCESSOR === "true";
|
|
891
|
-
const baseProcessor =
|
|
892
|
-
|
|
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
|
+
);
|
|
893
972
|
const instrumentations = [];
|
|
894
973
|
const hasWebFramework = hasWebFrameworkInstalled();
|
|
895
974
|
const enableAutoInstrumentation = shouldEnableAutoInstrumentation(
|
|
@@ -902,15 +981,11 @@ async function performInitialization(options) {
|
|
|
902
981
|
const undiciConfig = buildUndiciInstrumentationConfig(options, config);
|
|
903
982
|
instrumentations.push(
|
|
904
983
|
...autoInstrumentationsNode.getNodeAutoInstrumentations({
|
|
905
|
-
// Enable HTTP instrumentation with filtering (for http/https modules)
|
|
906
984
|
"@opentelemetry/instrumentation-http": httpConfig,
|
|
907
|
-
// Enable undici instrumentation with filtering (for fetch API)
|
|
908
985
|
"@opentelemetry/instrumentation-undici": undiciConfig,
|
|
909
|
-
// Enable web framework instrumentations
|
|
910
986
|
"@opentelemetry/instrumentation-express": { enabled: true },
|
|
911
987
|
"@opentelemetry/instrumentation-fastify": { enabled: true },
|
|
912
988
|
"@opentelemetry/instrumentation-koa": { enabled: true },
|
|
913
|
-
// Disable noisy instrumentations by default
|
|
914
989
|
"@opentelemetry/instrumentation-fs": { enabled: false },
|
|
915
990
|
"@opentelemetry/instrumentation-dns": { enabled: false }
|
|
916
991
|
})
|
|
@@ -938,18 +1013,20 @@ async function performInitialization(options) {
|
|
|
938
1013
|
serviceName,
|
|
939
1014
|
...serviceVersion && { serviceVersion },
|
|
940
1015
|
instrumentations,
|
|
941
|
-
// Allow advanced overrides
|
|
942
1016
|
...options.sdk
|
|
943
1017
|
};
|
|
944
|
-
const sdk =
|
|
945
|
-
|
|
1018
|
+
const sdk = yield* effect.Effect.sync(() => {
|
|
1019
|
+
const s = new sdkNode.NodeSDK(sdkConfig);
|
|
1020
|
+
s.start();
|
|
1021
|
+
return s;
|
|
1022
|
+
});
|
|
946
1023
|
sdkInstance = sdk;
|
|
947
1024
|
if (!options.disableAutoShutdown) {
|
|
948
|
-
registerShutdownHandlers(sdk);
|
|
1025
|
+
yield* effect.Effect.sync(() => registerShutdownHandlers(sdk));
|
|
949
1026
|
}
|
|
950
1027
|
logInitialization(config, serviceName, serviceVersion, options, enableAutoInstrumentation);
|
|
951
1028
|
return sdk;
|
|
952
|
-
}
|
|
1029
|
+
});
|
|
953
1030
|
function getSdkInstance() {
|
|
954
1031
|
return sdkInstance;
|
|
955
1032
|
}
|
|
@@ -962,7 +1039,7 @@ async function shutdownSdk() {
|
|
|
962
1039
|
}
|
|
963
1040
|
function resetSdk() {
|
|
964
1041
|
sdkInstance = null;
|
|
965
|
-
|
|
1042
|
+
initializationDeferred = null;
|
|
966
1043
|
}
|
|
967
1044
|
function registerShutdownHandlers(sdk) {
|
|
968
1045
|
const shutdown = async (signal) => {
|
|
@@ -1019,33 +1096,11 @@ function logInitialization(config, serviceName, serviceVersion, options, autoIns
|
|
|
1019
1096
|
}
|
|
1020
1097
|
|
|
1021
1098
|
// src/api.ts
|
|
1022
|
-
|
|
1023
|
-
const sdk =
|
|
1024
|
-
if (sdk) {
|
|
1025
|
-
const config = await loadConfig(options);
|
|
1026
|
-
initializePatternMatcher(config);
|
|
1027
|
-
}
|
|
1028
|
-
return sdk;
|
|
1029
|
-
}
|
|
1030
|
-
async function initializePatternMatchingOnly(options = {}) {
|
|
1031
|
-
const config = await loadConfig(options);
|
|
1032
|
-
initializePatternMatcher(config);
|
|
1033
|
-
logger.log("@atrim/instrumentation: Pattern matching initialized (legacy mode)");
|
|
1034
|
-
logger.log(
|
|
1035
|
-
" Note: NodeSDK is not initialized. Use initializeInstrumentation() for complete setup."
|
|
1036
|
-
);
|
|
1037
|
-
}
|
|
1038
|
-
var initializeInstrumentationEffect = (options = {}) => effect.Effect.gen(function* () {
|
|
1039
|
-
const sdk = yield* effect.Effect.tryPromise({
|
|
1040
|
-
try: () => initializeSdk(options),
|
|
1041
|
-
catch: (error) => new InitializationError2({
|
|
1042
|
-
reason: "SDK initialization failed",
|
|
1043
|
-
cause: error
|
|
1044
|
-
})
|
|
1045
|
-
});
|
|
1099
|
+
var initializeInstrumentation = (options = {}) => effect.Effect.gen(function* () {
|
|
1100
|
+
const sdk = yield* initializeSdkEffect(options);
|
|
1046
1101
|
if (sdk) {
|
|
1047
1102
|
yield* effect.Effect.tryPromise({
|
|
1048
|
-
try: () =>
|
|
1103
|
+
try: () => loadConfigWithOptions(options),
|
|
1049
1104
|
catch: (error) => new ConfigError2({
|
|
1050
1105
|
reason: "Failed to load config for pattern matcher",
|
|
1051
1106
|
cause: error
|
|
@@ -1060,9 +1115,9 @@ var initializeInstrumentationEffect = (options = {}) => effect.Effect.gen(functi
|
|
|
1060
1115
|
}
|
|
1061
1116
|
return sdk;
|
|
1062
1117
|
});
|
|
1063
|
-
var
|
|
1118
|
+
var initializePatternMatchingOnly = (options = {}) => effect.Effect.gen(function* () {
|
|
1064
1119
|
const config = yield* effect.Effect.tryPromise({
|
|
1065
|
-
try: () =>
|
|
1120
|
+
try: () => loadConfigWithOptions(options),
|
|
1066
1121
|
catch: (error) => new ConfigError2({
|
|
1067
1122
|
reason: "Failed to load configuration",
|
|
1068
1123
|
cause: error
|
|
@@ -1070,7 +1125,7 @@ var initializePatternMatchingOnlyEffect = (options = {}) => effect.Effect.gen(fu
|
|
|
1070
1125
|
});
|
|
1071
1126
|
yield* effect.Effect.sync(() => {
|
|
1072
1127
|
initializePatternMatcher(config);
|
|
1073
|
-
logger.log("@atrim/instrumentation: Pattern matching initialized (
|
|
1128
|
+
logger.log("@atrim/instrumentation: Pattern matching initialized (pattern-only mode)");
|
|
1074
1129
|
logger.log(
|
|
1075
1130
|
" Note: NodeSDK is not initialized. Use initializeInstrumentation() for complete setup."
|
|
1076
1131
|
);
|
|
@@ -1184,22 +1239,20 @@ exports.ShutdownError = ShutdownError2;
|
|
|
1184
1239
|
exports.annotateCacheOperation = annotateCacheOperation;
|
|
1185
1240
|
exports.annotateDbQuery = annotateDbQuery;
|
|
1186
1241
|
exports.annotateHttpRequest = annotateHttpRequest;
|
|
1242
|
+
exports.clearConfigCache = _resetConfigLoaderCache;
|
|
1187
1243
|
exports.createOtlpExporter = createOtlpExporter;
|
|
1188
|
-
exports.detectServiceInfo =
|
|
1189
|
-
exports.detectServiceInfoEffect = detectServiceInfo;
|
|
1244
|
+
exports.detectServiceInfo = detectServiceInfo;
|
|
1190
1245
|
exports.getOtlpEndpoint = getOtlpEndpoint;
|
|
1191
1246
|
exports.getPatternMatcher = getPatternMatcher;
|
|
1192
1247
|
exports.getSdkInstance = getSdkInstance;
|
|
1193
1248
|
exports.getServiceInfoWithFallback = getServiceInfoWithFallback;
|
|
1194
|
-
exports.getServiceName =
|
|
1195
|
-
exports.
|
|
1196
|
-
exports.getServiceVersion = getServiceVersionAsync;
|
|
1197
|
-
exports.getServiceVersionEffect = getServiceVersion;
|
|
1249
|
+
exports.getServiceName = getServiceName;
|
|
1250
|
+
exports.getServiceVersion = getServiceVersion;
|
|
1198
1251
|
exports.initializeInstrumentation = initializeInstrumentation;
|
|
1199
|
-
exports.initializeInstrumentationEffect = initializeInstrumentationEffect;
|
|
1200
1252
|
exports.initializePatternMatchingOnly = initializePatternMatchingOnly;
|
|
1201
|
-
exports.initializePatternMatchingOnlyEffect = initializePatternMatchingOnlyEffect;
|
|
1202
1253
|
exports.loadConfig = loadConfig;
|
|
1254
|
+
exports.loadConfigFromInline = loadConfigFromInline;
|
|
1255
|
+
exports.loadConfigWithOptions = loadConfigWithOptions;
|
|
1203
1256
|
exports.markSpanError = markSpanError;
|
|
1204
1257
|
exports.markSpanSuccess = markSpanSuccess;
|
|
1205
1258
|
exports.recordException = recordException;
|