@atrim/instrument-node 0.4.0 → 0.5.0-3a3dd2c-20251124202015
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 +66 -0
- package/package.json +13 -7
- package/target/dist/index.cjs +310 -197
- 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 +288 -199
- package/target/dist/index.js.map +1 -1
- package/target/dist/integrations/effect/auto/index.cjs +276 -0
- package/target/dist/integrations/effect/auto/index.cjs.map +1 -0
- package/target/dist/integrations/effect/auto/index.d.cts +1513 -0
- package/target/dist/integrations/effect/auto/index.d.ts +1513 -0
- package/target/dist/integrations/effect/auto/index.js +264 -0
- package/target/dist/integrations/effect/auto/index.js.map +1 -0
- package/target/dist/integrations/effect/index.cjs +424 -206
- package/target/dist/integrations/effect/index.cjs.map +1 -1
- package/target/dist/integrations/effect/index.d.cts +204 -13
- package/target/dist/integrations/effect/index.d.ts +204 -13
- package/target/dist/integrations/effect/index.js +421 -208
- package/target/dist/integrations/effect/index.js.map +1 -1
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import { Data,
|
|
1
|
+
import { Data, Context, Effect, Layer, FiberSet as FiberSet$1, Fiber, Option, FiberId, Tracer } from 'effect';
|
|
2
2
|
import * as Otlp from '@effect/opentelemetry/Otlp';
|
|
3
3
|
import { FetchHttpClient } from '@effect/platform';
|
|
4
4
|
import { TraceFlags, trace, context } from '@opentelemetry/api';
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
5
|
+
import { FileSystem } from '@effect/platform/FileSystem';
|
|
6
|
+
import * as HttpClient from '@effect/platform/HttpClient';
|
|
7
|
+
import * as HttpClientRequest from '@effect/platform/HttpClientRequest';
|
|
7
8
|
import { parse } from 'yaml';
|
|
8
9
|
import { z } from 'zod';
|
|
10
|
+
import { NodeContext } from '@effect/platform-node';
|
|
9
11
|
|
|
10
12
|
// src/integrations/effect/effect-tracer.ts
|
|
13
|
+
|
|
14
|
+
// ../../node_modules/.pnpm/@opentelemetry+semantic-conventions@1.38.0/node_modules/@opentelemetry/semantic-conventions/build/esm/stable_attributes.js
|
|
15
|
+
var ATTR_TELEMETRY_SDK_LANGUAGE = "telemetry.sdk.language";
|
|
16
|
+
var TELEMETRY_SDK_LANGUAGE_VALUE_NODEJS = "nodejs";
|
|
17
|
+
var ATTR_TELEMETRY_SDK_NAME = "telemetry.sdk.name";
|
|
11
18
|
var __defProp = Object.defineProperty;
|
|
12
19
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
13
20
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -38,13 +45,69 @@ var AutoIsolationConfigSchema = z.object({
|
|
|
38
45
|
add_metadata: z.boolean().default(true)
|
|
39
46
|
}).default({})
|
|
40
47
|
});
|
|
48
|
+
var SpanNamingRuleSchema = z.object({
|
|
49
|
+
// Match conditions (all specified conditions must match)
|
|
50
|
+
match: z.object({
|
|
51
|
+
// Match by file path pattern (regex)
|
|
52
|
+
file: z.string().optional(),
|
|
53
|
+
// Match by function name pattern (regex)
|
|
54
|
+
function: z.string().optional(),
|
|
55
|
+
// Match by operator type (gen, all, forEach, etc.)
|
|
56
|
+
operator: z.string().optional(),
|
|
57
|
+
// Match by call stack pattern (regex)
|
|
58
|
+
stack: z.string().optional(),
|
|
59
|
+
// Match by fiber annotation key
|
|
60
|
+
annotation: z.string().optional()
|
|
61
|
+
}),
|
|
62
|
+
// Name template with substitution variables
|
|
63
|
+
// Available: {operator}, {function}, {module}, {file}, {line}, {class}, {match:N}
|
|
64
|
+
name: z.string()
|
|
65
|
+
});
|
|
66
|
+
var AutoTracingConfigSchema = z.object({
|
|
67
|
+
// Global enable/disable for auto-tracing
|
|
68
|
+
enabled: z.boolean().default(true),
|
|
69
|
+
// Span naming configuration
|
|
70
|
+
span_naming: z.object({
|
|
71
|
+
// Default name template when no rules match
|
|
72
|
+
default: z.string().default("effect.{operator}"),
|
|
73
|
+
// Custom naming rules (applied in order, first match wins)
|
|
74
|
+
rules: z.array(SpanNamingRuleSchema).default([])
|
|
75
|
+
}).default({}),
|
|
76
|
+
// Pattern filtering
|
|
77
|
+
filter_patterns: z.object({
|
|
78
|
+
// Only trace spans matching these patterns
|
|
79
|
+
include: z.array(z.string()).default([]),
|
|
80
|
+
// Exclude spans matching these patterns (takes precedence)
|
|
81
|
+
exclude: z.array(z.string()).default([])
|
|
82
|
+
}).default({}),
|
|
83
|
+
// Sampling configuration
|
|
84
|
+
sampling: z.object({
|
|
85
|
+
// Sampling rate (0.0 to 1.0)
|
|
86
|
+
rate: z.number().min(0).max(1).default(1),
|
|
87
|
+
// Only trace effects with duration > this value
|
|
88
|
+
min_duration: z.string().default("0 millis")
|
|
89
|
+
}).default({})
|
|
90
|
+
});
|
|
41
91
|
var HttpFilteringConfigSchema = z.object({
|
|
42
92
|
// Patterns to ignore for outgoing HTTP requests (string patterns only in YAML)
|
|
43
93
|
ignore_outgoing_urls: z.array(z.string()).optional(),
|
|
44
94
|
// Patterns to ignore for incoming HTTP requests (string patterns only in YAML)
|
|
45
95
|
ignore_incoming_paths: z.array(z.string()).optional(),
|
|
46
96
|
// Require parent span for outgoing requests (prevents root spans for HTTP calls)
|
|
47
|
-
require_parent_for_outgoing_spans: z.boolean().optional()
|
|
97
|
+
require_parent_for_outgoing_spans: z.boolean().optional(),
|
|
98
|
+
// Trace context propagation configuration
|
|
99
|
+
// Controls which cross-origin requests receive W3C Trace Context headers (traceparent, tracestate)
|
|
100
|
+
propagate_trace_context: z.object({
|
|
101
|
+
// Strategy for trace propagation
|
|
102
|
+
// - "all": Propagate to all cross-origin requests (may cause CORS errors)
|
|
103
|
+
// - "none": Never propagate trace headers
|
|
104
|
+
// - "same-origin": Only propagate to same-origin requests (default, safe)
|
|
105
|
+
// - "patterns": Propagate based on include_urls patterns
|
|
106
|
+
strategy: z.enum(["all", "none", "same-origin", "patterns"]).default("same-origin"),
|
|
107
|
+
// URL patterns to include when strategy is "patterns"
|
|
108
|
+
// Supports regex patterns (e.g., "^https://api\\.myapp\\.com")
|
|
109
|
+
include_urls: z.array(z.string()).optional()
|
|
110
|
+
}).optional()
|
|
48
111
|
});
|
|
49
112
|
var InstrumentationConfigSchema = z.object({
|
|
50
113
|
version: z.string(),
|
|
@@ -57,238 +120,189 @@ var InstrumentationConfigSchema = z.object({
|
|
|
57
120
|
}),
|
|
58
121
|
effect: z.object({
|
|
59
122
|
auto_extract_metadata: z.boolean(),
|
|
60
|
-
auto_isolation: AutoIsolationConfigSchema.optional()
|
|
123
|
+
auto_isolation: AutoIsolationConfigSchema.optional(),
|
|
124
|
+
auto_tracing: AutoTracingConfigSchema.optional()
|
|
61
125
|
}).optional(),
|
|
62
126
|
http: HttpFilteringConfigSchema.optional()
|
|
63
127
|
});
|
|
64
128
|
(class extends Data.TaggedError("ConfigError") {
|
|
129
|
+
get message() {
|
|
130
|
+
return this.reason;
|
|
131
|
+
}
|
|
65
132
|
});
|
|
66
133
|
var ConfigUrlError = class extends Data.TaggedError("ConfigUrlError") {
|
|
134
|
+
get message() {
|
|
135
|
+
return this.reason;
|
|
136
|
+
}
|
|
67
137
|
};
|
|
68
138
|
var ConfigValidationError = class extends Data.TaggedError("ConfigValidationError") {
|
|
139
|
+
get message() {
|
|
140
|
+
return this.reason;
|
|
141
|
+
}
|
|
69
142
|
};
|
|
70
143
|
var ConfigFileError = class extends Data.TaggedError("ConfigFileError") {
|
|
144
|
+
get message() {
|
|
145
|
+
return this.reason;
|
|
146
|
+
}
|
|
71
147
|
};
|
|
72
148
|
(class extends Data.TaggedError("ServiceDetectionError") {
|
|
149
|
+
get message() {
|
|
150
|
+
return this.reason;
|
|
151
|
+
}
|
|
73
152
|
});
|
|
74
153
|
(class extends Data.TaggedError("InitializationError") {
|
|
154
|
+
get message() {
|
|
155
|
+
return this.reason;
|
|
156
|
+
}
|
|
75
157
|
});
|
|
76
158
|
(class extends Data.TaggedError("ExportError") {
|
|
159
|
+
get message() {
|
|
160
|
+
return this.reason;
|
|
161
|
+
}
|
|
77
162
|
});
|
|
78
163
|
(class extends Data.TaggedError("ShutdownError") {
|
|
164
|
+
get message() {
|
|
165
|
+
return this.reason;
|
|
166
|
+
}
|
|
79
167
|
});
|
|
80
168
|
var SECURITY_DEFAULTS = {
|
|
81
169
|
maxConfigSize: 1e6,
|
|
82
170
|
// 1MB
|
|
83
|
-
requestTimeout: 5e3
|
|
84
|
-
|
|
85
|
-
// Only HTTPS for remote configs
|
|
86
|
-
cacheTimeout: 3e5
|
|
87
|
-
// 5 minutes
|
|
171
|
+
requestTimeout: 5e3
|
|
172
|
+
// 5 seconds
|
|
88
173
|
};
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
{ pattern: "^internal\\.", description: "Internal operations" },
|
|
104
|
-
{ pattern: "^health\\.", description: "Health checks" }
|
|
105
|
-
]
|
|
106
|
-
},
|
|
107
|
-
effect: {
|
|
108
|
-
auto_extract_metadata: true
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
var validateConfigEffect = (rawConfig) => Effect.try({
|
|
113
|
-
try: () => InstrumentationConfigSchema.parse(rawConfig),
|
|
114
|
-
catch: (error) => new ConfigValidationError({
|
|
115
|
-
reason: "Invalid configuration schema",
|
|
116
|
-
cause: error
|
|
117
|
-
})
|
|
118
|
-
});
|
|
119
|
-
var loadConfigFromFileEffect = (filePath) => Effect.gen(function* () {
|
|
120
|
-
const fileContents = yield* Effect.try({
|
|
121
|
-
try: () => readFileSync(filePath, "utf8"),
|
|
122
|
-
catch: (error) => new ConfigFileError({
|
|
123
|
-
reason: `Failed to read config file at ${filePath}`,
|
|
174
|
+
var ConfigLoader = class extends Context.Tag("ConfigLoader")() {
|
|
175
|
+
};
|
|
176
|
+
var parseYamlContent = (content, uri) => Effect.gen(function* () {
|
|
177
|
+
const parsed = yield* Effect.try({
|
|
178
|
+
try: () => parse(content),
|
|
179
|
+
catch: (error) => new ConfigValidationError({
|
|
180
|
+
reason: uri ? `Failed to parse YAML from ${uri}` : "Failed to parse YAML",
|
|
181
|
+
cause: error
|
|
182
|
+
})
|
|
183
|
+
});
|
|
184
|
+
return yield* Effect.try({
|
|
185
|
+
try: () => InstrumentationConfigSchema.parse(parsed),
|
|
186
|
+
catch: (error) => new ConfigValidationError({
|
|
187
|
+
reason: uri ? `Invalid configuration schema from ${uri}` : "Invalid configuration schema",
|
|
124
188
|
cause: error
|
|
125
189
|
})
|
|
126
190
|
});
|
|
127
|
-
|
|
191
|
+
});
|
|
192
|
+
var loadFromFileWithFs = (fs, path, uri) => Effect.gen(function* () {
|
|
193
|
+
const content = yield* fs.readFileString(path).pipe(
|
|
194
|
+
Effect.mapError(
|
|
195
|
+
(error) => new ConfigFileError({
|
|
196
|
+
reason: `Failed to read config file at ${uri}`,
|
|
197
|
+
cause: error
|
|
198
|
+
})
|
|
199
|
+
)
|
|
200
|
+
);
|
|
201
|
+
if (content.length > SECURITY_DEFAULTS.maxConfigSize) {
|
|
128
202
|
return yield* Effect.fail(
|
|
129
203
|
new ConfigFileError({
|
|
130
204
|
reason: `Config file exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
|
|
131
205
|
})
|
|
132
206
|
);
|
|
133
207
|
}
|
|
134
|
-
|
|
135
|
-
try {
|
|
136
|
-
rawConfig = parse(fileContents);
|
|
137
|
-
} catch (error) {
|
|
138
|
-
return yield* Effect.fail(
|
|
139
|
-
new ConfigValidationError({
|
|
140
|
-
reason: "Invalid YAML syntax",
|
|
141
|
-
cause: error
|
|
142
|
-
})
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
return yield* validateConfigEffect(rawConfig);
|
|
208
|
+
return yield* parseYamlContent(content, uri);
|
|
146
209
|
});
|
|
147
|
-
var
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
210
|
+
var loadFromHttpWithClient = (client, url) => Effect.scoped(
|
|
211
|
+
Effect.gen(function* () {
|
|
212
|
+
if (url.startsWith("http://")) {
|
|
213
|
+
return yield* Effect.fail(
|
|
214
|
+
new ConfigUrlError({
|
|
215
|
+
reason: "Insecure protocol: only HTTPS URLs are allowed"
|
|
216
|
+
})
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
const request = HttpClientRequest.get(url).pipe(
|
|
220
|
+
HttpClientRequest.setHeaders({
|
|
221
|
+
Accept: "application/yaml, text/yaml, application/x-yaml"
|
|
156
222
|
})
|
|
157
223
|
);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
224
|
+
const response = yield* client.execute(request).pipe(
|
|
225
|
+
Effect.timeout(`${SECURITY_DEFAULTS.requestTimeout} millis`),
|
|
226
|
+
Effect.mapError((error) => {
|
|
227
|
+
if (error._tag === "TimeoutException") {
|
|
228
|
+
return new ConfigUrlError({
|
|
229
|
+
reason: `Config fetch timeout after ${SECURITY_DEFAULTS.requestTimeout}ms from ${url}`
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
return new ConfigUrlError({
|
|
233
|
+
reason: `Failed to load config from URL: ${url}`,
|
|
234
|
+
cause: error
|
|
235
|
+
});
|
|
163
236
|
})
|
|
164
237
|
);
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
238
|
+
if (response.status >= 400) {
|
|
239
|
+
return yield* Effect.fail(
|
|
240
|
+
new ConfigUrlError({
|
|
241
|
+
reason: `HTTP ${response.status} from ${url}`
|
|
242
|
+
})
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
const text = yield* response.text.pipe(
|
|
246
|
+
Effect.mapError(
|
|
247
|
+
(error) => new ConfigUrlError({
|
|
248
|
+
reason: `Failed to read response body from ${url}`,
|
|
249
|
+
cause: error
|
|
250
|
+
})
|
|
251
|
+
)
|
|
252
|
+
);
|
|
253
|
+
if (text.length > SECURITY_DEFAULTS.maxConfigSize) {
|
|
254
|
+
return yield* Effect.fail(
|
|
255
|
+
new ConfigUrlError({
|
|
256
|
+
reason: `Config exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
|
|
257
|
+
})
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
return yield* parseYamlContent(text, url);
|
|
261
|
+
})
|
|
262
|
+
);
|
|
263
|
+
var makeConfigLoader = Effect.gen(function* () {
|
|
264
|
+
const fs = yield* Effect.serviceOption(FileSystem);
|
|
265
|
+
const http = yield* HttpClient.HttpClient;
|
|
266
|
+
const loadFromUriUncached = (uri) => Effect.gen(function* () {
|
|
267
|
+
if (uri.startsWith("file://")) {
|
|
268
|
+
const path = uri.slice(7);
|
|
269
|
+
if (fs._tag === "None") {
|
|
270
|
+
return yield* Effect.fail(
|
|
271
|
+
new ConfigFileError({
|
|
272
|
+
reason: "FileSystem not available (browser environment?)",
|
|
273
|
+
cause: { uri }
|
|
188
274
|
})
|
|
189
275
|
);
|
|
190
276
|
}
|
|
191
|
-
return
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
277
|
+
return yield* loadFromFileWithFs(fs.value, path, uri);
|
|
278
|
+
}
|
|
279
|
+
if (uri.startsWith("http://") || uri.startsWith("https://")) {
|
|
280
|
+
return yield* loadFromHttpWithClient(http, uri);
|
|
281
|
+
}
|
|
282
|
+
if (fs._tag === "Some") {
|
|
283
|
+
return yield* loadFromFileWithFs(fs.value, uri, uri);
|
|
284
|
+
} else {
|
|
285
|
+
return yield* loadFromHttpWithClient(http, uri);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
const loadFromUriCached = yield* Effect.cachedFunction(loadFromUriUncached);
|
|
289
|
+
return ConfigLoader.of({
|
|
290
|
+
loadFromUri: loadFromUriCached,
|
|
291
|
+
loadFromInline: (content) => Effect.gen(function* () {
|
|
292
|
+
if (typeof content === "string") {
|
|
293
|
+
return yield* parseYamlContent(content);
|
|
294
|
+
}
|
|
295
|
+
return yield* Effect.try({
|
|
296
|
+
try: () => InstrumentationConfigSchema.parse(content),
|
|
297
|
+
catch: (error) => new ConfigValidationError({
|
|
298
|
+
reason: "Invalid configuration schema",
|
|
299
|
+
cause: error
|
|
300
|
+
})
|
|
301
|
+
});
|
|
214
302
|
})
|
|
215
303
|
});
|
|
216
|
-
if (text.length > SECURITY_DEFAULTS.maxConfigSize) {
|
|
217
|
-
return yield* Effect.fail(
|
|
218
|
-
new ConfigUrlError({
|
|
219
|
-
reason: `Config exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
|
|
220
|
-
})
|
|
221
|
-
);
|
|
222
|
-
}
|
|
223
|
-
let rawConfig;
|
|
224
|
-
try {
|
|
225
|
-
rawConfig = parse(text);
|
|
226
|
-
} catch (error) {
|
|
227
|
-
return yield* Effect.fail(
|
|
228
|
-
new ConfigValidationError({
|
|
229
|
-
reason: "Invalid YAML syntax",
|
|
230
|
-
cause: error
|
|
231
|
-
})
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
return yield* validateConfigEffect(rawConfig);
|
|
235
|
-
});
|
|
236
|
-
var makeConfigCache = () => Cache.make({
|
|
237
|
-
capacity: 100,
|
|
238
|
-
timeToLive: Duration.minutes(5),
|
|
239
|
-
lookup: (url) => fetchAndParseConfig(url)
|
|
240
|
-
});
|
|
241
|
-
var cacheInstance = null;
|
|
242
|
-
var getCache = Effect.gen(function* () {
|
|
243
|
-
if (!cacheInstance) {
|
|
244
|
-
cacheInstance = yield* makeConfigCache();
|
|
245
|
-
}
|
|
246
|
-
return cacheInstance;
|
|
247
304
|
});
|
|
248
|
-
var
|
|
249
|
-
if (cacheTimeout === 0) {
|
|
250
|
-
return yield* fetchAndParseConfig(url);
|
|
251
|
-
}
|
|
252
|
-
const cache = yield* getCache;
|
|
253
|
-
return yield* cache.get(url);
|
|
254
|
-
});
|
|
255
|
-
var loadConfigEffect = (options = {}) => Effect.gen(function* () {
|
|
256
|
-
if (options.config) {
|
|
257
|
-
return yield* validateConfigEffect(options.config);
|
|
258
|
-
}
|
|
259
|
-
const envConfigPath = process.env.ATRIM_INSTRUMENTATION_CONFIG;
|
|
260
|
-
if (envConfigPath) {
|
|
261
|
-
if (envConfigPath.startsWith("http://") || envConfigPath.startsWith("https://")) {
|
|
262
|
-
return yield* loadConfigFromUrlEffect(envConfigPath, options.cacheTimeout);
|
|
263
|
-
}
|
|
264
|
-
return yield* loadConfigFromFileEffect(envConfigPath);
|
|
265
|
-
}
|
|
266
|
-
if (options.configUrl) {
|
|
267
|
-
return yield* loadConfigFromUrlEffect(options.configUrl, options.cacheTimeout);
|
|
268
|
-
}
|
|
269
|
-
if (options.configPath) {
|
|
270
|
-
return yield* loadConfigFromFileEffect(options.configPath);
|
|
271
|
-
}
|
|
272
|
-
const defaultPath = join(process.cwd(), "instrumentation.yaml");
|
|
273
|
-
const exists = yield* Effect.sync(() => existsSync(defaultPath));
|
|
274
|
-
if (exists) {
|
|
275
|
-
return yield* loadConfigFromFileEffect(defaultPath);
|
|
276
|
-
}
|
|
277
|
-
return getDefaultConfig();
|
|
278
|
-
});
|
|
279
|
-
async function loadConfig(options = {}) {
|
|
280
|
-
return Effect.runPromise(
|
|
281
|
-
loadConfigEffect(options).pipe(
|
|
282
|
-
// Convert typed errors to regular Error with reason message for backward compatibility
|
|
283
|
-
Effect.mapError((error) => {
|
|
284
|
-
const message = error.reason;
|
|
285
|
-
const newError = new Error(message);
|
|
286
|
-
newError.cause = error.cause;
|
|
287
|
-
return newError;
|
|
288
|
-
})
|
|
289
|
-
)
|
|
290
|
-
);
|
|
291
|
-
}
|
|
305
|
+
var ConfigLoaderLive = Layer.effect(ConfigLoader, makeConfigLoader);
|
|
292
306
|
var PatternMatcher = class {
|
|
293
307
|
constructor(config) {
|
|
294
308
|
__publicField(this, "ignorePatterns", []);
|
|
@@ -436,13 +450,89 @@ var Logger = class {
|
|
|
436
450
|
}
|
|
437
451
|
};
|
|
438
452
|
var logger = new Logger();
|
|
453
|
+
var NodeConfigLoaderLive = ConfigLoaderLive.pipe(
|
|
454
|
+
Layer.provide(Layer.mergeAll(NodeContext.layer, FetchHttpClient.layer))
|
|
455
|
+
);
|
|
456
|
+
var cachedLoaderPromise = null;
|
|
457
|
+
function getCachedLoader() {
|
|
458
|
+
if (!cachedLoaderPromise) {
|
|
459
|
+
cachedLoaderPromise = Effect.runPromise(
|
|
460
|
+
Effect.gen(function* () {
|
|
461
|
+
return yield* ConfigLoader;
|
|
462
|
+
}).pipe(Effect.provide(NodeConfigLoaderLive))
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
return cachedLoaderPromise;
|
|
466
|
+
}
|
|
467
|
+
async function loadConfig(uri, options) {
|
|
468
|
+
if (options?.cacheTimeout === 0) {
|
|
469
|
+
const program = Effect.gen(function* () {
|
|
470
|
+
const loader2 = yield* ConfigLoader;
|
|
471
|
+
return yield* loader2.loadFromUri(uri);
|
|
472
|
+
});
|
|
473
|
+
return Effect.runPromise(program.pipe(Effect.provide(NodeConfigLoaderLive)));
|
|
474
|
+
}
|
|
475
|
+
const loader = await getCachedLoader();
|
|
476
|
+
return Effect.runPromise(loader.loadFromUri(uri));
|
|
477
|
+
}
|
|
478
|
+
async function loadConfigFromInline(content) {
|
|
479
|
+
const loader = await getCachedLoader();
|
|
480
|
+
return Effect.runPromise(loader.loadFromInline(content));
|
|
481
|
+
}
|
|
482
|
+
function getDefaultConfig() {
|
|
483
|
+
return {
|
|
484
|
+
version: "1.0",
|
|
485
|
+
instrumentation: {
|
|
486
|
+
enabled: true,
|
|
487
|
+
logging: "on",
|
|
488
|
+
description: "Default instrumentation configuration",
|
|
489
|
+
instrument_patterns: [
|
|
490
|
+
{ pattern: "^app\\.", enabled: true, description: "Application operations" },
|
|
491
|
+
{ pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
|
|
492
|
+
{ pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
|
|
493
|
+
],
|
|
494
|
+
ignore_patterns: [
|
|
495
|
+
{ pattern: "^test\\.", description: "Test utilities" },
|
|
496
|
+
{ pattern: "^internal\\.", description: "Internal operations" },
|
|
497
|
+
{ pattern: "^health\\.", description: "Health checks" }
|
|
498
|
+
]
|
|
499
|
+
},
|
|
500
|
+
effect: {
|
|
501
|
+
auto_extract_metadata: true
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
async function loadConfigWithOptions(options = {}) {
|
|
506
|
+
const loadOptions = options.cacheTimeout !== void 0 ? { cacheTimeout: options.cacheTimeout } : void 0;
|
|
507
|
+
if (options.config) {
|
|
508
|
+
return loadConfigFromInline(options.config);
|
|
509
|
+
}
|
|
510
|
+
const envConfigPath = process.env.ATRIM_INSTRUMENTATION_CONFIG;
|
|
511
|
+
if (envConfigPath) {
|
|
512
|
+
return loadConfig(envConfigPath, loadOptions);
|
|
513
|
+
}
|
|
514
|
+
if (options.configUrl) {
|
|
515
|
+
return loadConfig(options.configUrl, loadOptions);
|
|
516
|
+
}
|
|
517
|
+
if (options.configPath) {
|
|
518
|
+
return loadConfig(options.configPath, loadOptions);
|
|
519
|
+
}
|
|
520
|
+
const { existsSync } = await import('fs');
|
|
521
|
+
const { join } = await import('path');
|
|
522
|
+
const defaultPath = join(process.cwd(), "instrumentation.yaml");
|
|
523
|
+
if (existsSync(defaultPath)) {
|
|
524
|
+
return loadConfig(defaultPath, loadOptions);
|
|
525
|
+
}
|
|
526
|
+
return getDefaultConfig();
|
|
527
|
+
}
|
|
439
528
|
|
|
440
529
|
// src/integrations/effect/effect-tracer.ts
|
|
530
|
+
var SDK_NAME = "@effect/opentelemetry-otlp";
|
|
441
531
|
function createEffectInstrumentation(options = {}) {
|
|
442
532
|
return Layer.unwrapEffect(
|
|
443
533
|
Effect.gen(function* () {
|
|
444
534
|
const config = yield* Effect.tryPromise({
|
|
445
|
-
try: () =>
|
|
535
|
+
try: () => loadConfigWithOptions(options),
|
|
446
536
|
catch: (error) => ({
|
|
447
537
|
_tag: "ConfigError",
|
|
448
538
|
message: error instanceof Error ? error.message : String(error)
|
|
@@ -471,7 +561,9 @@ function createEffectInstrumentation(options = {}) {
|
|
|
471
561
|
attributes: {
|
|
472
562
|
"platform.component": "effect",
|
|
473
563
|
"effect.auto_metadata": autoExtractMetadata,
|
|
474
|
-
"effect.context_propagation": continueExistingTraces
|
|
564
|
+
"effect.context_propagation": continueExistingTraces,
|
|
565
|
+
[ATTR_TELEMETRY_SDK_LANGUAGE]: TELEMETRY_SDK_LANGUAGE_VALUE_NODEJS,
|
|
566
|
+
[ATTR_TELEMETRY_SDK_NAME]: SDK_NAME
|
|
475
567
|
}
|
|
476
568
|
},
|
|
477
569
|
// Bridge Effect context to OpenTelemetry global context
|
|
@@ -510,7 +602,9 @@ var EffectInstrumentationLive = Effect.sync(() => {
|
|
|
510
602
|
serviceName,
|
|
511
603
|
serviceVersion,
|
|
512
604
|
attributes: {
|
|
513
|
-
"platform.component": "effect"
|
|
605
|
+
"platform.component": "effect",
|
|
606
|
+
[ATTR_TELEMETRY_SDK_LANGUAGE]: TELEMETRY_SDK_LANGUAGE_VALUE_NODEJS,
|
|
607
|
+
[ATTR_TELEMETRY_SDK_NAME]: SDK_NAME
|
|
514
608
|
}
|
|
515
609
|
},
|
|
516
610
|
// CRITICAL: Bridge Effect context to OpenTelemetry global context
|
|
@@ -529,25 +623,144 @@ var EffectInstrumentationLive = Effect.sync(() => {
|
|
|
529
623
|
}
|
|
530
624
|
}).pipe(Layer.provide(FetchHttpClient.layer));
|
|
531
625
|
}).pipe(Layer.unwrapEffect);
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
626
|
+
function annotateUser(userId, email, username) {
|
|
627
|
+
const attributes = {
|
|
628
|
+
"user.id": userId
|
|
629
|
+
};
|
|
630
|
+
if (email) attributes["user.email"] = email;
|
|
631
|
+
if (username) attributes["user.name"] = username;
|
|
632
|
+
return Effect.annotateCurrentSpan(attributes);
|
|
535
633
|
}
|
|
536
|
-
function annotateDataSize(
|
|
634
|
+
function annotateDataSize(bytes, items, compressionRatio) {
|
|
635
|
+
const attributes = {
|
|
636
|
+
"data.size.bytes": bytes,
|
|
637
|
+
"data.size.items": items
|
|
638
|
+
};
|
|
639
|
+
if (compressionRatio !== void 0) {
|
|
640
|
+
attributes["data.compression.ratio"] = compressionRatio;
|
|
641
|
+
}
|
|
642
|
+
return Effect.annotateCurrentSpan(attributes);
|
|
537
643
|
}
|
|
538
|
-
function annotateBatch(
|
|
644
|
+
function annotateBatch(totalItems, batchSize, successCount, failureCount) {
|
|
645
|
+
const attributes = {
|
|
646
|
+
"batch.size": batchSize,
|
|
647
|
+
"batch.total_items": totalItems,
|
|
648
|
+
"batch.count": Math.ceil(totalItems / batchSize)
|
|
649
|
+
};
|
|
650
|
+
if (successCount !== void 0) {
|
|
651
|
+
attributes["batch.success_count"] = successCount;
|
|
652
|
+
}
|
|
653
|
+
if (failureCount !== void 0) {
|
|
654
|
+
attributes["batch.failure_count"] = failureCount;
|
|
655
|
+
}
|
|
656
|
+
return Effect.annotateCurrentSpan(attributes);
|
|
657
|
+
}
|
|
658
|
+
function annotateLLM(model, provider, tokens) {
|
|
659
|
+
const attributes = {
|
|
660
|
+
"llm.model": model,
|
|
661
|
+
"llm.provider": provider
|
|
662
|
+
};
|
|
663
|
+
if (tokens) {
|
|
664
|
+
if (tokens.prompt !== void 0) attributes["llm.tokens.prompt"] = tokens.prompt;
|
|
665
|
+
if (tokens.completion !== void 0) attributes["llm.tokens.completion"] = tokens.completion;
|
|
666
|
+
if (tokens.total !== void 0) attributes["llm.tokens.total"] = tokens.total;
|
|
667
|
+
}
|
|
668
|
+
return Effect.annotateCurrentSpan(attributes);
|
|
669
|
+
}
|
|
670
|
+
function annotateQuery(query, duration, rowCount, database) {
|
|
671
|
+
const attributes = {
|
|
672
|
+
"db.statement": query.length > 1e3 ? query.substring(0, 1e3) + "..." : query
|
|
673
|
+
};
|
|
674
|
+
if (duration !== void 0) attributes["db.duration.ms"] = duration;
|
|
675
|
+
if (rowCount !== void 0) attributes["db.row_count"] = rowCount;
|
|
676
|
+
if (database) attributes["db.name"] = database;
|
|
677
|
+
return Effect.annotateCurrentSpan(attributes);
|
|
678
|
+
}
|
|
679
|
+
function annotateHttpRequest(method, url, statusCode, contentLength) {
|
|
680
|
+
const attributes = {
|
|
681
|
+
"http.method": method,
|
|
682
|
+
"http.url": url
|
|
683
|
+
};
|
|
684
|
+
if (statusCode !== void 0) attributes["http.status_code"] = statusCode;
|
|
685
|
+
if (contentLength !== void 0) attributes["http.response.content_length"] = contentLength;
|
|
686
|
+
return Effect.annotateCurrentSpan(attributes);
|
|
539
687
|
}
|
|
540
|
-
function
|
|
688
|
+
function annotateError(error, recoverable, errorType) {
|
|
689
|
+
const errorMessage = typeof error === "string" ? error : error.message;
|
|
690
|
+
const errorStack = typeof error === "string" ? void 0 : error.stack;
|
|
691
|
+
const attributes = {
|
|
692
|
+
"error.message": errorMessage,
|
|
693
|
+
"error.recoverable": recoverable
|
|
694
|
+
};
|
|
695
|
+
if (errorType) attributes["error.type"] = errorType;
|
|
696
|
+
if (errorStack) attributes["error.stack"] = errorStack;
|
|
697
|
+
return Effect.annotateCurrentSpan(attributes);
|
|
541
698
|
}
|
|
542
|
-
function
|
|
699
|
+
function annotatePriority(priority, reason) {
|
|
700
|
+
const attributes = {
|
|
701
|
+
"operation.priority": priority
|
|
702
|
+
};
|
|
703
|
+
if (reason) attributes["operation.priority.reason"] = reason;
|
|
704
|
+
return Effect.annotateCurrentSpan(attributes);
|
|
543
705
|
}
|
|
544
|
-
function
|
|
706
|
+
function annotateCache(hit, key, ttl) {
|
|
707
|
+
const attributes = {
|
|
708
|
+
"cache.hit": hit,
|
|
709
|
+
"cache.key": key
|
|
710
|
+
};
|
|
711
|
+
if (ttl !== void 0) attributes["cache.ttl.seconds"] = ttl;
|
|
712
|
+
return Effect.annotateCurrentSpan(attributes);
|
|
545
713
|
}
|
|
546
|
-
function
|
|
714
|
+
function extractEffectMetadata() {
|
|
715
|
+
return Effect.gen(function* () {
|
|
716
|
+
const metadata = {};
|
|
717
|
+
const currentFiber = Fiber.getCurrentFiber();
|
|
718
|
+
if (Option.isSome(currentFiber)) {
|
|
719
|
+
const fiber = currentFiber.value;
|
|
720
|
+
const fiberId = fiber.id();
|
|
721
|
+
metadata["effect.fiber.id"] = FiberId.threadName(fiberId);
|
|
722
|
+
const status = yield* Fiber.status(fiber);
|
|
723
|
+
if (status._tag) {
|
|
724
|
+
metadata["effect.fiber.status"] = status._tag;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
const parentSpanResult = yield* Effect.currentSpan.pipe(
|
|
728
|
+
Effect.option
|
|
729
|
+
// Convert NoSuchElementException to Option
|
|
730
|
+
);
|
|
731
|
+
if (Option.isSome(parentSpanResult)) {
|
|
732
|
+
const parentSpan = parentSpanResult.value;
|
|
733
|
+
metadata["effect.operation.nested"] = true;
|
|
734
|
+
metadata["effect.operation.root"] = false;
|
|
735
|
+
if (parentSpan.spanId) {
|
|
736
|
+
metadata["effect.parent.span.id"] = parentSpan.spanId;
|
|
737
|
+
}
|
|
738
|
+
if (parentSpan.name) {
|
|
739
|
+
metadata["effect.parent.span.name"] = parentSpan.name;
|
|
740
|
+
}
|
|
741
|
+
if (parentSpan.traceId) {
|
|
742
|
+
metadata["effect.parent.trace.id"] = parentSpan.traceId;
|
|
743
|
+
}
|
|
744
|
+
} else {
|
|
745
|
+
metadata["effect.operation.nested"] = false;
|
|
746
|
+
metadata["effect.operation.root"] = true;
|
|
747
|
+
}
|
|
748
|
+
return metadata;
|
|
749
|
+
});
|
|
547
750
|
}
|
|
548
|
-
function
|
|
751
|
+
function autoEnrichSpan() {
|
|
752
|
+
return Effect.gen(function* () {
|
|
753
|
+
const metadata = yield* extractEffectMetadata();
|
|
754
|
+
yield* Effect.annotateCurrentSpan(metadata);
|
|
755
|
+
});
|
|
549
756
|
}
|
|
550
|
-
function
|
|
757
|
+
function withAutoEnrichedSpan(spanName, options) {
|
|
758
|
+
return (self) => {
|
|
759
|
+
return Effect.gen(function* () {
|
|
760
|
+
yield* autoEnrichSpan();
|
|
761
|
+
return yield* self;
|
|
762
|
+
}).pipe(Effect.withSpan(spanName, options));
|
|
763
|
+
};
|
|
551
764
|
}
|
|
552
765
|
var createLogicalParentLink = (parentSpan, useSpanLinks) => {
|
|
553
766
|
if (!useSpanLinks) {
|
|
@@ -674,6 +887,6 @@ var FiberSet = {
|
|
|
674
887
|
runWithSpan
|
|
675
888
|
};
|
|
676
889
|
|
|
677
|
-
export { EffectInstrumentationLive, FiberSet, annotateBatch, annotateCache, annotateDataSize, annotateError, annotateHttpRequest, annotateLLM, annotatePriority, annotateQuery, annotateSpawnedTasks, annotateUser, createEffectInstrumentation, runIsolated, runWithSpan };
|
|
890
|
+
export { EffectInstrumentationLive, FiberSet, annotateBatch, annotateCache, annotateDataSize, annotateError, annotateHttpRequest, annotateLLM, annotatePriority, annotateQuery, annotateSpawnedTasks, annotateUser, autoEnrichSpan, createEffectInstrumentation, extractEffectMetadata, runIsolated, runWithSpan, withAutoEnrichedSpan };
|
|
678
891
|
//# sourceMappingURL=index.js.map
|
|
679
892
|
//# sourceMappingURL=index.js.map
|