@atrim/instrument-node 0.8.1-dev.ae570af.20260116212440 → 0.9.0
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 +6 -6
- package/target/dist/integrations/effect/auto/index.cjs +1145 -1933
- package/target/dist/integrations/effect/auto/index.cjs.map +1 -1
- package/target/dist/integrations/effect/auto/index.d.cts +189 -656
- package/target/dist/integrations/effect/auto/index.d.ts +189 -656
- package/target/dist/integrations/effect/auto/index.js +1136 -1903
- package/target/dist/integrations/effect/auto/index.js.map +1 -1
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var effect = require('effect');
|
|
4
|
-
var opentelemetry = require('@effect/opentelemetry');
|
|
5
|
-
var sdkTraceBase = require('@opentelemetry/sdk-trace-base');
|
|
6
|
-
var exporterTraceOtlpHttp = require('@opentelemetry/exporter-trace-otlp-http');
|
|
7
|
-
var resources = require('@opentelemetry/resources');
|
|
8
|
-
var semanticConventions = require('@opentelemetry/semantic-conventions');
|
|
9
|
-
var OtelApi = require('@opentelemetry/api');
|
|
10
4
|
var FileSystem = require('@effect/platform/FileSystem');
|
|
11
5
|
var HttpClient = require('@effect/platform/HttpClient');
|
|
12
6
|
var HttpClientRequest = require('@effect/platform/HttpClientRequest');
|
|
@@ -15,6 +9,12 @@ var zod = require('zod');
|
|
|
15
9
|
var fs = require('fs');
|
|
16
10
|
var path2 = require('path');
|
|
17
11
|
var TracerModule = require('effect/Tracer');
|
|
12
|
+
var opentelemetry = require('@effect/opentelemetry');
|
|
13
|
+
var OtelApi = require('@opentelemetry/api');
|
|
14
|
+
var sdkTraceBase = require('@opentelemetry/sdk-trace-base');
|
|
15
|
+
var exporterTraceOtlpHttp = require('@opentelemetry/exporter-trace-otlp-http');
|
|
16
|
+
var resources = require('@opentelemetry/resources');
|
|
17
|
+
var semanticConventions = require('@opentelemetry/semantic-conventions');
|
|
18
18
|
|
|
19
19
|
function _interopNamespace(e) {
|
|
20
20
|
if (e && e.__esModule) return e;
|
|
@@ -34,258 +34,36 @@ function _interopNamespace(e) {
|
|
|
34
34
|
return Object.freeze(n);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
var OtelApi__namespace = /*#__PURE__*/_interopNamespace(OtelApi);
|
|
38
37
|
var HttpClient__namespace = /*#__PURE__*/_interopNamespace(HttpClient);
|
|
39
38
|
var HttpClientRequest__namespace = /*#__PURE__*/_interopNamespace(HttpClientRequest);
|
|
40
39
|
var yaml__namespace = /*#__PURE__*/_interopNamespace(yaml);
|
|
41
40
|
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
42
41
|
var path2__namespace = /*#__PURE__*/_interopNamespace(path2);
|
|
43
42
|
var TracerModule__namespace = /*#__PURE__*/_interopNamespace(TracerModule);
|
|
43
|
+
var OtelApi__namespace = /*#__PURE__*/_interopNamespace(OtelApi);
|
|
44
44
|
|
|
45
45
|
var __defProp = Object.defineProperty;
|
|
46
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
47
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
48
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
46
49
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
47
|
-
var
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
var
|
|
51
|
-
var
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// Which operators to auto-isolate
|
|
60
|
-
operators: zod.z.object({
|
|
61
|
-
fiberset_run: zod.z.boolean().default(true),
|
|
62
|
-
effect_fork: zod.z.boolean().default(true),
|
|
63
|
-
effect_fork_daemon: zod.z.boolean().default(true),
|
|
64
|
-
effect_fork_in: zod.z.boolean().default(false)
|
|
65
|
-
}).default({}),
|
|
66
|
-
// Virtual parent tracking configuration
|
|
67
|
-
tracking: zod.z.object({
|
|
68
|
-
use_span_links: zod.z.boolean().default(true),
|
|
69
|
-
use_attributes: zod.z.boolean().default(true),
|
|
70
|
-
capture_logical_parent: zod.z.boolean().default(true)
|
|
71
|
-
}).default({}),
|
|
72
|
-
// Span categorization
|
|
73
|
-
attributes: zod.z.object({
|
|
74
|
-
category: zod.z.string().default("background_task"),
|
|
75
|
-
add_metadata: zod.z.boolean().default(true)
|
|
76
|
-
}).default({})
|
|
77
|
-
});
|
|
78
|
-
var SpanNamingRuleSchema = zod.z.object({
|
|
79
|
-
// Match criteria (all specified criteria must match)
|
|
80
|
-
match: zod.z.object({
|
|
81
|
-
// Regex pattern to match file path
|
|
82
|
-
file: zod.z.string().optional(),
|
|
83
|
-
// Regex pattern to match function name
|
|
84
|
-
function: zod.z.string().optional(),
|
|
85
|
-
// Regex pattern to match module name
|
|
86
|
-
module: zod.z.string().optional()
|
|
87
|
-
}),
|
|
88
|
-
// Span name template with variables:
|
|
89
|
-
// {fiber_id} - Fiber ID
|
|
90
|
-
// {function} - Function name
|
|
91
|
-
// {module} - Module name
|
|
92
|
-
// {file} - File path
|
|
93
|
-
// {line} - Line number
|
|
94
|
-
// {operator} - Effect operator (gen, all, forEach, etc.)
|
|
95
|
-
// {match:field:N} - Captured regex group from match
|
|
96
|
-
name: zod.z.string()
|
|
97
|
-
});
|
|
98
|
-
var AutoInstrumentationConfigSchema = zod.z.object({
|
|
99
|
-
// Enable/disable auto-instrumentation
|
|
100
|
-
enabled: zod.z.boolean().default(false),
|
|
101
|
-
// Tracing granularity
|
|
102
|
-
// - 'fiber': Trace at fiber creation (recommended, lower overhead)
|
|
103
|
-
// - 'operator': Trace each Effect operator (higher granularity, more overhead)
|
|
104
|
-
granularity: zod.z.enum(["fiber", "operator"]).default("fiber"),
|
|
105
|
-
// Smart span naming configuration
|
|
106
|
-
span_naming: zod.z.object({
|
|
107
|
-
// Default span name template when no rules match
|
|
108
|
-
default: zod.z.string().default("effect.fiber.{fiber_id}"),
|
|
109
|
-
// Infer span names from source code (requires stack trace parsing)
|
|
110
|
-
// Adds ~50-100μs overhead per fiber
|
|
111
|
-
infer_from_source: zod.z.boolean().default(true),
|
|
112
|
-
// Naming rules (first match wins)
|
|
113
|
-
rules: zod.z.array(SpanNamingRuleSchema).default([])
|
|
114
|
-
}).default({}),
|
|
115
|
-
// Span relationship configuration for forked fibers
|
|
116
|
-
// Controls how child fiber spans relate to their parent/forking context
|
|
117
|
-
span_relationships: zod.z.object({
|
|
118
|
-
// Relationship type between forked fiber spans and their parent context
|
|
119
|
-
// - 'parent-child': Use parent-child relationship (default, traditional tracing)
|
|
120
|
-
// Parent span shows child as nested. Works well with most observability tools.
|
|
121
|
-
// - 'span-links': Use span links (semantically correct for async forks per OTel spec)
|
|
122
|
-
// Fibers get independent traces linked to parent. Better for long-running fibers.
|
|
123
|
-
// - 'both': Create parent-child AND add span links
|
|
124
|
-
// Maximum visibility but may create redundant data.
|
|
125
|
-
type: zod.z.enum(["parent-child", "span-links", "both"]).default("parent-child"),
|
|
126
|
-
// Custom attributes to add to span links (only used when type includes links)
|
|
127
|
-
link_attributes: zod.z.object({
|
|
128
|
-
// Link type identifier
|
|
129
|
-
"link.type": zod.z.string().default("fork"),
|
|
130
|
-
// Custom attributes (key-value pairs)
|
|
131
|
-
custom: zod.z.record(zod.z.string()).optional()
|
|
132
|
-
}).optional()
|
|
133
|
-
}).default({}),
|
|
134
|
-
// Pattern-based filtering
|
|
135
|
-
filter: zod.z.object({
|
|
136
|
-
// Only trace spans matching these patterns (empty = trace all)
|
|
137
|
-
include: zod.z.array(zod.z.string()).default([]),
|
|
138
|
-
// Never trace spans matching these patterns
|
|
139
|
-
exclude: zod.z.array(zod.z.string()).default([])
|
|
140
|
-
}).default({}),
|
|
141
|
-
// Performance controls
|
|
142
|
-
performance: zod.z.object({
|
|
143
|
-
// Sample rate (0.0 - 1.0)
|
|
144
|
-
sampling_rate: zod.z.number().min(0).max(1).default(1),
|
|
145
|
-
// Skip fibers shorter than this duration (e.g., "10ms", "100 millis")
|
|
146
|
-
min_duration: zod.z.string().default("0ms"),
|
|
147
|
-
// Maximum concurrent traced fibers (0 = unlimited)
|
|
148
|
-
max_concurrent: zod.z.number().default(0)
|
|
149
|
-
}).default({}),
|
|
150
|
-
// Automatic metadata extraction
|
|
151
|
-
metadata: zod.z.object({
|
|
152
|
-
// Extract Effect fiber information
|
|
153
|
-
fiber_info: zod.z.boolean().default(true),
|
|
154
|
-
// Extract source location (file:line)
|
|
155
|
-
source_location: zod.z.boolean().default(true),
|
|
156
|
-
// Extract parent fiber information
|
|
157
|
-
parent_fiber: zod.z.boolean().default(true)
|
|
158
|
-
}).default({})
|
|
159
|
-
});
|
|
160
|
-
var OperationTracingConfigSchema = zod.z.object({
|
|
161
|
-
// Enable/disable operation tracing
|
|
162
|
-
enabled: zod.z.boolean().default(false),
|
|
163
|
-
// Global span naming settings
|
|
164
|
-
span_naming: zod.z.object({
|
|
165
|
-
// Include source location (file:line) in span name
|
|
166
|
-
// Default: true - produces "effect.all (index.ts:42)"
|
|
167
|
-
// When false - produces "effect.all"
|
|
168
|
-
include_location: zod.z.boolean().default(true),
|
|
169
|
-
// Span name template with variables:
|
|
170
|
-
// {op} - Operation name (all, forEach, retry, etc.)
|
|
171
|
-
// {file} - Full file path
|
|
172
|
-
// {filename} - Just the filename (basename)
|
|
173
|
-
// {line} - Line number
|
|
174
|
-
// {column} - Column number
|
|
175
|
-
// Default: "effect.{op} ({filename}:{line})"
|
|
176
|
-
template: zod.z.string().default("effect.{op} ({filename}:{line})")
|
|
177
|
-
}).default({}),
|
|
178
|
-
// Operations to trace
|
|
179
|
-
operations: zod.z.array(
|
|
180
|
-
zod.z.object({
|
|
181
|
-
// Operation name: 'all', 'forEach', 'retry', etc.
|
|
182
|
-
name: zod.z.string(),
|
|
183
|
-
// Custom span name template (overrides global span_naming.template)
|
|
184
|
-
// Supports same variables: {op}, {file}, {filename}, {line}, {column}
|
|
185
|
-
span_name: zod.z.string().optional(),
|
|
186
|
-
// Include item count in span attributes
|
|
187
|
-
include_count: zod.z.boolean().default(true),
|
|
188
|
-
// Include stack trace in span attributes
|
|
189
|
-
include_stack: zod.z.boolean().default(true)
|
|
190
|
-
})
|
|
191
|
-
).default([])
|
|
192
|
-
});
|
|
193
|
-
var HttpFilteringConfigSchema = zod.z.object({
|
|
194
|
-
// Patterns to ignore for outgoing HTTP requests (string patterns only in YAML)
|
|
195
|
-
ignore_outgoing_urls: zod.z.array(zod.z.string()).optional(),
|
|
196
|
-
// Patterns to ignore for incoming HTTP requests (string patterns only in YAML)
|
|
197
|
-
ignore_incoming_paths: zod.z.array(zod.z.string()).optional(),
|
|
198
|
-
// Require parent span for outgoing requests (prevents root spans for HTTP calls)
|
|
199
|
-
require_parent_for_outgoing_spans: zod.z.boolean().optional(),
|
|
200
|
-
// Trace context propagation configuration
|
|
201
|
-
// Controls which cross-origin requests receive W3C Trace Context headers (traceparent, tracestate)
|
|
202
|
-
propagate_trace_context: zod.z.object({
|
|
203
|
-
// Strategy for trace propagation
|
|
204
|
-
// - "all": Propagate to all cross-origin requests (may cause CORS errors)
|
|
205
|
-
// - "none": Never propagate trace headers
|
|
206
|
-
// - "same-origin": Only propagate to same-origin requests (default, safe)
|
|
207
|
-
// - "patterns": Propagate based on include_urls patterns
|
|
208
|
-
strategy: zod.z.enum(["all", "none", "same-origin", "patterns"]).default("same-origin"),
|
|
209
|
-
// URL patterns to include when strategy is "patterns"
|
|
210
|
-
// Supports regex patterns (e.g., "^https://api\\.myapp\\.com")
|
|
211
|
-
include_urls: zod.z.array(zod.z.string()).optional()
|
|
212
|
-
}).optional()
|
|
213
|
-
});
|
|
214
|
-
var ExporterConfigSchema = zod.z.object({
|
|
215
|
-
// Exporter type: 'otlp' | 'console' | 'none'
|
|
216
|
-
// - 'otlp': Export to OTLP endpoint (production)
|
|
217
|
-
// - 'console': Log spans to console (development)
|
|
218
|
-
// - 'none': No export (disable tracing)
|
|
219
|
-
type: zod.z.enum(["otlp", "console", "none"]).default("otlp"),
|
|
220
|
-
// OTLP endpoint URL (for type: otlp)
|
|
221
|
-
// Defaults to OTEL_EXPORTER_OTLP_ENDPOINT env var or http://localhost:4318
|
|
222
|
-
endpoint: zod.z.string().optional(),
|
|
223
|
-
// Custom headers to send with OTLP requests (for type: otlp)
|
|
224
|
-
// Useful for authentication (x-api-key, Authorization, etc.)
|
|
225
|
-
headers: zod.z.record(zod.z.string()).optional(),
|
|
226
|
-
// Span processor type
|
|
227
|
-
// - 'batch': Batch spans for export (production, lower overhead)
|
|
228
|
-
// - 'simple': Export immediately (development, no batching delay)
|
|
229
|
-
processor: zod.z.enum(["batch", "simple"]).default("batch"),
|
|
230
|
-
// Batch processor settings (for processor: batch)
|
|
231
|
-
batch: zod.z.object({
|
|
232
|
-
// Max time to wait before exporting (milliseconds)
|
|
233
|
-
scheduled_delay_millis: zod.z.number().default(1e3),
|
|
234
|
-
// Max batch size
|
|
235
|
-
max_export_batch_size: zod.z.number().default(100)
|
|
236
|
-
}).optional()
|
|
237
|
-
});
|
|
238
|
-
var InstrumentationConfigSchema = zod.z.object({
|
|
239
|
-
version: zod.z.string(),
|
|
240
|
-
instrumentation: zod.z.object({
|
|
241
|
-
enabled: zod.z.boolean(),
|
|
242
|
-
description: zod.z.string().optional(),
|
|
243
|
-
logging: zod.z.enum(["on", "off", "minimal"]).optional().default("on"),
|
|
244
|
-
instrument_patterns: zod.z.array(PatternConfigSchema),
|
|
245
|
-
ignore_patterns: zod.z.array(PatternConfigSchema)
|
|
246
|
-
}),
|
|
247
|
-
effect: zod.z.object({
|
|
248
|
-
// Enable/disable Effect tracing entirely
|
|
249
|
-
// When false, EffectInstrumentationLive returns Layer.empty
|
|
250
|
-
enabled: zod.z.boolean().default(true),
|
|
251
|
-
// Exporter mode (legacy - use exporter.type instead):
|
|
252
|
-
// - "unified": Use global TracerProvider from Node SDK (recommended, enables filtering)
|
|
253
|
-
// - "standalone": Use Effect's own OTLP exporter (bypasses Node SDK filtering)
|
|
254
|
-
exporter: zod.z.enum(["unified", "standalone"]).default("unified"),
|
|
255
|
-
// Exporter configuration (for auto-instrumentation)
|
|
256
|
-
exporter_config: ExporterConfigSchema.optional(),
|
|
257
|
-
auto_extract_metadata: zod.z.boolean(),
|
|
258
|
-
auto_isolation: AutoIsolationConfigSchema.optional(),
|
|
259
|
-
// Auto-instrumentation: automatic tracing of all Effect fibers
|
|
260
|
-
auto_instrumentation: AutoInstrumentationConfigSchema.optional(),
|
|
261
|
-
// Operation tracing: automatic tracing of Effect.all, Effect.forEach, etc.
|
|
262
|
-
operation_tracing: OperationTracingConfigSchema.optional()
|
|
263
|
-
}).optional(),
|
|
264
|
-
http: HttpFilteringConfigSchema.optional()
|
|
265
|
-
});
|
|
266
|
-
var defaultConfig = {
|
|
267
|
-
version: "1.0",
|
|
268
|
-
instrumentation: {
|
|
269
|
-
enabled: true,
|
|
270
|
-
logging: "on",
|
|
271
|
-
description: "Default instrumentation configuration",
|
|
272
|
-
instrument_patterns: [
|
|
273
|
-
{ pattern: "^app\\.", enabled: true, description: "Application operations" },
|
|
274
|
-
{ pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
|
|
275
|
-
{ pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
|
|
276
|
-
],
|
|
277
|
-
ignore_patterns: [
|
|
278
|
-
{ pattern: "^test\\.", description: "Test utilities" },
|
|
279
|
-
{ pattern: "^internal\\.", description: "Internal operations" },
|
|
280
|
-
{ pattern: "^health\\.", description: "Health checks" }
|
|
281
|
-
]
|
|
282
|
-
},
|
|
283
|
-
effect: {
|
|
284
|
-
enabled: true,
|
|
285
|
-
exporter: "unified",
|
|
286
|
-
auto_extract_metadata: true
|
|
50
|
+
var __esm = (fn, res) => function __init() {
|
|
51
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
52
|
+
};
|
|
53
|
+
var __export = (target, all) => {
|
|
54
|
+
for (var name in all)
|
|
55
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
56
|
+
};
|
|
57
|
+
var __copyProps = (to, from, except, desc) => {
|
|
58
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
59
|
+
for (let key of __getOwnPropNames(from))
|
|
60
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
61
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
287
62
|
}
|
|
63
|
+
return to;
|
|
288
64
|
};
|
|
65
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
66
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
289
67
|
function parseAndValidateConfig(content) {
|
|
290
68
|
let parsed;
|
|
291
69
|
if (typeof content === "string") {
|
|
@@ -295,262 +73,508 @@ function parseAndValidateConfig(content) {
|
|
|
295
73
|
}
|
|
296
74
|
return InstrumentationConfigSchema.parse(parsed);
|
|
297
75
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
})
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
76
|
+
var __defProp2, __defNormalProp2, __publicField2, PatternConfigSchema, AutoIsolationConfigSchema, SpanNamingRuleSchema, AutoInstrumentationConfigSchema, OperationTracingConfigSchema, HttpFilteringConfigSchema, ExporterConfigSchema, InstrumentationConfigSchema, defaultConfig, ConfigUrlError, ConfigValidationError, ConfigFileError, SECURITY_DEFAULTS, ConfigLoader, parseYamlContent, loadFromFileWithFs, loadFromHttpWithClient, makeConfigLoader, Logger, logger;
|
|
77
|
+
var init_dist = __esm({
|
|
78
|
+
"../core/target/dist/index.js"() {
|
|
79
|
+
__defProp2 = Object.defineProperty;
|
|
80
|
+
__defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
81
|
+
__publicField2 = (obj, key, value) => __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
82
|
+
PatternConfigSchema = zod.z.object({
|
|
83
|
+
pattern: zod.z.string(),
|
|
84
|
+
enabled: zod.z.boolean().optional(),
|
|
85
|
+
description: zod.z.string().optional()
|
|
86
|
+
});
|
|
87
|
+
AutoIsolationConfigSchema = zod.z.object({
|
|
88
|
+
// Global enable/disable for auto-isolation
|
|
89
|
+
enabled: zod.z.boolean().default(false),
|
|
90
|
+
// Which operators to auto-isolate
|
|
91
|
+
operators: zod.z.object({
|
|
92
|
+
fiberset_run: zod.z.boolean().default(true),
|
|
93
|
+
effect_fork: zod.z.boolean().default(true),
|
|
94
|
+
effect_fork_daemon: zod.z.boolean().default(true),
|
|
95
|
+
effect_fork_in: zod.z.boolean().default(false)
|
|
96
|
+
}).default({}),
|
|
97
|
+
// Virtual parent tracking configuration
|
|
98
|
+
tracking: zod.z.object({
|
|
99
|
+
use_span_links: zod.z.boolean().default(true),
|
|
100
|
+
use_attributes: zod.z.boolean().default(true),
|
|
101
|
+
capture_logical_parent: zod.z.boolean().default(true)
|
|
102
|
+
}).default({}),
|
|
103
|
+
// Span categorization
|
|
104
|
+
attributes: zod.z.object({
|
|
105
|
+
category: zod.z.string().default("background_task"),
|
|
106
|
+
add_metadata: zod.z.boolean().default(true)
|
|
107
|
+
}).default({})
|
|
108
|
+
});
|
|
109
|
+
SpanNamingRuleSchema = zod.z.object({
|
|
110
|
+
// Match criteria (all specified criteria must match)
|
|
111
|
+
match: zod.z.object({
|
|
112
|
+
// Regex pattern to match file path
|
|
113
|
+
file: zod.z.string().optional(),
|
|
114
|
+
// Regex pattern to match function name
|
|
115
|
+
function: zod.z.string().optional(),
|
|
116
|
+
// Regex pattern to match module name
|
|
117
|
+
module: zod.z.string().optional()
|
|
118
|
+
}),
|
|
119
|
+
// Span name template with variables:
|
|
120
|
+
// {fiber_id} - Fiber ID
|
|
121
|
+
// {function} - Function name
|
|
122
|
+
// {module} - Module name
|
|
123
|
+
// {file} - File path
|
|
124
|
+
// {line} - Line number
|
|
125
|
+
// {operator} - Effect operator (gen, all, forEach, etc.)
|
|
126
|
+
// {match:field:N} - Captured regex group from match
|
|
127
|
+
name: zod.z.string()
|
|
128
|
+
});
|
|
129
|
+
AutoInstrumentationConfigSchema = zod.z.object({
|
|
130
|
+
// Enable/disable auto-instrumentation
|
|
131
|
+
enabled: zod.z.boolean().default(false),
|
|
132
|
+
// Tracing granularity
|
|
133
|
+
// - 'fiber': Trace at fiber creation (recommended, lower overhead)
|
|
134
|
+
// - 'operator': Trace each Effect operator (higher granularity, more overhead)
|
|
135
|
+
granularity: zod.z.enum(["fiber", "operator"]).default("fiber"),
|
|
136
|
+
// Smart span naming configuration
|
|
137
|
+
span_naming: zod.z.object({
|
|
138
|
+
// Default span name template when no rules match
|
|
139
|
+
default: zod.z.string().default("effect.fiber.{fiber_id}"),
|
|
140
|
+
// Infer span names from source code (requires stack trace parsing)
|
|
141
|
+
// Adds ~50-100μs overhead per fiber
|
|
142
|
+
infer_from_source: zod.z.boolean().default(true),
|
|
143
|
+
// Naming rules (first match wins)
|
|
144
|
+
rules: zod.z.array(SpanNamingRuleSchema).default([])
|
|
145
|
+
}).default({}),
|
|
146
|
+
// Span relationship configuration for forked fibers
|
|
147
|
+
// Controls how child fiber spans relate to their parent/forking context
|
|
148
|
+
span_relationships: zod.z.object({
|
|
149
|
+
// Relationship type between forked fiber spans and their parent context
|
|
150
|
+
// - 'parent-child': Use parent-child relationship (default, traditional tracing)
|
|
151
|
+
// Parent span shows child as nested. Works well with most observability tools.
|
|
152
|
+
// - 'span-links': Use span links (semantically correct for async forks per OTel spec)
|
|
153
|
+
// Fibers get independent traces linked to parent. Better for long-running fibers.
|
|
154
|
+
// - 'both': Create parent-child AND add span links
|
|
155
|
+
// Maximum visibility but may create redundant data.
|
|
156
|
+
type: zod.z.enum(["parent-child", "span-links", "both"]).default("parent-child"),
|
|
157
|
+
// Custom attributes to add to span links (only used when type includes links)
|
|
158
|
+
link_attributes: zod.z.object({
|
|
159
|
+
// Link type identifier
|
|
160
|
+
"link.type": zod.z.string().default("fork"),
|
|
161
|
+
// Custom attributes (key-value pairs)
|
|
162
|
+
custom: zod.z.record(zod.z.string()).optional()
|
|
163
|
+
}).optional()
|
|
164
|
+
}).default({}),
|
|
165
|
+
// Pattern-based filtering
|
|
166
|
+
filter: zod.z.object({
|
|
167
|
+
// Only trace spans matching these patterns (empty = trace all)
|
|
168
|
+
include: zod.z.array(zod.z.string()).default([]),
|
|
169
|
+
// Never trace spans matching these patterns
|
|
170
|
+
exclude: zod.z.array(zod.z.string()).default([])
|
|
171
|
+
}).default({}),
|
|
172
|
+
// Performance controls
|
|
173
|
+
performance: zod.z.object({
|
|
174
|
+
// Sample rate (0.0 - 1.0)
|
|
175
|
+
sampling_rate: zod.z.number().min(0).max(1).default(1),
|
|
176
|
+
// Skip fibers shorter than this duration (e.g., "10ms", "100 millis")
|
|
177
|
+
min_duration: zod.z.string().default("0ms"),
|
|
178
|
+
// Maximum concurrent traced fibers (0 = unlimited)
|
|
179
|
+
max_concurrent: zod.z.number().default(0)
|
|
180
|
+
}).default({}),
|
|
181
|
+
// Automatic metadata extraction
|
|
182
|
+
metadata: zod.z.object({
|
|
183
|
+
// Extract Effect fiber information
|
|
184
|
+
fiber_info: zod.z.boolean().default(true),
|
|
185
|
+
// Extract source location (file:line)
|
|
186
|
+
source_location: zod.z.boolean().default(true),
|
|
187
|
+
// Extract parent fiber information
|
|
188
|
+
parent_fiber: zod.z.boolean().default(true)
|
|
189
|
+
}).default({})
|
|
190
|
+
});
|
|
191
|
+
OperationTracingConfigSchema = zod.z.object({
|
|
192
|
+
// Enable/disable operation tracing
|
|
193
|
+
enabled: zod.z.boolean().default(false),
|
|
194
|
+
// Global span naming settings
|
|
195
|
+
span_naming: zod.z.object({
|
|
196
|
+
// Include source location (file:line) in span name
|
|
197
|
+
// Default: true - produces "effect.all (index.ts:42)"
|
|
198
|
+
// When false - produces "effect.all"
|
|
199
|
+
include_location: zod.z.boolean().default(true),
|
|
200
|
+
// Span name template with variables:
|
|
201
|
+
// {op} - Operation name (all, forEach, retry, etc.)
|
|
202
|
+
// {file} - Full file path
|
|
203
|
+
// {filename} - Just the filename (basename)
|
|
204
|
+
// {line} - Line number
|
|
205
|
+
// {column} - Column number
|
|
206
|
+
// Default: "effect.{op} ({filename}:{line})"
|
|
207
|
+
template: zod.z.string().default("effect.{op} ({filename}:{line})")
|
|
208
|
+
}).default({}),
|
|
209
|
+
// Operations to trace
|
|
210
|
+
operations: zod.z.array(
|
|
211
|
+
zod.z.object({
|
|
212
|
+
// Operation name: 'all', 'forEach', 'retry', etc.
|
|
213
|
+
name: zod.z.string(),
|
|
214
|
+
// Custom span name template (overrides global span_naming.template)
|
|
215
|
+
// Supports same variables: {op}, {file}, {filename}, {line}, {column}
|
|
216
|
+
span_name: zod.z.string().optional(),
|
|
217
|
+
// Include item count in span attributes
|
|
218
|
+
include_count: zod.z.boolean().default(true),
|
|
219
|
+
// Include stack trace in span attributes
|
|
220
|
+
include_stack: zod.z.boolean().default(true)
|
|
386
221
|
})
|
|
387
|
-
)
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
222
|
+
).default([])
|
|
223
|
+
});
|
|
224
|
+
HttpFilteringConfigSchema = zod.z.object({
|
|
225
|
+
// Patterns to ignore for outgoing HTTP requests (string patterns only in YAML)
|
|
226
|
+
ignore_outgoing_urls: zod.z.array(zod.z.string()).optional(),
|
|
227
|
+
// Patterns to ignore for incoming HTTP requests (string patterns only in YAML)
|
|
228
|
+
ignore_incoming_paths: zod.z.array(zod.z.string()).optional(),
|
|
229
|
+
// Require parent span for outgoing requests (prevents root spans for HTTP calls)
|
|
230
|
+
require_parent_for_outgoing_spans: zod.z.boolean().optional(),
|
|
231
|
+
// Trace context propagation configuration
|
|
232
|
+
// Controls which cross-origin requests receive W3C Trace Context headers (traceparent, tracestate)
|
|
233
|
+
propagate_trace_context: zod.z.object({
|
|
234
|
+
// Strategy for trace propagation
|
|
235
|
+
// - "all": Propagate to all cross-origin requests (may cause CORS errors)
|
|
236
|
+
// - "none": Never propagate trace headers
|
|
237
|
+
// - "same-origin": Only propagate to same-origin requests (default, safe)
|
|
238
|
+
// - "patterns": Propagate based on include_urls patterns
|
|
239
|
+
strategy: zod.z.enum(["all", "none", "same-origin", "patterns"]).default("same-origin"),
|
|
240
|
+
// URL patterns to include when strategy is "patterns"
|
|
241
|
+
// Supports regex patterns (e.g., "^https://api\\.myapp\\.com")
|
|
242
|
+
include_urls: zod.z.array(zod.z.string()).optional()
|
|
243
|
+
}).optional()
|
|
244
|
+
});
|
|
245
|
+
ExporterConfigSchema = zod.z.object({
|
|
246
|
+
// Exporter type: 'otlp' | 'console' | 'none'
|
|
247
|
+
// - 'otlp': Export to OTLP endpoint (production)
|
|
248
|
+
// - 'console': Log spans to console (development)
|
|
249
|
+
// - 'none': No export (disable tracing)
|
|
250
|
+
type: zod.z.enum(["otlp", "console", "none"]).default("otlp"),
|
|
251
|
+
// OTLP endpoint URL (for type: otlp)
|
|
252
|
+
// Defaults to OTEL_EXPORTER_OTLP_ENDPOINT env var or http://localhost:4318
|
|
253
|
+
endpoint: zod.z.string().optional(),
|
|
254
|
+
// Custom headers to send with OTLP requests (for type: otlp)
|
|
255
|
+
// Useful for authentication (x-api-key, Authorization, etc.)
|
|
256
|
+
headers: zod.z.record(zod.z.string()).optional(),
|
|
257
|
+
// Span processor type
|
|
258
|
+
// - 'batch': Batch spans for export (production, lower overhead)
|
|
259
|
+
// - 'simple': Export immediately (development, no batching delay)
|
|
260
|
+
processor: zod.z.enum(["batch", "simple"]).default("batch"),
|
|
261
|
+
// Batch processor settings (for processor: batch)
|
|
262
|
+
batch: zod.z.object({
|
|
263
|
+
// Max time to wait before exporting (milliseconds)
|
|
264
|
+
scheduled_delay_millis: zod.z.number().default(1e3),
|
|
265
|
+
// Max batch size
|
|
266
|
+
max_export_batch_size: zod.z.number().default(100)
|
|
267
|
+
}).optional()
|
|
268
|
+
});
|
|
269
|
+
InstrumentationConfigSchema = zod.z.object({
|
|
270
|
+
version: zod.z.string(),
|
|
271
|
+
instrumentation: zod.z.object({
|
|
272
|
+
enabled: zod.z.boolean(),
|
|
273
|
+
description: zod.z.string().optional(),
|
|
274
|
+
logging: zod.z.enum(["on", "off", "minimal"]).optional().default("on"),
|
|
275
|
+
instrument_patterns: zod.z.array(PatternConfigSchema),
|
|
276
|
+
ignore_patterns: zod.z.array(PatternConfigSchema)
|
|
277
|
+
}),
|
|
278
|
+
effect: zod.z.object({
|
|
279
|
+
// Enable/disable Effect tracing entirely
|
|
280
|
+
// When false, EffectInstrumentationLive returns Layer.empty
|
|
281
|
+
enabled: zod.z.boolean().default(true),
|
|
282
|
+
// Exporter mode (legacy - use exporter.type instead):
|
|
283
|
+
// - "unified": Use global TracerProvider from Node SDK (recommended, enables filtering)
|
|
284
|
+
// - "standalone": Use Effect's own OTLP exporter (bypasses Node SDK filtering)
|
|
285
|
+
exporter: zod.z.enum(["unified", "standalone"]).default("unified"),
|
|
286
|
+
// Exporter configuration (for auto-instrumentation)
|
|
287
|
+
exporter_config: ExporterConfigSchema.optional(),
|
|
288
|
+
auto_extract_metadata: zod.z.boolean(),
|
|
289
|
+
auto_isolation: AutoIsolationConfigSchema.optional(),
|
|
290
|
+
// Auto-instrumentation: automatic tracing of all Effect fibers
|
|
291
|
+
auto_instrumentation: AutoInstrumentationConfigSchema.optional(),
|
|
292
|
+
// Operation tracing: automatic tracing of Effect.all, Effect.forEach, etc.
|
|
293
|
+
operation_tracing: OperationTracingConfigSchema.optional()
|
|
294
|
+
}).optional(),
|
|
295
|
+
http: HttpFilteringConfigSchema.optional()
|
|
296
|
+
});
|
|
297
|
+
defaultConfig = {
|
|
298
|
+
version: "1.0",
|
|
299
|
+
instrumentation: {
|
|
300
|
+
enabled: true,
|
|
301
|
+
logging: "on",
|
|
302
|
+
description: "Default instrumentation configuration",
|
|
303
|
+
instrument_patterns: [
|
|
304
|
+
{ pattern: "^app\\.", enabled: true, description: "Application operations" },
|
|
305
|
+
{ pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
|
|
306
|
+
{ pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
|
|
307
|
+
],
|
|
308
|
+
ignore_patterns: [
|
|
309
|
+
{ pattern: "^test\\.", description: "Test utilities" },
|
|
310
|
+
{ pattern: "^internal\\.", description: "Internal operations" },
|
|
311
|
+
{ pattern: "^health\\.", description: "Health checks" }
|
|
312
|
+
]
|
|
313
|
+
},
|
|
314
|
+
effect: {
|
|
315
|
+
enabled: true,
|
|
316
|
+
exporter: "unified",
|
|
317
|
+
auto_extract_metadata: true
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
(class extends effect.Data.TaggedError("ConfigError") {
|
|
321
|
+
get message() {
|
|
322
|
+
return this.reason;
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
ConfigUrlError = class extends effect.Data.TaggedError("ConfigUrlError") {
|
|
326
|
+
get message() {
|
|
327
|
+
return this.reason;
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
ConfigValidationError = class extends effect.Data.TaggedError("ConfigValidationError") {
|
|
331
|
+
get message() {
|
|
332
|
+
return this.reason;
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
ConfigFileError = class extends effect.Data.TaggedError("ConfigFileError") {
|
|
336
|
+
get message() {
|
|
337
|
+
return this.reason;
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
(class extends effect.Data.TaggedError("ServiceDetectionError") {
|
|
341
|
+
get message() {
|
|
342
|
+
return this.reason;
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
(class extends effect.Data.TaggedError("InitializationError") {
|
|
346
|
+
get message() {
|
|
347
|
+
return this.reason;
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
(class extends effect.Data.TaggedError("ExportError") {
|
|
351
|
+
get message() {
|
|
352
|
+
return this.reason;
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
(class extends effect.Data.TaggedError("ShutdownError") {
|
|
356
|
+
get message() {
|
|
357
|
+
return this.reason;
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
SECURITY_DEFAULTS = {
|
|
361
|
+
maxConfigSize: 1e6,
|
|
362
|
+
// 1MB
|
|
363
|
+
requestTimeout: 5e3
|
|
364
|
+
// 5 seconds
|
|
365
|
+
};
|
|
366
|
+
ConfigLoader = class extends effect.Context.Tag("ConfigLoader")() {
|
|
367
|
+
};
|
|
368
|
+
parseYamlContent = (content, uri) => effect.Effect.gen(function* () {
|
|
369
|
+
const parsed = yield* effect.Effect.try({
|
|
370
|
+
try: () => yaml.parse(content),
|
|
371
|
+
catch: (error) => new ConfigValidationError({
|
|
372
|
+
reason: uri ? `Failed to parse YAML from ${uri}` : "Failed to parse YAML",
|
|
404
373
|
cause: error
|
|
405
|
-
});
|
|
406
|
-
})
|
|
407
|
-
);
|
|
408
|
-
if (response.status >= 400) {
|
|
409
|
-
return yield* effect.Effect.fail(
|
|
410
|
-
new ConfigUrlError({
|
|
411
|
-
reason: `HTTP ${response.status} from ${url}`
|
|
412
374
|
})
|
|
413
|
-
);
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
reason: `Failed to read response body from ${url}`,
|
|
375
|
+
});
|
|
376
|
+
return yield* effect.Effect.try({
|
|
377
|
+
try: () => InstrumentationConfigSchema.parse(parsed),
|
|
378
|
+
catch: (error) => new ConfigValidationError({
|
|
379
|
+
reason: uri ? `Invalid configuration schema from ${uri}` : "Invalid configuration schema",
|
|
419
380
|
cause: error
|
|
420
381
|
})
|
|
421
|
-
)
|
|
422
|
-
);
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
loadFromFileWithFs = (fs2, path3, uri) => effect.Effect.gen(function* () {
|
|
385
|
+
const content = yield* fs2.readFileString(path3).pipe(
|
|
386
|
+
effect.Effect.mapError(
|
|
387
|
+
(error) => new ConfigFileError({
|
|
388
|
+
reason: `Failed to read config file at ${uri}`,
|
|
389
|
+
cause: error
|
|
390
|
+
})
|
|
391
|
+
)
|
|
428
392
|
);
|
|
429
|
-
|
|
430
|
-
return yield* parseYamlContent(text, url);
|
|
431
|
-
})
|
|
432
|
-
);
|
|
433
|
-
var makeConfigLoader = effect.Effect.gen(function* () {
|
|
434
|
-
const fs3 = yield* effect.Effect.serviceOption(FileSystem.FileSystem);
|
|
435
|
-
const http = yield* HttpClient__namespace.HttpClient;
|
|
436
|
-
const loadFromUriUncached = (uri) => effect.Effect.gen(function* () {
|
|
437
|
-
if (uri.startsWith("file://")) {
|
|
438
|
-
const path3 = uri.slice(7);
|
|
439
|
-
if (fs3._tag === "None") {
|
|
393
|
+
if (content.length > SECURITY_DEFAULTS.maxConfigSize) {
|
|
440
394
|
return yield* effect.Effect.fail(
|
|
441
395
|
new ConfigFileError({
|
|
442
|
-
reason:
|
|
443
|
-
cause: { uri }
|
|
396
|
+
reason: `Config file exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
|
|
444
397
|
})
|
|
445
398
|
);
|
|
446
399
|
}
|
|
447
|
-
return yield*
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
400
|
+
return yield* parseYamlContent(content, uri);
|
|
401
|
+
});
|
|
402
|
+
loadFromHttpWithClient = (client, url) => effect.Effect.scoped(
|
|
403
|
+
effect.Effect.gen(function* () {
|
|
404
|
+
if (url.startsWith("http://")) {
|
|
405
|
+
return yield* effect.Effect.fail(
|
|
406
|
+
new ConfigUrlError({
|
|
407
|
+
reason: "Insecure protocol: only HTTPS URLs are allowed"
|
|
408
|
+
})
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
const request = HttpClientRequest__namespace.get(url).pipe(
|
|
412
|
+
HttpClientRequest__namespace.setHeaders({
|
|
413
|
+
Accept: "application/yaml, text/yaml, application/x-yaml"
|
|
414
|
+
})
|
|
415
|
+
);
|
|
416
|
+
const response = yield* client.execute(request).pipe(
|
|
417
|
+
effect.Effect.timeout(`${SECURITY_DEFAULTS.requestTimeout} millis`),
|
|
418
|
+
effect.Effect.mapError((error) => {
|
|
419
|
+
if (error._tag === "TimeoutException") {
|
|
420
|
+
return new ConfigUrlError({
|
|
421
|
+
reason: `Config fetch timeout after ${SECURITY_DEFAULTS.requestTimeout}ms from ${url}`
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
return new ConfigUrlError({
|
|
425
|
+
reason: `Failed to load config from URL: ${url}`,
|
|
426
|
+
cause: error
|
|
427
|
+
});
|
|
428
|
+
})
|
|
429
|
+
);
|
|
430
|
+
if (response.status >= 400) {
|
|
431
|
+
return yield* effect.Effect.fail(
|
|
432
|
+
new ConfigUrlError({
|
|
433
|
+
reason: `HTTP ${response.status} from ${url}`
|
|
434
|
+
})
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
const text = yield* response.text.pipe(
|
|
438
|
+
effect.Effect.mapError(
|
|
439
|
+
(error) => new ConfigUrlError({
|
|
440
|
+
reason: `Failed to read response body from ${url}`,
|
|
441
|
+
cause: error
|
|
442
|
+
})
|
|
443
|
+
)
|
|
444
|
+
);
|
|
445
|
+
if (text.length > SECURITY_DEFAULTS.maxConfigSize) {
|
|
446
|
+
return yield* effect.Effect.fail(
|
|
447
|
+
new ConfigUrlError({
|
|
448
|
+
reason: `Config exceeds maximum size of ${SECURITY_DEFAULTS.maxConfigSize} bytes`
|
|
449
|
+
})
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
return yield* parseYamlContent(text, url);
|
|
453
|
+
})
|
|
454
|
+
);
|
|
455
|
+
makeConfigLoader = effect.Effect.gen(function* () {
|
|
456
|
+
const fs2 = yield* effect.Effect.serviceOption(FileSystem.FileSystem);
|
|
457
|
+
const http = yield* HttpClient__namespace.HttpClient;
|
|
458
|
+
const loadFromUriUncached = (uri) => effect.Effect.gen(function* () {
|
|
459
|
+
if (uri.startsWith("file://")) {
|
|
460
|
+
const path3 = uri.slice(7);
|
|
461
|
+
if (fs2._tag === "None") {
|
|
462
|
+
return yield* effect.Effect.fail(
|
|
463
|
+
new ConfigFileError({
|
|
464
|
+
reason: "FileSystem not available (browser environment?)",
|
|
465
|
+
cause: { uri }
|
|
466
|
+
})
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
return yield* loadFromFileWithFs(fs2.value, path3, uri);
|
|
470
|
+
}
|
|
471
|
+
if (uri.startsWith("http://") || uri.startsWith("https://")) {
|
|
472
|
+
return yield* loadFromHttpWithClient(http, uri);
|
|
473
|
+
}
|
|
474
|
+
if (fs2._tag === "Some") {
|
|
475
|
+
return yield* loadFromFileWithFs(fs2.value, uri, uri);
|
|
476
|
+
} else {
|
|
477
|
+
return yield* loadFromHttpWithClient(http, uri);
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
const loadFromUriCached = yield* effect.Effect.cachedFunction(loadFromUriUncached);
|
|
481
|
+
return ConfigLoader.of({
|
|
482
|
+
loadFromUri: loadFromUriCached,
|
|
483
|
+
loadFromInline: (content) => effect.Effect.gen(function* () {
|
|
484
|
+
if (typeof content === "string") {
|
|
485
|
+
return yield* parseYamlContent(content);
|
|
486
|
+
}
|
|
487
|
+
return yield* effect.Effect.try({
|
|
488
|
+
try: () => InstrumentationConfigSchema.parse(content),
|
|
489
|
+
catch: (error) => new ConfigValidationError({
|
|
490
|
+
reason: "Invalid configuration schema",
|
|
491
|
+
cause: error
|
|
492
|
+
})
|
|
493
|
+
});
|
|
470
494
|
})
|
|
471
495
|
});
|
|
472
|
-
})
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
496
|
+
});
|
|
497
|
+
effect.Layer.effect(ConfigLoader, makeConfigLoader);
|
|
498
|
+
Logger = class {
|
|
499
|
+
constructor() {
|
|
500
|
+
__publicField2(this, "level", "on");
|
|
501
|
+
__publicField2(this, "hasLoggedMinimal", false);
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Set the logging level
|
|
505
|
+
*/
|
|
506
|
+
setLevel(level) {
|
|
507
|
+
this.level = level;
|
|
508
|
+
this.hasLoggedMinimal = false;
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Get the current logging level
|
|
512
|
+
*/
|
|
513
|
+
getLevel() {
|
|
514
|
+
return this.level;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Log a minimal initialization message (only shown once in minimal mode)
|
|
518
|
+
*/
|
|
519
|
+
minimal(message) {
|
|
520
|
+
if (this.level === "off") {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
if (this.level === "minimal" && !this.hasLoggedMinimal) {
|
|
524
|
+
console.log(message);
|
|
525
|
+
this.hasLoggedMinimal = true;
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
if (this.level === "on") {
|
|
529
|
+
console.log(message);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Log an informational message
|
|
534
|
+
*/
|
|
535
|
+
log(...args) {
|
|
536
|
+
if (this.level === "on") {
|
|
537
|
+
console.log(...args);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Log a warning message (shown in minimal mode)
|
|
542
|
+
*/
|
|
543
|
+
warn(...args) {
|
|
544
|
+
if (this.level !== "off") {
|
|
545
|
+
console.warn(...args);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Log an error message (shown in minimal mode)
|
|
550
|
+
*/
|
|
551
|
+
error(...args) {
|
|
552
|
+
if (this.level !== "off") {
|
|
553
|
+
console.error(...args);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Check if full logging is enabled
|
|
558
|
+
*/
|
|
559
|
+
isEnabled() {
|
|
560
|
+
return this.level === "on";
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Check if minimal logging is enabled
|
|
564
|
+
*/
|
|
565
|
+
isMinimal() {
|
|
566
|
+
return this.level === "minimal";
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Check if logging is completely disabled
|
|
570
|
+
*/
|
|
571
|
+
isDisabled() {
|
|
572
|
+
return this.level === "off";
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
logger = new Logger();
|
|
551
576
|
}
|
|
552
|
-
};
|
|
553
|
-
var logger = new Logger();
|
|
577
|
+
});
|
|
554
578
|
async function loadFromFile(filePath) {
|
|
555
579
|
const { readFile } = await import('fs/promises');
|
|
556
580
|
const content = await readFile(filePath, "utf-8");
|
|
@@ -591,170 +615,589 @@ async function loadConfigWithOptions(options = {}) {
|
|
|
591
615
|
if (options.configPath) {
|
|
592
616
|
return loadConfig(options.configPath);
|
|
593
617
|
}
|
|
594
|
-
const { existsSync:
|
|
618
|
+
const { existsSync: existsSync2 } = await import('fs');
|
|
595
619
|
const { join: join2 } = await import('path');
|
|
596
620
|
const defaultPath = join2(process.cwd(), "instrumentation.yaml");
|
|
597
|
-
if (
|
|
621
|
+
if (existsSync2(defaultPath)) {
|
|
598
622
|
return loadConfig(defaultPath);
|
|
599
623
|
}
|
|
600
624
|
return defaultConfig;
|
|
601
625
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
enabled: false,
|
|
606
|
-
granularity: "fiber",
|
|
607
|
-
span_naming: {
|
|
608
|
-
default: "effect.fiber.{fiber_id}",
|
|
609
|
-
infer_from_source: true,
|
|
610
|
-
rules: []
|
|
611
|
-
},
|
|
612
|
-
span_relationships: {
|
|
613
|
-
type: "parent-child"
|
|
614
|
-
},
|
|
615
|
-
filter: {
|
|
616
|
-
include: [],
|
|
617
|
-
exclude: []
|
|
618
|
-
},
|
|
619
|
-
performance: {
|
|
620
|
-
sampling_rate: 1,
|
|
621
|
-
min_duration: "0ms",
|
|
622
|
-
max_concurrent: 0
|
|
623
|
-
},
|
|
624
|
-
metadata: {
|
|
625
|
-
fiber_info: true,
|
|
626
|
-
source_location: true,
|
|
627
|
-
parent_fiber: true
|
|
628
|
-
}
|
|
629
|
-
};
|
|
630
|
-
var AutoTracingConfig = class extends effect.Context.Tag("AutoTracingConfig")() {
|
|
631
|
-
};
|
|
632
|
-
var loadAutoTracingConfig = (options) => effect.Effect.gen(function* () {
|
|
633
|
-
const config = yield* effect.Effect.tryPromise({
|
|
634
|
-
try: () => loadConfigWithOptions(options),
|
|
635
|
-
catch: (error) => {
|
|
636
|
-
logger.log(`@atrim/auto-trace: Failed to load config: ${error}`);
|
|
637
|
-
return error;
|
|
638
|
-
}
|
|
639
|
-
}).pipe(effect.Effect.catchAll(() => effect.Effect.succeed(null)));
|
|
640
|
-
if (!config) {
|
|
641
|
-
logger.log("@atrim/auto-trace: No config found, using defaults");
|
|
642
|
-
return defaultAutoTracingConfig;
|
|
626
|
+
var init_config_loader = __esm({
|
|
627
|
+
"src/core/config-loader.ts"() {
|
|
628
|
+
init_dist();
|
|
643
629
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
630
|
+
});
|
|
631
|
+
exports.defaultAutoTracingConfig = void 0; exports.AutoTracingConfig = void 0; exports.loadAutoTracingConfig = void 0; exports.loadAutoTracingConfigSync = void 0; exports.AutoTracingConfigLive = void 0; exports.AutoTracingConfigLayer = void 0; exports.loadFullConfigSync = void 0;
|
|
632
|
+
var init_config = __esm({
|
|
633
|
+
"src/integrations/effect/auto/config.ts"() {
|
|
634
|
+
init_dist();
|
|
635
|
+
init_config_loader();
|
|
636
|
+
exports.defaultAutoTracingConfig = {
|
|
637
|
+
enabled: false,
|
|
638
|
+
granularity: "fiber",
|
|
639
|
+
span_naming: {
|
|
640
|
+
default: "effect.fiber.{fiber_id}",
|
|
641
|
+
infer_from_source: true,
|
|
642
|
+
rules: []
|
|
643
|
+
},
|
|
644
|
+
span_relationships: {
|
|
645
|
+
type: "parent-child"
|
|
646
|
+
},
|
|
647
|
+
filter: {
|
|
648
|
+
include: [],
|
|
649
|
+
exclude: []
|
|
650
|
+
},
|
|
651
|
+
performance: {
|
|
652
|
+
sampling_rate: 1,
|
|
653
|
+
min_duration: "0ms",
|
|
654
|
+
max_concurrent: 0
|
|
655
|
+
},
|
|
656
|
+
metadata: {
|
|
657
|
+
fiber_info: true,
|
|
658
|
+
source_location: true,
|
|
659
|
+
parent_fiber: true
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
exports.AutoTracingConfig = class extends effect.Context.Tag("AutoTracingConfig")() {
|
|
663
|
+
};
|
|
664
|
+
exports.loadAutoTracingConfig = (options) => effect.Effect.gen(function* () {
|
|
665
|
+
const config = yield* effect.Effect.tryPromise({
|
|
666
|
+
try: () => loadConfigWithOptions(options),
|
|
667
|
+
catch: (error) => {
|
|
668
|
+
logger.log(`@atrim/auto-trace: Failed to load config: ${error}`);
|
|
669
|
+
return error;
|
|
670
|
+
}
|
|
671
|
+
}).pipe(effect.Effect.catchAll(() => effect.Effect.succeed(null)));
|
|
672
|
+
if (!config) {
|
|
673
|
+
logger.log("@atrim/auto-trace: No config found, using defaults");
|
|
674
|
+
return exports.defaultAutoTracingConfig;
|
|
675
|
+
}
|
|
676
|
+
const autoConfig = config.effect?.auto_instrumentation;
|
|
677
|
+
if (!autoConfig) {
|
|
678
|
+
logger.log("@atrim/auto-trace: No auto_instrumentation config, using defaults");
|
|
679
|
+
return exports.defaultAutoTracingConfig;
|
|
680
|
+
}
|
|
681
|
+
const parsed = AutoInstrumentationConfigSchema.safeParse(autoConfig);
|
|
682
|
+
if (!parsed.success) {
|
|
683
|
+
logger.log(`@atrim/auto-trace: Invalid config, using defaults: ${parsed.error.message}`);
|
|
684
|
+
return exports.defaultAutoTracingConfig;
|
|
685
|
+
}
|
|
686
|
+
logger.log("@atrim/auto-trace: Loaded config from instrumentation.yaml");
|
|
687
|
+
return parsed.data;
|
|
688
|
+
});
|
|
689
|
+
exports.loadAutoTracingConfigSync = () => {
|
|
690
|
+
return exports.defaultAutoTracingConfig;
|
|
691
|
+
};
|
|
692
|
+
exports.AutoTracingConfigLive = effect.Layer.effect(exports.AutoTracingConfig, exports.loadAutoTracingConfig());
|
|
693
|
+
exports.AutoTracingConfigLayer = (config) => effect.Layer.succeed(exports.AutoTracingConfig, config);
|
|
694
|
+
exports.loadFullConfigSync = () => {
|
|
695
|
+
const defaultPath = path2__namespace.join(process.cwd(), "instrumentation.yaml");
|
|
696
|
+
try {
|
|
697
|
+
if (fs__namespace.existsSync(defaultPath)) {
|
|
698
|
+
const content = fs__namespace.readFileSync(defaultPath, "utf-8");
|
|
699
|
+
const parsed = yaml__namespace.parse(content);
|
|
700
|
+
const result = InstrumentationConfigSchema.safeParse(parsed);
|
|
701
|
+
if (result.success) {
|
|
702
|
+
logger.log(`@atrim/auto-trace: Loaded config from ${defaultPath}`);
|
|
703
|
+
return result.data;
|
|
704
|
+
} else {
|
|
705
|
+
logger.log(`@atrim/auto-trace: Invalid config, using defaults: ${result.error.message}`);
|
|
706
|
+
return defaultConfig;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
} catch (error) {
|
|
710
|
+
logger.log(`@atrim/auto-trace: Failed to load config: ${error}`);
|
|
711
|
+
}
|
|
712
|
+
logger.log("@atrim/auto-trace: No config found, using defaults");
|
|
713
|
+
return defaultConfig;
|
|
714
|
+
};
|
|
653
715
|
}
|
|
654
|
-
logger.log("@atrim/auto-trace: Loaded config from instrumentation.yaml");
|
|
655
|
-
return parsed.data;
|
|
656
716
|
});
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
try: () => loadConfigWithOptions(options),
|
|
665
|
-
catch: (error) => {
|
|
666
|
-
logger.log(`@atrim/auto-trace: Failed to load config: ${error}`);
|
|
667
|
-
return error;
|
|
668
|
-
}
|
|
669
|
-
}).pipe(effect.Effect.catchAll(() => effect.Effect.succeed(null)));
|
|
670
|
-
if (!config) {
|
|
671
|
-
logger.log("@atrim/auto-trace: No config found, using defaults");
|
|
672
|
-
return defaultConfig;
|
|
717
|
+
exports.AutoTracingEnabled = void 0; exports.AutoTracingSpanName = void 0; exports.withoutAutoTracing = void 0; exports.setSpanName = void 0;
|
|
718
|
+
var init_span_control = __esm({
|
|
719
|
+
"src/integrations/effect/auto/span-control.ts"() {
|
|
720
|
+
exports.AutoTracingEnabled = effect.FiberRef.unsafeMake(true);
|
|
721
|
+
exports.AutoTracingSpanName = effect.FiberRef.unsafeMake(effect.Option.none());
|
|
722
|
+
exports.withoutAutoTracing = (effect$1) => effect$1.pipe(effect.Effect.locally(exports.AutoTracingEnabled, false));
|
|
723
|
+
exports.setSpanName = (name) => (effect$1) => effect$1.pipe(effect.Effect.locally(exports.AutoTracingSpanName, effect.Option.some(name)));
|
|
673
724
|
}
|
|
674
|
-
return config;
|
|
675
725
|
});
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
726
|
+
|
|
727
|
+
// src/integrations/effect/auto/unified-tracing-supervisor.ts
|
|
728
|
+
var unified_tracing_supervisor_exports = {};
|
|
729
|
+
__export(unified_tracing_supervisor_exports, {
|
|
730
|
+
AutoTracingEnabled: () => exports.AutoTracingEnabled,
|
|
731
|
+
AutoTracingSpanName: () => exports.AutoTracingSpanName,
|
|
732
|
+
UnifiedTracingLive: () => exports.UnifiedTracingLive,
|
|
733
|
+
UnifiedTracingSupervisor: () => exports.UnifiedTracingSupervisor,
|
|
734
|
+
createUnifiedTracingLayer: () => exports.createUnifiedTracingLayer,
|
|
735
|
+
enableOpSupervision: () => exports.enableOpSupervision,
|
|
736
|
+
flushAndShutdown: () => exports.flushAndShutdown,
|
|
737
|
+
forceFlush: () => exports.forceFlush,
|
|
738
|
+
setSpanName: () => exports.setSpanName,
|
|
739
|
+
withAutoTracing: () => exports.withAutoTracing,
|
|
740
|
+
withUnifiedTracing: () => exports.withUnifiedTracing,
|
|
741
|
+
withoutAutoTracing: () => exports.withoutAutoTracing
|
|
742
|
+
});
|
|
743
|
+
function getOperationMeta(effect) {
|
|
744
|
+
const trace2 = effect.trace;
|
|
745
|
+
if (trace2 && trace2._tag === "OperationMeta") {
|
|
746
|
+
return trace2;
|
|
693
747
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
Error.stackTraceLimit = 10;
|
|
701
|
-
const error = new Error();
|
|
702
|
-
Error.stackTraceLimit = originalLimit;
|
|
703
|
-
if (!error.stack) return void 0;
|
|
704
|
-
const lines = error.stack.split("\n");
|
|
705
|
-
const startIndex = 3 + skipFrames;
|
|
706
|
-
for (let i = startIndex; i < lines.length; i++) {
|
|
707
|
-
const line = lines[i];
|
|
708
|
-
if (line === void 0) continue;
|
|
709
|
-
if (line.includes("@atrim/instrument") || line.includes("node_modules/effect")) {
|
|
748
|
+
return void 0;
|
|
749
|
+
}
|
|
750
|
+
function parseSourceLocation(stack) {
|
|
751
|
+
const lines = stack.split("\n");
|
|
752
|
+
for (const line of lines.slice(1)) {
|
|
753
|
+
if (line.includes("node_modules") || line.includes("/effect/") || line.includes("fiberRuntime.ts") || line.includes("core.ts")) {
|
|
710
754
|
continue;
|
|
711
755
|
}
|
|
712
|
-
const match = line.match(/at\s+(
|
|
713
|
-
if (match) {
|
|
714
|
-
const [, funcName, filePath, lineNum, colNum] = match;
|
|
756
|
+
const match = line.match(/at\s+(?:.*?\s+\()?(.+):(\d+):(\d+)\)?/);
|
|
757
|
+
if (match && match[1] && match[2]) {
|
|
715
758
|
return {
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
column: parseInt(colNum ?? "0", 10)
|
|
759
|
+
file: match[1],
|
|
760
|
+
line: parseInt(match[2], 10),
|
|
761
|
+
...match[3] ? { column: parseInt(match[3], 10) } : {}
|
|
720
762
|
};
|
|
721
763
|
}
|
|
722
764
|
}
|
|
723
765
|
return void 0;
|
|
724
766
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
767
|
+
function makeSourceKey(source) {
|
|
768
|
+
if (!source) return "unknown";
|
|
769
|
+
return `${source.file}:${source.line}`;
|
|
770
|
+
}
|
|
771
|
+
function formatLocation(source) {
|
|
772
|
+
if (!source) return "unknown";
|
|
773
|
+
const filename = source.file.split("/").pop() ?? source.file;
|
|
774
|
+
return `${filename}:${source.line}`;
|
|
775
|
+
}
|
|
776
|
+
function getGlobalProviderSetup() {
|
|
777
|
+
if (_globalProviderSetup === void 0) {
|
|
778
|
+
_globalProviderSetup = setupGlobalTracerProvider();
|
|
736
779
|
}
|
|
737
|
-
return
|
|
738
|
-
effect.Effect.zipRight(effect.Effect.forkDaemon(effect$1))
|
|
739
|
-
);
|
|
740
|
-
};
|
|
741
|
-
|
|
742
|
-
// src/integrations/effect/auto/patch-fork.ts
|
|
743
|
-
var patchAttempted = false;
|
|
744
|
-
function isEffectForkPatched() {
|
|
745
|
-
return false;
|
|
780
|
+
return _globalProviderSetup;
|
|
746
781
|
}
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
782
|
+
var currentSourceLocation; exports.UnifiedTracingSupervisor = void 0; var setupGlobalTracerProvider, _globalProviderSetup; exports.createUnifiedTracingLayer = void 0; exports.UnifiedTracingLive = void 0; exports.enableOpSupervision = void 0; exports.withUnifiedTracing = void 0; exports.withAutoTracing = void 0; exports.flushAndShutdown = void 0; exports.forceFlush = void 0;
|
|
783
|
+
var init_unified_tracing_supervisor = __esm({
|
|
784
|
+
"src/integrations/effect/auto/unified-tracing-supervisor.ts"() {
|
|
785
|
+
init_dist();
|
|
786
|
+
init_config();
|
|
787
|
+
init_span_control();
|
|
788
|
+
init_span_control();
|
|
789
|
+
currentSourceLocation = effect.GlobalValue.globalValue(
|
|
790
|
+
/* @__PURE__ */ Symbol.for("effect/FiberRef/currentSourceLocation"),
|
|
791
|
+
() => effect.FiberRef.unsafeMake(void 0)
|
|
792
|
+
);
|
|
793
|
+
exports.UnifiedTracingSupervisor = class extends effect.Supervisor.AbstractSupervisor {
|
|
794
|
+
constructor(config) {
|
|
795
|
+
super();
|
|
796
|
+
this.config = config;
|
|
797
|
+
// ========== Fork span registry ==========
|
|
798
|
+
// Maps sourceKey -> pending fork span (for correlating with child fibers)
|
|
799
|
+
__publicField(this, "pendingForkSpans", /* @__PURE__ */ new Map());
|
|
800
|
+
// ========== Fiber tracking ==========
|
|
801
|
+
__publicField(this, "fiberSpans", /* @__PURE__ */ new WeakMap());
|
|
802
|
+
__publicField(this, "fiberContexts", /* @__PURE__ */ new WeakMap());
|
|
803
|
+
__publicField(this, "fiberStartTimes", /* @__PURE__ */ new WeakMap());
|
|
804
|
+
// ========== Operation tracking ==========
|
|
805
|
+
__publicField(this, "processedEffects", /* @__PURE__ */ new WeakSet());
|
|
806
|
+
__publicField(this, "configuredOps");
|
|
807
|
+
// ========== OTel ==========
|
|
808
|
+
__publicField(this, "_tracer", null);
|
|
809
|
+
__publicField(this, "activeFiberCount", 0);
|
|
810
|
+
const defaultOps = [
|
|
811
|
+
{ name: "all", includeCount: true, includeStack: true },
|
|
812
|
+
{ name: "forEach", includeCount: true, includeStack: true },
|
|
813
|
+
{ name: "fork", includeStack: true }
|
|
814
|
+
];
|
|
815
|
+
this.configuredOps = new Map(defaultOps.map((op) => [op.name, op]));
|
|
816
|
+
logger.log("@atrim/unified-tracing: Supervisor initialized");
|
|
817
|
+
logger.log(` Operations: ${Array.from(this.configuredOps.keys()).join(", ")}`);
|
|
818
|
+
}
|
|
819
|
+
get tracer() {
|
|
820
|
+
if (!this._tracer) {
|
|
821
|
+
this._tracer = OtelApi__namespace.trace.getTracer("@atrim/unified-tracing", "1.0.0");
|
|
822
|
+
}
|
|
823
|
+
return this._tracer;
|
|
824
|
+
}
|
|
825
|
+
get value() {
|
|
826
|
+
return effect.Effect.void;
|
|
827
|
+
}
|
|
828
|
+
// ==========================================================================
|
|
829
|
+
// onEffect - Operation-level tracing (Effect.all, Effect.forEach, Effect.fork)
|
|
830
|
+
// ==========================================================================
|
|
831
|
+
onEffect(fiber, effect) {
|
|
832
|
+
if (this.processedEffects.has(effect)) {
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
const meta = getOperationMeta(effect);
|
|
836
|
+
if (!meta) {
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
const opConfig = this.configuredOps.get(meta.op);
|
|
840
|
+
if (!opConfig) {
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
this.processedEffects.add(effect);
|
|
844
|
+
const fiberId = fiber.id().id;
|
|
845
|
+
const location = parseSourceLocation(meta.capturedAt);
|
|
846
|
+
const sourceKey = makeSourceKey(location);
|
|
847
|
+
logger.log(`@atrim/unified-tracing: onEffect "${meta.op}" in fiber ${fiberId} at ${sourceKey}`);
|
|
848
|
+
const parentContext = this.resolveParentContext(fiber);
|
|
849
|
+
const spanName = location ? `effect.${meta.op} (${formatLocation(location)})` : `effect.${meta.op}`;
|
|
850
|
+
const span = this.tracer.startSpan(spanName, { kind: OtelApi__namespace.SpanKind.INTERNAL }, parentContext);
|
|
851
|
+
span.setAttribute("effect.operation", meta.op);
|
|
852
|
+
if (opConfig.includeCount && meta.count !== void 0) {
|
|
853
|
+
span.setAttribute("effect.item_count", meta.count);
|
|
854
|
+
}
|
|
855
|
+
if (opConfig.includeStack && location) {
|
|
856
|
+
span.setAttribute("code.filepath", location.file);
|
|
857
|
+
span.setAttribute("code.lineno", location.line);
|
|
858
|
+
if (location.column !== void 0) {
|
|
859
|
+
span.setAttribute("code.column", location.column);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
logger.log(
|
|
863
|
+
`@atrim/unified-tracing: Created operation span "${spanName}" spanId=${span.spanContext().spanId}`
|
|
864
|
+
);
|
|
865
|
+
if (meta.op === "fork") {
|
|
866
|
+
const forkContext = OtelApi__namespace.trace.setSpan(parentContext, span);
|
|
867
|
+
this.pendingForkSpans.set(sourceKey, {
|
|
868
|
+
forkSpan: span,
|
|
869
|
+
forkContext,
|
|
870
|
+
timestamp: process.hrtime.bigint(),
|
|
871
|
+
spanEnded: false
|
|
872
|
+
});
|
|
873
|
+
logger.log(`@atrim/unified-tracing: Registered pending fork at ${sourceKey}`);
|
|
874
|
+
} else {
|
|
875
|
+
span.setStatus({ code: OtelApi__namespace.SpanStatusCode.OK });
|
|
876
|
+
span.end();
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
// ==========================================================================
|
|
880
|
+
// onStart - Fiber-level tracing
|
|
881
|
+
// ==========================================================================
|
|
882
|
+
onStart(_context, _effect, parent, fiber) {
|
|
883
|
+
const fiberId = fiber.id().id;
|
|
884
|
+
const fiberRefs = fiber.getFiberRefs();
|
|
885
|
+
const enabled = effect.FiberRefs.getOrDefault(fiberRefs, exports.AutoTracingEnabled);
|
|
886
|
+
if (!enabled) {
|
|
887
|
+
logger.log(`@atrim/unified-tracing: Auto-tracing disabled for fiber ${fiberId}`);
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
const samplingRate = this.config.performance?.sampling_rate ?? 1;
|
|
891
|
+
if (samplingRate < 1 && Math.random() > samplingRate) {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
const nameOverride = effect.FiberRefs.getOrDefault(fiberRefs, exports.AutoTracingSpanName);
|
|
895
|
+
const sourceLocation = effect.FiberRefs.getOrDefault(fiberRefs, currentSourceLocation);
|
|
896
|
+
const sourceKey = makeSourceKey(sourceLocation);
|
|
897
|
+
logger.log(`@atrim/unified-tracing: onStart fiber ${fiberId} at ${sourceKey}`);
|
|
898
|
+
let parentContext;
|
|
899
|
+
const pendingFork = this.pendingForkSpans.get(sourceKey);
|
|
900
|
+
if (pendingFork) {
|
|
901
|
+
parentContext = pendingFork.forkContext;
|
|
902
|
+
logger.log(
|
|
903
|
+
`@atrim/unified-tracing: Matched fiber ${fiberId} to fork span at ${sourceKey} - using fork span as parent`
|
|
904
|
+
);
|
|
905
|
+
if (!pendingFork.spanEnded) {
|
|
906
|
+
pendingFork.forkSpan.setStatus({ code: OtelApi__namespace.SpanStatusCode.OK });
|
|
907
|
+
pendingFork.forkSpan.end();
|
|
908
|
+
logger.log(`@atrim/unified-tracing: Ended fork span at ${sourceKey}`);
|
|
909
|
+
}
|
|
910
|
+
this.pendingForkSpans.delete(sourceKey);
|
|
911
|
+
} else {
|
|
912
|
+
parentContext = this.resolveParentContextFromFiberRefs(
|
|
913
|
+
fiberRefs,
|
|
914
|
+
_context,
|
|
915
|
+
parent
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
let spanName;
|
|
919
|
+
if (effect.Option.isSome(nameOverride)) {
|
|
920
|
+
spanName = nameOverride.value;
|
|
921
|
+
} else if (sourceLocation) {
|
|
922
|
+
spanName = `effect.fiber (${formatLocation(sourceLocation)})`;
|
|
923
|
+
} else {
|
|
924
|
+
spanName = `effect.fiber-${fiberId}`;
|
|
925
|
+
}
|
|
926
|
+
const span = this.tracer.startSpan(spanName, { kind: OtelApi__namespace.SpanKind.INTERNAL }, parentContext);
|
|
927
|
+
span.setAttribute("effect.auto_traced", true);
|
|
928
|
+
span.setAttribute("effect.fiber.id", fiberId);
|
|
929
|
+
if (sourceLocation) {
|
|
930
|
+
span.setAttribute("code.filepath", sourceLocation.file);
|
|
931
|
+
span.setAttribute("code.lineno", sourceLocation.line);
|
|
932
|
+
if (sourceLocation.column !== void 0) {
|
|
933
|
+
span.setAttribute("code.column", sourceLocation.column);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
if (effect.Option.isSome(parent)) {
|
|
937
|
+
span.setAttribute("effect.fiber.parent_id", parent.value.id().id);
|
|
938
|
+
}
|
|
939
|
+
logger.log(
|
|
940
|
+
`@atrim/unified-tracing: Created fiber span "${spanName}" spanId=${span.spanContext().spanId}`
|
|
941
|
+
);
|
|
942
|
+
const newContext = OtelApi__namespace.trace.setSpan(parentContext, span);
|
|
943
|
+
this.fiberSpans.set(fiber, span);
|
|
944
|
+
this.fiberContexts.set(fiber, newContext);
|
|
945
|
+
this.fiberStartTimes.set(fiber, process.hrtime.bigint());
|
|
946
|
+
this.activeFiberCount++;
|
|
947
|
+
}
|
|
948
|
+
// ==========================================================================
|
|
949
|
+
// onEnd - Fiber completion
|
|
950
|
+
// ==========================================================================
|
|
951
|
+
onEnd(exit, fiber) {
|
|
952
|
+
const fiberId = fiber.id().id;
|
|
953
|
+
const span = this.fiberSpans.get(fiber);
|
|
954
|
+
if (!span) {
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
const startTime = this.fiberStartTimes.get(fiber);
|
|
958
|
+
if (startTime) {
|
|
959
|
+
const duration = process.hrtime.bigint() - startTime;
|
|
960
|
+
const minDuration = this.parseMinDuration(this.config.performance?.min_duration);
|
|
961
|
+
if (minDuration > 0 && duration < minDuration) {
|
|
962
|
+
this.cleanup(fiber);
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
if (effect.Exit.isSuccess(exit)) {
|
|
967
|
+
span.setStatus({ code: OtelApi__namespace.SpanStatusCode.OK });
|
|
968
|
+
} else {
|
|
969
|
+
span.setStatus({ code: OtelApi__namespace.SpanStatusCode.ERROR, message: "Fiber failed" });
|
|
970
|
+
span.setAttribute("effect.fiber.failed", true);
|
|
971
|
+
}
|
|
972
|
+
span.end();
|
|
973
|
+
logger.log(`@atrim/unified-tracing: Ended fiber span for fiber ${fiberId}`);
|
|
974
|
+
this.cleanup(fiber);
|
|
975
|
+
}
|
|
976
|
+
// ==========================================================================
|
|
977
|
+
// Helpers
|
|
978
|
+
// ==========================================================================
|
|
979
|
+
cleanup(fiber) {
|
|
980
|
+
this.fiberSpans.delete(fiber);
|
|
981
|
+
this.fiberContexts.delete(fiber);
|
|
982
|
+
this.fiberStartTimes.delete(fiber);
|
|
983
|
+
this.activeFiberCount--;
|
|
984
|
+
}
|
|
985
|
+
resolveParentContext(fiber) {
|
|
986
|
+
const fiberContext = fiber.currentContext;
|
|
987
|
+
const maybeParentSpan = effect.Context.getOption(fiberContext, effect.Tracer.ParentSpan);
|
|
988
|
+
if (effect.Option.isSome(maybeParentSpan)) {
|
|
989
|
+
const effectSpan = maybeParentSpan.value;
|
|
990
|
+
return OtelApi__namespace.trace.setSpan(
|
|
991
|
+
OtelApi__namespace.ROOT_CONTEXT,
|
|
992
|
+
OtelApi__namespace.trace.wrapSpanContext({
|
|
993
|
+
traceId: effectSpan.traceId,
|
|
994
|
+
spanId: effectSpan.spanId,
|
|
995
|
+
traceFlags: OtelApi__namespace.TraceFlags.SAMPLED
|
|
996
|
+
})
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
return OtelApi__namespace.ROOT_CONTEXT;
|
|
1000
|
+
}
|
|
1001
|
+
resolveParentContextFromFiberRefs(fiberRefs, context2, parent) {
|
|
1002
|
+
const inheritedCtx = effect.FiberRefs.getOrDefault(fiberRefs, TracerModule__namespace.currentOtelSpanContext);
|
|
1003
|
+
if (inheritedCtx) {
|
|
1004
|
+
const span = OtelApi__namespace.trace.getSpan(inheritedCtx);
|
|
1005
|
+
if (span) {
|
|
1006
|
+
logger.log(
|
|
1007
|
+
`@atrim/unified-tracing: Using inherited OTel context from FiberRef - spanId=${span.spanContext().spanId}`
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
return inheritedCtx;
|
|
1011
|
+
}
|
|
1012
|
+
const maybeParentSpan = effect.Context.getOption(context2, effect.Tracer.ParentSpan);
|
|
1013
|
+
if (effect.Option.isSome(maybeParentSpan)) {
|
|
1014
|
+
const effectSpan = maybeParentSpan.value;
|
|
1015
|
+
logger.log(
|
|
1016
|
+
`@atrim/unified-tracing: Using Effect ParentSpan - traceId=${effectSpan.traceId.slice(0, 8)}..., spanId=${effectSpan.spanId.slice(0, 8)}...`
|
|
1017
|
+
);
|
|
1018
|
+
return OtelApi__namespace.trace.setSpan(
|
|
1019
|
+
OtelApi__namespace.ROOT_CONTEXT,
|
|
1020
|
+
OtelApi__namespace.trace.wrapSpanContext({
|
|
1021
|
+
traceId: effectSpan.traceId,
|
|
1022
|
+
spanId: effectSpan.spanId,
|
|
1023
|
+
traceFlags: OtelApi__namespace.TraceFlags.SAMPLED
|
|
1024
|
+
})
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
const activeContext = OtelApi__namespace.context.active();
|
|
1028
|
+
const activeSpan = OtelApi__namespace.trace.getSpan(activeContext);
|
|
1029
|
+
if (activeSpan && activeSpan.spanContext().traceFlags === OtelApi__namespace.TraceFlags.SAMPLED) {
|
|
1030
|
+
logger.log(
|
|
1031
|
+
`@atrim/unified-tracing: Auto-bridging from active OTel context - spanId=${activeSpan.spanContext().spanId}`
|
|
1032
|
+
);
|
|
1033
|
+
return activeContext;
|
|
1034
|
+
}
|
|
1035
|
+
if (effect.Option.isSome(parent)) {
|
|
1036
|
+
const parentCtx = this.fiberContexts.get(parent.value);
|
|
1037
|
+
if (parentCtx) {
|
|
1038
|
+
logger.log(`@atrim/unified-tracing: Using parent fiber's context`);
|
|
1039
|
+
return parentCtx;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
return OtelApi__namespace.ROOT_CONTEXT;
|
|
1043
|
+
}
|
|
1044
|
+
parseMinDuration(duration) {
|
|
1045
|
+
if (!duration || duration === "0ms") return BigInt(0);
|
|
1046
|
+
const match = duration.match(/^(\d+)\s*(ms|millis|s|sec|seconds|us|micros)?$/i);
|
|
1047
|
+
if (!match) return BigInt(0);
|
|
1048
|
+
const value = parseInt(match[1] ?? "0", 10);
|
|
1049
|
+
const unit = (match[2] ?? "ms").toLowerCase();
|
|
1050
|
+
switch (unit) {
|
|
1051
|
+
case "us":
|
|
1052
|
+
case "micros":
|
|
1053
|
+
return BigInt(value) * BigInt(1e3);
|
|
1054
|
+
case "ms":
|
|
1055
|
+
case "millis":
|
|
1056
|
+
return BigInt(value) * BigInt(1e6);
|
|
1057
|
+
case "s":
|
|
1058
|
+
case "sec":
|
|
1059
|
+
case "seconds":
|
|
1060
|
+
return BigInt(value) * BigInt(1e9);
|
|
1061
|
+
default:
|
|
1062
|
+
return BigInt(value) * BigInt(1e6);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
setupGlobalTracerProvider = () => {
|
|
1067
|
+
const config = exports.loadFullConfigSync();
|
|
1068
|
+
const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
|
|
1069
|
+
const serviceVersion = process.env.npm_package_version || "1.0.0";
|
|
1070
|
+
const exporterConfig = config.effect?.exporter_config ?? {
|
|
1071
|
+
type: "otlp",
|
|
1072
|
+
processor: "batch"
|
|
1073
|
+
};
|
|
1074
|
+
logger.log("@atrim/unified-tracing: Setting up global TracerProvider");
|
|
1075
|
+
logger.log(` Service: ${serviceName}`);
|
|
1076
|
+
logger.log(` Exporter: ${exporterConfig.type}`);
|
|
1077
|
+
if (exporterConfig.type === "none") {
|
|
1078
|
+
logger.log('@atrim/unified-tracing: Exporter type is "none", skipping provider setup');
|
|
1079
|
+
return null;
|
|
1080
|
+
}
|
|
1081
|
+
let exporter;
|
|
1082
|
+
if (exporterConfig.type === "console") {
|
|
1083
|
+
exporter = new sdkTraceBase.ConsoleSpanExporter();
|
|
1084
|
+
} else {
|
|
1085
|
+
const endpoint = exporterConfig.endpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
|
|
1086
|
+
const otlpConfig = {
|
|
1087
|
+
url: `${endpoint}/v1/traces`
|
|
1088
|
+
};
|
|
1089
|
+
if (exporterConfig.headers) {
|
|
1090
|
+
otlpConfig.headers = exporterConfig.headers;
|
|
1091
|
+
}
|
|
1092
|
+
exporter = new exporterTraceOtlpHttp.OTLPTraceExporter(otlpConfig);
|
|
1093
|
+
}
|
|
1094
|
+
let spanProcessor;
|
|
1095
|
+
if (exporterConfig.processor === "simple" || exporterConfig.type === "console") {
|
|
1096
|
+
spanProcessor = new sdkTraceBase.SimpleSpanProcessor(exporter);
|
|
1097
|
+
} else {
|
|
1098
|
+
const batchConfig = exporterConfig.batch ?? {
|
|
1099
|
+
scheduled_delay_millis: 1e3,
|
|
1100
|
+
max_export_batch_size: 100
|
|
1101
|
+
};
|
|
1102
|
+
spanProcessor = new sdkTraceBase.BatchSpanProcessor(exporter, {
|
|
1103
|
+
scheduledDelayMillis: batchConfig.scheduled_delay_millis,
|
|
1104
|
+
maxExportBatchSize: batchConfig.max_export_batch_size
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
const provider = new sdkTraceBase.BasicTracerProvider({
|
|
1108
|
+
resource: resources.resourceFromAttributes({
|
|
1109
|
+
[semanticConventions.ATTR_SERVICE_NAME]: serviceName,
|
|
1110
|
+
[semanticConventions.ATTR_SERVICE_VERSION]: serviceVersion
|
|
1111
|
+
}),
|
|
1112
|
+
spanProcessors: [spanProcessor]
|
|
1113
|
+
});
|
|
1114
|
+
OtelApi__namespace.trace.setGlobalTracerProvider(provider);
|
|
1115
|
+
logger.log("@atrim/unified-tracing: Global TracerProvider registered");
|
|
1116
|
+
return { provider, spanProcessor };
|
|
1117
|
+
};
|
|
1118
|
+
exports.createUnifiedTracingLayer = () => {
|
|
1119
|
+
const config = exports.loadFullConfigSync();
|
|
1120
|
+
const autoConfig = config.effect?.auto_instrumentation ?? {
|
|
1121
|
+
enabled: true,
|
|
1122
|
+
granularity: "fiber",
|
|
1123
|
+
span_naming: { default: "effect.{function}", infer_from_source: true, rules: [] },
|
|
1124
|
+
span_relationships: { type: "parent-child" },
|
|
1125
|
+
filter: { include: [], exclude: [] },
|
|
1126
|
+
performance: { sampling_rate: 1, min_duration: "0ms", max_concurrent: 0 },
|
|
1127
|
+
metadata: { fiber_info: true, source_location: true, parent_fiber: true }
|
|
1128
|
+
};
|
|
1129
|
+
if (!getGlobalProviderSetup()) {
|
|
1130
|
+
logger.log("@atrim/unified-tracing: No TracerProvider, using empty layer");
|
|
1131
|
+
return effect.Layer.empty;
|
|
1132
|
+
}
|
|
1133
|
+
const supervisor = new exports.UnifiedTracingSupervisor(autoConfig);
|
|
1134
|
+
const supervisorLayer = effect.Supervisor.addSupervisor(supervisor);
|
|
1135
|
+
const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
|
|
1136
|
+
const serviceVersion = process.env.npm_package_version || "1.0.0";
|
|
1137
|
+
const resourceLayer = opentelemetry.Resource.layer({ serviceName, serviceVersion });
|
|
1138
|
+
const effectTracerLayer = opentelemetry.Tracer.layerGlobal;
|
|
1139
|
+
const tracerWithResource = effectTracerLayer.pipe(effect.Layer.provide(resourceLayer));
|
|
1140
|
+
const opSupervisionLayer = effect.Layer.effectDiscard(
|
|
1141
|
+
effect.Effect.patchRuntimeFlags(effect.RuntimeFlagsPatch.enable(effect.RuntimeFlags.OpSupervision))
|
|
1142
|
+
);
|
|
1143
|
+
return effect.Layer.mergeAll(
|
|
1144
|
+
effect.Layer.discard(tracerWithResource),
|
|
1145
|
+
effect.Layer.enableSourceCapture,
|
|
1146
|
+
supervisorLayer,
|
|
1147
|
+
opSupervisionLayer
|
|
1148
|
+
);
|
|
1149
|
+
};
|
|
1150
|
+
exports.UnifiedTracingLive = effect.Layer.suspend(
|
|
1151
|
+
() => exports.createUnifiedTracingLayer()
|
|
1152
|
+
);
|
|
1153
|
+
exports.enableOpSupervision = (effect$1) => effect.Effect.withRuntimeFlagsPatch(effect$1, effect.RuntimeFlagsPatch.enable(effect.RuntimeFlags.OpSupervision));
|
|
1154
|
+
exports.withUnifiedTracing = (effect$1) => effect$1.pipe(exports.enableOpSupervision, effect.Effect.provide(exports.UnifiedTracingLive));
|
|
1155
|
+
exports.withAutoTracing = (effect$1, config, rootSpanName) => {
|
|
1156
|
+
const autoConfig = config ?? {
|
|
1157
|
+
enabled: true,
|
|
1158
|
+
granularity: "fiber",
|
|
1159
|
+
span_naming: { default: "effect.{function}", infer_from_source: true, rules: [] },
|
|
1160
|
+
span_relationships: { type: "parent-child" },
|
|
1161
|
+
filter: { include: [], exclude: [] },
|
|
1162
|
+
performance: { sampling_rate: 1, min_duration: "0ms", max_concurrent: 0 },
|
|
1163
|
+
metadata: { fiber_info: true, source_location: true, parent_fiber: true }
|
|
1164
|
+
};
|
|
1165
|
+
const supervisor = new exports.UnifiedTracingSupervisor(autoConfig);
|
|
1166
|
+
const supervisorLayer = effect.Supervisor.addSupervisor(supervisor);
|
|
1167
|
+
const opSupervisionLayer = effect.Layer.effectDiscard(
|
|
1168
|
+
effect.Effect.patchRuntimeFlags(effect.RuntimeFlagsPatch.enable(effect.RuntimeFlags.OpSupervision))
|
|
1169
|
+
);
|
|
1170
|
+
const combinedLayer = effect.Layer.mergeAll(
|
|
1171
|
+
supervisorLayer,
|
|
1172
|
+
effect.Layer.enableSourceCapture,
|
|
1173
|
+
opSupervisionLayer
|
|
1174
|
+
);
|
|
1175
|
+
const wrappedEffect = rootSpanName ? effect$1.pipe(effect.Effect.withSpan(rootSpanName)) : effect$1;
|
|
1176
|
+
return wrappedEffect.pipe(effect.Effect.provide(combinedLayer));
|
|
1177
|
+
};
|
|
1178
|
+
exports.flushAndShutdown = async () => {
|
|
1179
|
+
const setup = getGlobalProviderSetup();
|
|
1180
|
+
if (setup?.provider) {
|
|
1181
|
+
logger.log("@atrim/unified-tracing: Flushing and shutting down TracerProvider...");
|
|
1182
|
+
await setup.provider.forceFlush();
|
|
1183
|
+
await setup.provider.shutdown();
|
|
1184
|
+
logger.log("@atrim/unified-tracing: TracerProvider shutdown complete");
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
exports.forceFlush = async () => {
|
|
1188
|
+
const setup = getGlobalProviderSetup();
|
|
1189
|
+
if (setup?.provider) {
|
|
1190
|
+
logger.log("@atrim/unified-tracing: Force flushing TracerProvider...");
|
|
1191
|
+
await setup.provider.forceFlush();
|
|
1192
|
+
logger.log("@atrim/unified-tracing: Force flush complete");
|
|
1193
|
+
}
|
|
1194
|
+
};
|
|
750
1195
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
patchAttempted = false;
|
|
757
|
-
}
|
|
1196
|
+
});
|
|
1197
|
+
|
|
1198
|
+
// src/integrations/effect/auto/index.ts
|
|
1199
|
+
init_unified_tracing_supervisor();
|
|
1200
|
+
init_config();
|
|
758
1201
|
var compiledRulesCache = /* @__PURE__ */ new WeakMap();
|
|
759
1202
|
function compileNamingRules(rules) {
|
|
760
1203
|
const cached = compiledRulesCache.get(rules);
|
|
@@ -875,1333 +1318,102 @@ function sanitizeSpanName(name) {
|
|
|
875
1318
|
}
|
|
876
1319
|
return sanitized;
|
|
877
1320
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
// Optional TracerProvider (if provided, use this instead of global)
|
|
893
|
-
__publicField(this, "tracerProvider", null);
|
|
894
|
-
// Compiled filter patterns
|
|
895
|
-
__publicField(this, "includePatterns");
|
|
896
|
-
__publicField(this, "excludePatterns");
|
|
897
|
-
// Active fiber count (for max_concurrent limiting)
|
|
898
|
-
__publicField(this, "activeFiberCount", 0);
|
|
899
|
-
// Root span for parent context (set by withAutoTracing)
|
|
900
|
-
__publicField(this, "_rootSpan", null);
|
|
901
|
-
if (tracerProvider) {
|
|
902
|
-
this.tracerProvider = tracerProvider;
|
|
903
|
-
logger.log("@atrim/auto-trace: Using provided TracerProvider");
|
|
904
|
-
}
|
|
905
|
-
this.includePatterns = (config.filter?.include || []).map((p) => new RegExp(p));
|
|
906
|
-
this.excludePatterns = (config.filter?.exclude || []).map((p) => new RegExp(p));
|
|
907
|
-
logger.log("@atrim/auto-trace: Supervisor initialized");
|
|
908
|
-
logger.log(` Granularity: ${config.granularity || "fiber"}`);
|
|
909
|
-
logger.log(` Sampling rate: ${config.performance?.sampling_rate ?? 1}`);
|
|
910
|
-
logger.log(` Infer from source: ${config.span_naming?.infer_from_source ?? true}`);
|
|
911
|
-
}
|
|
912
|
-
/**
|
|
913
|
-
* Set the root span for parent context propagation
|
|
914
|
-
*/
|
|
915
|
-
setRootSpan(span) {
|
|
916
|
-
this._rootSpan = span;
|
|
917
|
-
}
|
|
918
|
-
/**
|
|
919
|
-
* Get the tracer lazily - uses provided TracerProvider if available, otherwise uses global
|
|
920
|
-
*/
|
|
921
|
-
get tracer() {
|
|
922
|
-
if (!this._tracer) {
|
|
923
|
-
if (this.tracerProvider) {
|
|
924
|
-
logger.log("@atrim/auto-trace: Getting tracer from provided TracerProvider");
|
|
925
|
-
this._tracer = this.tracerProvider.getTracer("@atrim/auto-trace", "1.0.0");
|
|
926
|
-
} else {
|
|
927
|
-
logger.log("@atrim/auto-trace: Getting tracer from global API");
|
|
928
|
-
this._tracer = OtelApi__namespace.trace.getTracer("@atrim/auto-trace", "1.0.0");
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
return this._tracer;
|
|
932
|
-
}
|
|
933
|
-
/**
|
|
934
|
-
* Returns the current value (void for this supervisor)
|
|
935
|
-
*/
|
|
936
|
-
get value() {
|
|
937
|
-
return effect.Effect.void;
|
|
938
|
-
}
|
|
939
|
-
/**
|
|
940
|
-
* Called when a fiber starts executing
|
|
941
|
-
*/
|
|
942
|
-
onStart(_context, _effect, parent, fiber) {
|
|
943
|
-
logger.log(`@atrim/auto-trace: onStart called for fiber ${fiber.id().id}`);
|
|
944
|
-
const fiberRefsValue = fiber.getFiberRefs();
|
|
945
|
-
const enabled = effect.FiberRefs.getOrDefault(fiberRefsValue, AutoTracingEnabled);
|
|
946
|
-
if (!enabled) {
|
|
947
|
-
logger.log(`@atrim/auto-trace: Auto-tracing disabled for fiber ${fiber.id().id}`);
|
|
948
|
-
return;
|
|
949
|
-
}
|
|
950
|
-
const samplingRate = this.config.performance?.sampling_rate ?? 1;
|
|
951
|
-
if (samplingRate < 1 && Math.random() > samplingRate) {
|
|
952
|
-
return;
|
|
953
|
-
}
|
|
954
|
-
const maxConcurrent = this.config.performance?.max_concurrent ?? 0;
|
|
955
|
-
if (maxConcurrent > 0 && this.activeFiberCount >= maxConcurrent) {
|
|
956
|
-
return;
|
|
957
|
-
}
|
|
958
|
-
const nameOverride = effect.FiberRefs.getOrDefault(fiberRefsValue, AutoTracingSpanName);
|
|
959
|
-
let sourceInfo = effect.FiberRefs.getOrDefault(fiberRefsValue, CapturedSourceLocation);
|
|
960
|
-
if (sourceInfo) {
|
|
961
|
-
logger.log(`@atrim/auto-trace: Using captured source location for fiber ${fiber.id().id}`);
|
|
962
|
-
logger.log(` function: ${sourceInfo.function}`);
|
|
963
|
-
logger.log(` file: ${sourceInfo.file}`);
|
|
964
|
-
logger.log(` line: ${sourceInfo.line}`);
|
|
965
|
-
} else if (this.config.span_naming?.infer_from_source) {
|
|
966
|
-
sourceInfo = this.parseStackTrace();
|
|
967
|
-
if (sourceInfo) {
|
|
968
|
-
logger.log(`@atrim/auto-trace: Inferred source from stack for fiber ${fiber.id().id}`);
|
|
969
|
-
logger.log(` function: ${sourceInfo.function}`);
|
|
970
|
-
logger.log(` file: ${sourceInfo.file}`);
|
|
971
|
-
logger.log(` line: ${sourceInfo.line}`);
|
|
972
|
-
} else {
|
|
973
|
-
logger.log(`@atrim/auto-trace: No source info for fiber ${fiber.id().id}`);
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
let spanName;
|
|
977
|
-
if (effect.Option.isSome(nameOverride)) {
|
|
978
|
-
spanName = nameOverride.value;
|
|
979
|
-
} else {
|
|
980
|
-
spanName = inferSpanName(fiber.id().id, sourceInfo, this.config);
|
|
981
|
-
}
|
|
982
|
-
if (!this.shouldTrace(spanName)) {
|
|
983
|
-
return;
|
|
1321
|
+
var CapturedSourceLocation = effect.FiberRef.unsafeMake(void 0);
|
|
1322
|
+
function captureCallSite(skipFrames = 0) {
|
|
1323
|
+
const originalLimit = Error.stackTraceLimit;
|
|
1324
|
+
Error.stackTraceLimit = 10;
|
|
1325
|
+
const error = new Error();
|
|
1326
|
+
Error.stackTraceLimit = originalLimit;
|
|
1327
|
+
if (!error.stack) return void 0;
|
|
1328
|
+
const lines = error.stack.split("\n");
|
|
1329
|
+
const startIndex = 3 + skipFrames;
|
|
1330
|
+
for (let i = startIndex; i < lines.length; i++) {
|
|
1331
|
+
const line = lines[i];
|
|
1332
|
+
if (line === void 0) continue;
|
|
1333
|
+
if (line.includes("@atrim/instrument") || line.includes("node_modules/effect")) {
|
|
1334
|
+
continue;
|
|
984
1335
|
}
|
|
985
|
-
const
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
const effectSpan = maybeEffectParentSpan.value;
|
|
994
|
-
logger.log(
|
|
995
|
-
`@atrim/auto-trace: Found ParentSpan - traceId=${effectSpan.traceId.slice(0, 8)}..., spanId=${effectSpan.spanId.slice(0, 8)}...`
|
|
996
|
-
);
|
|
997
|
-
const otelSpanContext = {
|
|
998
|
-
traceId: effectSpan.traceId,
|
|
999
|
-
spanId: effectSpan.spanId,
|
|
1000
|
-
traceFlags: effectSpan.sampled ? OtelApi__namespace.TraceFlags.SAMPLED : OtelApi__namespace.TraceFlags.NONE,
|
|
1001
|
-
isRemote: false
|
|
1336
|
+
const match = line.match(/at\s+(?:(.+?)\s+)?\(?(.+?):(\d+):(\d+)\)?/);
|
|
1337
|
+
if (match) {
|
|
1338
|
+
const [, funcName, filePath, lineNum, colNum] = match;
|
|
1339
|
+
return {
|
|
1340
|
+
function: funcName?.trim() ?? "anonymous",
|
|
1341
|
+
file: filePath ?? "unknown",
|
|
1342
|
+
line: parseInt(lineNum ?? "0", 10),
|
|
1343
|
+
column: parseInt(colNum ?? "0", 10)
|
|
1002
1344
|
};
|
|
1003
|
-
if (useParentChild) {
|
|
1004
|
-
const wrappedSpan = OtelApi__namespace.trace.wrapSpanContext(otelSpanContext);
|
|
1005
|
-
parentContext = OtelApi__namespace.trace.setSpan(OtelApi__namespace.ROOT_CONTEXT, wrappedSpan);
|
|
1006
|
-
}
|
|
1007
|
-
if (useSpanLinks) {
|
|
1008
|
-
const linkAttributes = this.getLinkAttributes();
|
|
1009
|
-
spanLinks.push({
|
|
1010
|
-
context: otelSpanContext,
|
|
1011
|
-
attributes: linkAttributes
|
|
1012
|
-
});
|
|
1013
|
-
logger.log(`@atrim/auto-trace: Added span link to parent (${relationshipType})`);
|
|
1014
|
-
}
|
|
1015
|
-
} else if (effect.Option.isSome(parent)) {
|
|
1016
|
-
parentFiberId = parent.value.id().id;
|
|
1017
|
-
const parentSpan = this.fiberSpans.get(parent.value);
|
|
1018
|
-
if (parentSpan) {
|
|
1019
|
-
if (useParentChild) {
|
|
1020
|
-
parentContext = OtelApi__namespace.trace.setSpan(OtelApi__namespace.ROOT_CONTEXT, parentSpan);
|
|
1021
|
-
}
|
|
1022
|
-
if (useSpanLinks) {
|
|
1023
|
-
const linkAttributes = this.getLinkAttributes();
|
|
1024
|
-
spanLinks.push({
|
|
1025
|
-
context: parentSpan.spanContext(),
|
|
1026
|
-
attributes: linkAttributes
|
|
1027
|
-
});
|
|
1028
|
-
}
|
|
1029
|
-
} else if (this._rootSpan) {
|
|
1030
|
-
if (useParentChild) {
|
|
1031
|
-
parentContext = OtelApi__namespace.trace.setSpan(OtelApi__namespace.ROOT_CONTEXT, this._rootSpan);
|
|
1032
|
-
}
|
|
1033
|
-
if (useSpanLinks) {
|
|
1034
|
-
const linkAttributes = this.getLinkAttributes();
|
|
1035
|
-
spanLinks.push({
|
|
1036
|
-
context: this._rootSpan.spanContext(),
|
|
1037
|
-
attributes: linkAttributes
|
|
1038
|
-
});
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
} else if (this._rootSpan) {
|
|
1042
|
-
if (useParentChild) {
|
|
1043
|
-
parentContext = OtelApi__namespace.trace.setSpan(OtelApi__namespace.ROOT_CONTEXT, this._rootSpan);
|
|
1044
|
-
}
|
|
1045
|
-
if (useSpanLinks) {
|
|
1046
|
-
const linkAttributes = this.getLinkAttributes();
|
|
1047
|
-
spanLinks.push({
|
|
1048
|
-
context: this._rootSpan.spanContext(),
|
|
1049
|
-
attributes: linkAttributes
|
|
1050
|
-
});
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
if (effect.Option.isSome(parent)) {
|
|
1054
|
-
parentFiberId = parent.value.id().id;
|
|
1055
|
-
}
|
|
1056
|
-
const spanOptions = {
|
|
1057
|
-
kind: OtelApi__namespace.SpanKind.INTERNAL,
|
|
1058
|
-
attributes: this.getInitialAttributes(fiber, sourceInfo, parentFiberId)
|
|
1059
|
-
};
|
|
1060
|
-
if (spanLinks.length > 0) {
|
|
1061
|
-
spanOptions.links = spanLinks;
|
|
1062
|
-
}
|
|
1063
|
-
const span = this.tracer.startSpan(spanName, spanOptions, parentContext);
|
|
1064
|
-
logger.log(`@atrim/auto-trace: Created span "${spanName}" for fiber ${fiber.id().id}`);
|
|
1065
|
-
this.fiberSpans.set(fiber, span);
|
|
1066
|
-
this.fiberStartTimes.set(fiber, process.hrtime.bigint());
|
|
1067
|
-
this.activeFiberCount++;
|
|
1068
|
-
}
|
|
1069
|
-
/**
|
|
1070
|
-
* Called when a fiber completes (success or failure)
|
|
1071
|
-
*/
|
|
1072
|
-
onEnd(exit, fiber) {
|
|
1073
|
-
logger.log(`@atrim/auto-trace: onEnd called for fiber ${fiber.id().id}`);
|
|
1074
|
-
const span = this.fiberSpans.get(fiber);
|
|
1075
|
-
if (!span) {
|
|
1076
|
-
logger.log(
|
|
1077
|
-
`@atrim/auto-trace: No span found for fiber ${fiber.id().id} (skipped or filtered)`
|
|
1078
|
-
);
|
|
1079
|
-
return;
|
|
1080
|
-
}
|
|
1081
|
-
const startTime = this.fiberStartTimes.get(fiber);
|
|
1082
|
-
if (startTime) {
|
|
1083
|
-
const duration = process.hrtime.bigint() - startTime;
|
|
1084
|
-
const minDuration = this.parseMinDuration(this.config.performance?.min_duration);
|
|
1085
|
-
if (minDuration > 0 && duration < minDuration) {
|
|
1086
|
-
this.fiberSpans.delete(fiber);
|
|
1087
|
-
this.fiberStartTimes.delete(fiber);
|
|
1088
|
-
this.activeFiberCount--;
|
|
1089
|
-
return;
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
if (effect.Exit.isSuccess(exit)) {
|
|
1093
|
-
span.setStatus({ code: OtelApi__namespace.SpanStatusCode.OK });
|
|
1094
|
-
} else {
|
|
1095
|
-
span.setStatus({
|
|
1096
|
-
code: OtelApi__namespace.SpanStatusCode.ERROR,
|
|
1097
|
-
message: "Fiber failed"
|
|
1098
|
-
});
|
|
1099
|
-
span.setAttribute("effect.fiber.failed", true);
|
|
1100
|
-
}
|
|
1101
|
-
span.end();
|
|
1102
|
-
logger.log(`@atrim/auto-trace: Ended span for fiber ${fiber.id().id}`);
|
|
1103
|
-
this.fiberSpans.delete(fiber);
|
|
1104
|
-
this.fiberStartTimes.delete(fiber);
|
|
1105
|
-
this.activeFiberCount--;
|
|
1106
|
-
}
|
|
1107
|
-
/**
|
|
1108
|
-
* Get attributes for span links from config
|
|
1109
|
-
*/
|
|
1110
|
-
getLinkAttributes() {
|
|
1111
|
-
const linkConfig = this.config.span_relationships?.link_attributes;
|
|
1112
|
-
const attrs = {
|
|
1113
|
-
"link.type": linkConfig?.["link.type"] ?? "fork"
|
|
1114
|
-
};
|
|
1115
|
-
if (linkConfig?.custom) {
|
|
1116
|
-
for (const [key, value] of Object.entries(linkConfig.custom)) {
|
|
1117
|
-
attrs[key] = value;
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
return attrs;
|
|
1121
|
-
}
|
|
1122
|
-
/**
|
|
1123
|
-
* Check if a span name should be traced based on filter patterns
|
|
1124
|
-
*/
|
|
1125
|
-
shouldTrace(spanName) {
|
|
1126
|
-
for (const pattern of this.excludePatterns) {
|
|
1127
|
-
if (pattern.test(spanName)) {
|
|
1128
|
-
return false;
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
if (this.includePatterns.length > 0) {
|
|
1132
|
-
for (const pattern of this.includePatterns) {
|
|
1133
|
-
if (pattern.test(spanName)) {
|
|
1134
|
-
return true;
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
return false;
|
|
1138
|
-
}
|
|
1139
|
-
return true;
|
|
1140
|
-
}
|
|
1141
|
-
/**
|
|
1142
|
-
* Get initial span attributes for a fiber
|
|
1143
|
-
*/
|
|
1144
|
-
getInitialAttributes(fiber, sourceInfo, parentFiberId) {
|
|
1145
|
-
const attrs = {
|
|
1146
|
-
"effect.auto_traced": true
|
|
1147
|
-
};
|
|
1148
|
-
if (this.config.metadata?.fiber_info !== false) {
|
|
1149
|
-
attrs["effect.fiber.id"] = fiber.id().id;
|
|
1150
|
-
}
|
|
1151
|
-
if (this.config.metadata?.source_location !== false && sourceInfo) {
|
|
1152
|
-
attrs["code.function"] = sourceInfo.function;
|
|
1153
|
-
attrs["code.filepath"] = sourceInfo.file;
|
|
1154
|
-
attrs["code.lineno"] = sourceInfo.line;
|
|
1155
|
-
attrs["code.column"] = sourceInfo.column;
|
|
1156
|
-
}
|
|
1157
|
-
if (this.config.metadata?.parent_fiber !== false && parentFiberId !== void 0) {
|
|
1158
|
-
attrs["effect.fiber.parent_id"] = parentFiberId;
|
|
1159
1345
|
}
|
|
1160
|
-
return attrs;
|
|
1161
1346
|
}
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
logger.log("@atrim/auto-trace: [parseStackTrace] No stack available");
|
|
1169
|
-
return void 0;
|
|
1170
|
-
}
|
|
1171
|
-
const lines = stack.split("\n");
|
|
1172
|
-
logger.log(`@atrim/auto-trace: [parseStackTrace] Stack has ${lines.length} lines`);
|
|
1173
|
-
for (let i = 0; i < Math.min(10, lines.length); i++) {
|
|
1174
|
-
logger.log(`@atrim/auto-trace: [stack ${i}] ${lines[i]}`);
|
|
1175
|
-
}
|
|
1176
|
-
for (let i = 3; i < lines.length; i++) {
|
|
1177
|
-
const line = lines[i];
|
|
1178
|
-
if (line === void 0) continue;
|
|
1179
|
-
if (!line.includes("node_modules/effect") && !line.includes("@atrim/instrument") && !line.includes("auto/supervisor")) {
|
|
1180
|
-
logger.log(`@atrim/auto-trace: [parseStackTrace] Found user frame: ${line}`);
|
|
1181
|
-
const match = line.match(/at\s+(?:(.+?)\s+)?\(?(.+?):(\d+):(\d+)\)?/);
|
|
1182
|
-
if (match) {
|
|
1183
|
-
const [, funcName, filePath, lineNum, colNum] = match;
|
|
1184
|
-
const sourceInfo = {
|
|
1185
|
-
function: funcName ?? "anonymous",
|
|
1186
|
-
file: filePath ?? "unknown",
|
|
1187
|
-
line: parseInt(lineNum ?? "0", 10),
|
|
1188
|
-
column: parseInt(colNum ?? "0", 10)
|
|
1189
|
-
};
|
|
1190
|
-
logger.log(
|
|
1191
|
-
`@atrim/auto-trace: [parseStackTrace] Parsed: ${sourceInfo.function} at ${sourceInfo.file}:${sourceInfo.line}`
|
|
1192
|
-
);
|
|
1193
|
-
return sourceInfo;
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
logger.log("@atrim/auto-trace: [parseStackTrace] No user code frame found in stack");
|
|
1198
|
-
return void 0;
|
|
1199
|
-
}
|
|
1200
|
-
/**
|
|
1201
|
-
* Parse min_duration string to nanoseconds
|
|
1202
|
-
*/
|
|
1203
|
-
parseMinDuration(duration) {
|
|
1204
|
-
if (!duration || duration === "0ms") return BigInt(0);
|
|
1205
|
-
const match = duration.match(/^(\d+)\s*(ms|millis|s|sec|seconds|us|micros)?$/i);
|
|
1206
|
-
if (!match) return BigInt(0);
|
|
1207
|
-
const value = parseInt(match[1] ?? "0", 10);
|
|
1208
|
-
const unit = (match[2] ?? "ms").toLowerCase();
|
|
1209
|
-
switch (unit) {
|
|
1210
|
-
case "us":
|
|
1211
|
-
case "micros":
|
|
1212
|
-
return BigInt(value) * BigInt(1e3);
|
|
1213
|
-
case "ms":
|
|
1214
|
-
case "millis":
|
|
1215
|
-
return BigInt(value) * BigInt(1e6);
|
|
1216
|
-
case "s":
|
|
1217
|
-
case "sec":
|
|
1218
|
-
case "seconds":
|
|
1219
|
-
return BigInt(value) * BigInt(1e9);
|
|
1220
|
-
default:
|
|
1221
|
-
return BigInt(value) * BigInt(1e6);
|
|
1222
|
-
}
|
|
1347
|
+
return void 0;
|
|
1348
|
+
}
|
|
1349
|
+
var tracedFork = (effect$1) => {
|
|
1350
|
+
const callSite = captureCallSite();
|
|
1351
|
+
if (!callSite) {
|
|
1352
|
+
return effect.Effect.fork(effect$1);
|
|
1223
1353
|
}
|
|
1354
|
+
return effect.FiberRef.set(CapturedSourceLocation, callSite).pipe(effect.Effect.zipRight(effect.Effect.fork(effect$1)));
|
|
1224
1355
|
};
|
|
1225
|
-
var
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
return effect.Layer.unwrapEffect(
|
|
1230
|
-
effect.Effect.gen(function* () {
|
|
1231
|
-
const config = options?.config ?? (yield* loadAutoTracingConfig());
|
|
1232
|
-
if (!config.enabled) {
|
|
1233
|
-
logger.log("@atrim/auto-trace: Auto-tracing disabled via config");
|
|
1234
|
-
return effect.Layer.empty;
|
|
1235
|
-
}
|
|
1236
|
-
patchEffectFork();
|
|
1237
|
-
const supervisor = createAutoTracingSupervisor(config);
|
|
1238
|
-
return effect.Supervisor.addSupervisor(supervisor);
|
|
1239
|
-
})
|
|
1240
|
-
);
|
|
1241
|
-
};
|
|
1242
|
-
var withAutoTracing = (effect$1, config, mainSpanName) => {
|
|
1243
|
-
if (!config.enabled) {
|
|
1244
|
-
logger.log("@atrim/auto-trace: Auto-tracing disabled via config");
|
|
1245
|
-
return effect$1;
|
|
1356
|
+
var tracedForkDaemon = (effect$1) => {
|
|
1357
|
+
const callSite = captureCallSite();
|
|
1358
|
+
if (!callSite) {
|
|
1359
|
+
return effect.Effect.forkDaemon(effect$1);
|
|
1246
1360
|
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
const spanName = mainSpanName ?? inferSpanName(0, void 0, config);
|
|
1250
|
-
const mainSpan = tracer.startSpan(spanName, {
|
|
1251
|
-
kind: OtelApi__namespace.SpanKind.INTERNAL,
|
|
1252
|
-
attributes: {
|
|
1253
|
-
"effect.auto_traced": true,
|
|
1254
|
-
"effect.fiber.id": 0,
|
|
1255
|
-
// Main fiber
|
|
1256
|
-
"effect.main_fiber": true
|
|
1257
|
-
}
|
|
1258
|
-
});
|
|
1259
|
-
supervisor.setRootSpan(mainSpan);
|
|
1260
|
-
return effect.Effect.acquireUseRelease(
|
|
1261
|
-
// Acquire: return the span (already started)
|
|
1262
|
-
effect.Effect.succeed(mainSpan),
|
|
1263
|
-
// Use: run the supervised effect
|
|
1264
|
-
() => effect.Effect.supervised(supervisor)(effect$1),
|
|
1265
|
-
// Release: end the span
|
|
1266
|
-
(span, exit) => effect.Effect.sync(() => {
|
|
1267
|
-
if (effect.Exit.isSuccess(exit)) {
|
|
1268
|
-
span.setStatus({ code: OtelApi__namespace.SpanStatusCode.OK });
|
|
1269
|
-
} else {
|
|
1270
|
-
span.setStatus({
|
|
1271
|
-
code: OtelApi__namespace.SpanStatusCode.ERROR,
|
|
1272
|
-
message: "Effect failed"
|
|
1273
|
-
});
|
|
1274
|
-
}
|
|
1275
|
-
span.end();
|
|
1276
|
-
})
|
|
1361
|
+
return effect.FiberRef.set(CapturedSourceLocation, callSite).pipe(
|
|
1362
|
+
effect.Effect.zipRight(effect.Effect.forkDaemon(effect$1))
|
|
1277
1363
|
);
|
|
1278
1364
|
};
|
|
1279
|
-
var AutoTracingLive = createAutoTracingLayer();
|
|
1280
|
-
var withoutAutoTracing = (effect$1) => effect$1.pipe(effect.Effect.locally(AutoTracingEnabled, false));
|
|
1281
|
-
var setSpanName = (name) => (effect$1) => effect$1.pipe(effect.Effect.locally(AutoTracingSpanName, effect.Option.some(name)));
|
|
1282
|
-
var createExporterLayer = (exporterConfig, serviceName, serviceVersion) => {
|
|
1283
|
-
const config = exporterConfig ?? {
|
|
1284
|
-
type: "otlp",
|
|
1285
|
-
processor: "batch",
|
|
1286
|
-
batch: {
|
|
1287
|
-
scheduled_delay_millis: 1e3,
|
|
1288
|
-
max_export_batch_size: 100
|
|
1289
|
-
}
|
|
1290
|
-
};
|
|
1291
|
-
if (config.type === "none") {
|
|
1292
|
-
logger.log('@atrim/auto-trace: Exporter type is "none", no spans will be exported');
|
|
1293
|
-
return effect.Layer.empty;
|
|
1294
|
-
}
|
|
1295
|
-
const createSpanExporter = () => {
|
|
1296
|
-
if (config.type === "console") {
|
|
1297
|
-
logger.log("@atrim/auto-trace: Using ConsoleSpanExporter");
|
|
1298
|
-
return new sdkTraceBase.ConsoleSpanExporter();
|
|
1299
|
-
}
|
|
1300
|
-
const endpoint = config.endpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
|
|
1301
|
-
logger.log(`@atrim/auto-trace: Using OTLPTraceExporter (${endpoint})`);
|
|
1302
|
-
const exporterConfig2 = {
|
|
1303
|
-
url: `${endpoint}/v1/traces`
|
|
1304
|
-
};
|
|
1305
|
-
if (config.headers) {
|
|
1306
|
-
exporterConfig2.headers = config.headers;
|
|
1307
|
-
logger.log(
|
|
1308
|
-
`@atrim/auto-trace: Using custom headers: ${Object.keys(config.headers).join(", ")}`
|
|
1309
|
-
);
|
|
1310
|
-
}
|
|
1311
|
-
return new exporterTraceOtlpHttp.OTLPTraceExporter(exporterConfig2);
|
|
1312
|
-
};
|
|
1313
|
-
const createSpanProcessor = () => {
|
|
1314
|
-
const exporter = createSpanExporter();
|
|
1315
|
-
if (config.processor === "simple" || config.type === "console") {
|
|
1316
|
-
logger.log("@atrim/auto-trace: Using SimpleSpanProcessor");
|
|
1317
|
-
return new sdkTraceBase.SimpleSpanProcessor(exporter);
|
|
1318
|
-
}
|
|
1319
|
-
const batchConfig = config.batch ?? {
|
|
1320
|
-
scheduled_delay_millis: 1e3,
|
|
1321
|
-
max_export_batch_size: 100
|
|
1322
|
-
};
|
|
1323
|
-
logger.log("@atrim/auto-trace: Using BatchSpanProcessor");
|
|
1324
|
-
return new sdkTraceBase.BatchSpanProcessor(exporter, {
|
|
1325
|
-
scheduledDelayMillis: batchConfig.scheduled_delay_millis,
|
|
1326
|
-
maxExportBatchSize: batchConfig.max_export_batch_size
|
|
1327
|
-
});
|
|
1328
|
-
};
|
|
1329
|
-
return effect.Layer.effectDiscard(
|
|
1330
|
-
effect.Effect.sync(() => {
|
|
1331
|
-
const provider = new sdkTraceBase.BasicTracerProvider({
|
|
1332
|
-
resource: resources.resourceFromAttributes({
|
|
1333
|
-
[semanticConventions.ATTR_SERVICE_NAME]: serviceName,
|
|
1334
|
-
[semanticConventions.ATTR_SERVICE_VERSION]: serviceVersion
|
|
1335
|
-
}),
|
|
1336
|
-
spanProcessors: [createSpanProcessor()]
|
|
1337
|
-
});
|
|
1338
|
-
OtelApi__namespace.trace.setGlobalTracerProvider(provider);
|
|
1339
|
-
logger.log("@atrim/auto-trace: Global TracerProvider registered");
|
|
1340
|
-
return () => {
|
|
1341
|
-
provider.shutdown().catch((err) => {
|
|
1342
|
-
logger.log(`@atrim/auto-trace: Error shutting down provider: ${err}`);
|
|
1343
|
-
});
|
|
1344
|
-
};
|
|
1345
|
-
})
|
|
1346
|
-
);
|
|
1347
|
-
};
|
|
1348
|
-
var createFullAutoTracingLayer = () => {
|
|
1349
|
-
return effect.Layer.unwrapEffect(
|
|
1350
|
-
effect.Effect.gen(function* () {
|
|
1351
|
-
const config = yield* loadFullConfig();
|
|
1352
|
-
const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
|
|
1353
|
-
const serviceVersion = process.env.npm_package_version || "1.0.0";
|
|
1354
|
-
const autoConfig = config.effect?.auto_instrumentation;
|
|
1355
|
-
if (!autoConfig?.enabled) {
|
|
1356
|
-
logger.log("@atrim/auto-trace: Auto-instrumentation disabled via config");
|
|
1357
|
-
return effect.Layer.empty;
|
|
1358
|
-
}
|
|
1359
|
-
patchEffectFork();
|
|
1360
|
-
const exporterConfig = config.effect?.exporter_config;
|
|
1361
|
-
logger.log("@atrim/auto-trace: Full auto-instrumentation enabled");
|
|
1362
|
-
logger.log(` Service: ${serviceName}`);
|
|
1363
|
-
logger.log(` Exporter: ${exporterConfig?.type ?? "otlp"}`);
|
|
1364
|
-
const exporterLayer = createExporterLayer(exporterConfig, serviceName, serviceVersion);
|
|
1365
|
-
const supervisor = createAutoTracingSupervisor(autoConfig);
|
|
1366
|
-
const supervisorLayer = effect.Supervisor.addSupervisor(supervisor);
|
|
1367
|
-
return effect.Layer.mergeAll(exporterLayer, supervisorLayer);
|
|
1368
|
-
})
|
|
1369
|
-
);
|
|
1370
|
-
};
|
|
1371
|
-
var FullAutoTracingLive = createFullAutoTracingLayer();
|
|
1372
1365
|
|
|
1373
|
-
// src/integrations/effect/auto/
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
|
|
1379
|
-
const serviceVersion = process.env.npm_package_version || "1.0.0";
|
|
1380
|
-
const exporterConfig = config.effect?.exporter_config ?? {
|
|
1381
|
-
type: "otlp",
|
|
1382
|
-
processor: "batch"
|
|
1383
|
-
};
|
|
1384
|
-
logger.log("@atrim/auto-trace: Effect-native tracing enabled");
|
|
1385
|
-
logger.log(` Service: ${serviceName}`);
|
|
1386
|
-
logger.log(` Exporter: ${exporterConfig.type}`);
|
|
1387
|
-
if (exporterConfig.type === "none") {
|
|
1388
|
-
logger.log('@atrim/auto-trace: Exporter type is "none", using empty layer');
|
|
1389
|
-
return effect.Layer.empty;
|
|
1390
|
-
}
|
|
1391
|
-
const createSpanProcessor = () => {
|
|
1392
|
-
if (exporterConfig.type === "console") {
|
|
1393
|
-
logger.log("@atrim/auto-trace: Using ConsoleSpanExporter with SimpleSpanProcessor");
|
|
1394
|
-
return new sdkTraceBase.SimpleSpanProcessor(new sdkTraceBase.ConsoleSpanExporter());
|
|
1395
|
-
}
|
|
1396
|
-
const endpoint = exporterConfig.endpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
|
|
1397
|
-
logger.log(`@atrim/auto-trace: Using OTLPTraceExporter (${endpoint})`);
|
|
1398
|
-
const otlpConfig = {
|
|
1399
|
-
url: `${endpoint}/v1/traces`
|
|
1400
|
-
};
|
|
1401
|
-
if (exporterConfig.headers) {
|
|
1402
|
-
otlpConfig.headers = exporterConfig.headers;
|
|
1403
|
-
logger.log(
|
|
1404
|
-
`@atrim/auto-trace: Using custom headers: ${Object.keys(exporterConfig.headers).join(", ")}`
|
|
1405
|
-
);
|
|
1406
|
-
}
|
|
1407
|
-
const exporter = new exporterTraceOtlpHttp.OTLPTraceExporter(otlpConfig);
|
|
1408
|
-
if (exporterConfig.processor === "simple") {
|
|
1409
|
-
logger.log("@atrim/auto-trace: Using SimpleSpanProcessor");
|
|
1410
|
-
return new sdkTraceBase.SimpleSpanProcessor(exporter);
|
|
1411
|
-
}
|
|
1412
|
-
const batchConfig = exporterConfig.batch ?? {
|
|
1413
|
-
scheduled_delay_millis: 1e3,
|
|
1414
|
-
max_export_batch_size: 100
|
|
1415
|
-
};
|
|
1416
|
-
logger.log("@atrim/auto-trace: Using BatchSpanProcessor");
|
|
1417
|
-
return new sdkTraceBase.BatchSpanProcessor(exporter, {
|
|
1418
|
-
scheduledDelayMillis: batchConfig.scheduled_delay_millis,
|
|
1419
|
-
maxExportBatchSize: batchConfig.max_export_batch_size
|
|
1420
|
-
});
|
|
1421
|
-
};
|
|
1422
|
-
const sdkLayer = opentelemetry.NodeSdk.layer(() => ({
|
|
1423
|
-
resource: {
|
|
1424
|
-
serviceName,
|
|
1425
|
-
serviceVersion
|
|
1426
|
-
},
|
|
1427
|
-
spanProcessor: createSpanProcessor()
|
|
1428
|
-
}));
|
|
1429
|
-
logger.log("@atrim/auto-trace: NodeSdk layer created - HTTP requests will be auto-traced");
|
|
1430
|
-
return effect.Layer.discard(sdkLayer);
|
|
1431
|
-
})
|
|
1432
|
-
);
|
|
1433
|
-
};
|
|
1434
|
-
var EffectTracingLive = createEffectTracingLayer();
|
|
1435
|
-
var createCombinedTracingLayer = () => {
|
|
1436
|
-
return effect.Layer.unwrapEffect(
|
|
1437
|
-
effect.Effect.gen(function* () {
|
|
1438
|
-
const config = yield* loadFullConfig();
|
|
1439
|
-
const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
|
|
1440
|
-
const serviceVersion = process.env.npm_package_version || "1.0.0";
|
|
1441
|
-
const exporterConfig = config.effect?.exporter_config ?? {
|
|
1442
|
-
type: "otlp",
|
|
1443
|
-
processor: "batch"
|
|
1444
|
-
};
|
|
1445
|
-
const autoConfig = config.effect?.auto_instrumentation ?? {
|
|
1446
|
-
enabled: true,
|
|
1447
|
-
granularity: "fiber",
|
|
1448
|
-
span_naming: {
|
|
1449
|
-
default: "effect.{function}",
|
|
1450
|
-
infer_from_source: true,
|
|
1451
|
-
rules: []
|
|
1452
|
-
},
|
|
1453
|
-
span_relationships: {
|
|
1454
|
-
type: "parent-child"
|
|
1455
|
-
},
|
|
1456
|
-
filter: { include: [], exclude: [] },
|
|
1457
|
-
performance: { sampling_rate: 1, min_duration: "0ms", max_concurrent: 0 },
|
|
1458
|
-
metadata: { fiber_info: true, source_location: true, parent_fiber: true }
|
|
1459
|
-
};
|
|
1460
|
-
logger.log("@atrim/auto-trace: Combined tracing enabled (HTTP + Fiber)");
|
|
1461
|
-
logger.log(` Service: ${serviceName}`);
|
|
1462
|
-
logger.log(` Exporter: ${exporterConfig.type}`);
|
|
1463
|
-
if (exporterConfig.type === "none") {
|
|
1464
|
-
logger.log('@atrim/auto-trace: Exporter type is "none", using empty layer');
|
|
1465
|
-
return effect.Layer.empty;
|
|
1466
|
-
}
|
|
1467
|
-
const createSpanExporter = () => {
|
|
1468
|
-
if (exporterConfig.type === "console") {
|
|
1469
|
-
logger.log("@atrim/auto-trace: Using ConsoleSpanExporter");
|
|
1470
|
-
return new sdkTraceBase.ConsoleSpanExporter();
|
|
1471
|
-
}
|
|
1472
|
-
const endpoint = exporterConfig.endpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
|
|
1473
|
-
logger.log(`@atrim/auto-trace: Using OTLPTraceExporter (${endpoint})`);
|
|
1474
|
-
const otlpConfig = {
|
|
1475
|
-
url: `${endpoint}/v1/traces`
|
|
1476
|
-
};
|
|
1477
|
-
if (exporterConfig.headers) {
|
|
1478
|
-
otlpConfig.headers = exporterConfig.headers;
|
|
1479
|
-
logger.log(
|
|
1480
|
-
`@atrim/auto-trace: Using custom headers: ${Object.keys(exporterConfig.headers).join(", ")}`
|
|
1481
|
-
);
|
|
1482
|
-
}
|
|
1483
|
-
return new exporterTraceOtlpHttp.OTLPTraceExporter(otlpConfig);
|
|
1484
|
-
};
|
|
1485
|
-
const createSpanProcessor = () => {
|
|
1486
|
-
const exporter = createSpanExporter();
|
|
1487
|
-
if (exporterConfig.processor === "simple" || exporterConfig.type === "console") {
|
|
1488
|
-
logger.log("@atrim/auto-trace: Using SimpleSpanProcessor");
|
|
1489
|
-
return new sdkTraceBase.SimpleSpanProcessor(exporter);
|
|
1490
|
-
}
|
|
1491
|
-
const batchConfig = exporterConfig.batch ?? {
|
|
1492
|
-
scheduled_delay_millis: 1e3,
|
|
1493
|
-
max_export_batch_size: 100
|
|
1494
|
-
};
|
|
1495
|
-
logger.log("@atrim/auto-trace: Using BatchSpanProcessor");
|
|
1496
|
-
return new sdkTraceBase.BatchSpanProcessor(exporter, {
|
|
1497
|
-
scheduledDelayMillis: batchConfig.scheduled_delay_millis,
|
|
1498
|
-
maxExportBatchSize: batchConfig.max_export_batch_size
|
|
1499
|
-
});
|
|
1500
|
-
};
|
|
1501
|
-
const globalProviderLayer = effect.Layer.effectDiscard(
|
|
1502
|
-
effect.Effect.sync(() => {
|
|
1503
|
-
const provider = new sdkTraceBase.BasicTracerProvider({
|
|
1504
|
-
resource: resources.resourceFromAttributes({
|
|
1505
|
-
[semanticConventions.ATTR_SERVICE_NAME]: serviceName,
|
|
1506
|
-
[semanticConventions.ATTR_SERVICE_VERSION]: serviceVersion
|
|
1507
|
-
}),
|
|
1508
|
-
spanProcessors: [createSpanProcessor()]
|
|
1509
|
-
});
|
|
1510
|
-
OtelApi__namespace.trace.setGlobalTracerProvider(provider);
|
|
1511
|
-
logger.log("@atrim/auto-trace: Global TracerProvider registered");
|
|
1512
|
-
})
|
|
1513
|
-
);
|
|
1514
|
-
const resourceLayer = opentelemetry.Resource.layer({
|
|
1515
|
-
serviceName,
|
|
1516
|
-
serviceVersion
|
|
1517
|
-
});
|
|
1518
|
-
const effectTracerLayer = opentelemetry.Tracer.layerGlobal;
|
|
1519
|
-
const supervisor = createAutoTracingSupervisor(autoConfig);
|
|
1520
|
-
const supervisorLayer = effect.Supervisor.addSupervisor(supervisor);
|
|
1521
|
-
logger.log("@atrim/auto-trace: Combined layer created");
|
|
1522
|
-
logger.log(" - HTTP requests: auto-traced via Effect platform (global provider)");
|
|
1523
|
-
logger.log(" - Forked fibers: auto-traced via Supervisor (global provider)");
|
|
1524
|
-
const tracerWithResource = effectTracerLayer.pipe(effect.Layer.provide(resourceLayer));
|
|
1525
|
-
return effect.Layer.mergeAll(globalProviderLayer, effect.Layer.discard(tracerWithResource), supervisorLayer);
|
|
1526
|
-
})
|
|
1527
|
-
);
|
|
1528
|
-
};
|
|
1529
|
-
var CombinedTracingLive = createCombinedTracingLayer();
|
|
1530
|
-
var currentSourceLocation = effect.GlobalValue.globalValue(
|
|
1531
|
-
/* @__PURE__ */ Symbol.for("effect/FiberRef/currentSourceLocation"),
|
|
1532
|
-
() => effect.FiberRef.unsafeMake(void 0)
|
|
1533
|
-
);
|
|
1534
|
-
var sourceFileCache = /* @__PURE__ */ new Map();
|
|
1535
|
-
function extractFunctionNameFromSource(location) {
|
|
1536
|
-
try {
|
|
1537
|
-
let lines = sourceFileCache.get(location.file);
|
|
1538
|
-
if (!lines) {
|
|
1539
|
-
if (!fs__namespace.existsSync(location.file)) {
|
|
1540
|
-
return void 0;
|
|
1541
|
-
}
|
|
1542
|
-
const content = fs__namespace.readFileSync(location.file, "utf-8");
|
|
1543
|
-
lines = content.split("\n");
|
|
1544
|
-
sourceFileCache.set(location.file, lines);
|
|
1545
|
-
}
|
|
1546
|
-
const lineIndex = location.line - 1;
|
|
1547
|
-
if (lineIndex < 0 || lineIndex >= lines.length) {
|
|
1548
|
-
return void 0;
|
|
1549
|
-
}
|
|
1550
|
-
const line = lines[lineIndex];
|
|
1551
|
-
if (!line) return void 0;
|
|
1552
|
-
const fromColumn = line.slice(location.column - 1);
|
|
1553
|
-
const forkPattern = /(?:fork|forkDaemon|forkScoped|forkIn)\s*\(\s*([a-zA-Z_$][a-zA-Z0-9_$]*)/;
|
|
1554
|
-
const match = fromColumn.match(forkPattern);
|
|
1555
|
-
if (match && match[1]) {
|
|
1556
|
-
return match[1];
|
|
1557
|
-
}
|
|
1558
|
-
const fullLinePattern = /(?:fork|forkDaemon|forkScoped|forkIn)\s*\(\s*([a-zA-Z_$][a-zA-Z0-9_$]*)/;
|
|
1559
|
-
const fullMatch = line.match(fullLinePattern);
|
|
1560
|
-
if (fullMatch && fullMatch[1]) {
|
|
1561
|
-
return fullMatch[1];
|
|
1562
|
-
}
|
|
1563
|
-
return void 0;
|
|
1564
|
-
} catch {
|
|
1565
|
-
return void 0;
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
var AutoTracingEnabled2 = effect.FiberRef.unsafeMake(true);
|
|
1569
|
-
var AutoTracingSpanName2 = effect.FiberRef.unsafeMake(effect.Option.none());
|
|
1570
|
-
var SourceCaptureSupervisor = class extends effect.Supervisor.AbstractSupervisor {
|
|
1571
|
-
constructor(config) {
|
|
1572
|
-
super();
|
|
1573
|
-
this.config = config;
|
|
1574
|
-
// WeakMap to associate fibers with their OTel spans
|
|
1575
|
-
__publicField(this, "fiberSpans", /* @__PURE__ */ new WeakMap());
|
|
1576
|
-
// WeakMap to associate fibers with their OTel contexts (for child fiber lookups)
|
|
1577
|
-
__publicField(this, "fiberContexts", /* @__PURE__ */ new WeakMap());
|
|
1578
|
-
// WeakMap for fiber start times (for min_duration filtering)
|
|
1579
|
-
__publicField(this, "fiberStartTimes", /* @__PURE__ */ new WeakMap());
|
|
1580
|
-
// OpenTelemetry tracer - lazily initialized
|
|
1581
|
-
__publicField(this, "_tracer", null);
|
|
1582
|
-
// Active fiber count (for max_concurrent limiting)
|
|
1583
|
-
__publicField(this, "activeFiberCount", 0);
|
|
1584
|
-
// Default root span for fibers without a ParentSpan in their context
|
|
1585
|
-
// This enables auto-instrumentation without requiring Effect.withSpan()
|
|
1586
|
-
__publicField(this, "_rootSpan", null);
|
|
1587
|
-
logger.log("@atrim/source-capture: Supervisor initialized (native source capture)");
|
|
1588
|
-
logger.log(` Granularity: ${config.granularity || "fiber"}`);
|
|
1589
|
-
logger.log(` Sampling rate: ${config.performance?.sampling_rate ?? 1}`);
|
|
1590
|
-
}
|
|
1591
|
-
/**
|
|
1592
|
-
* Set the default root span for auto-instrumentation.
|
|
1593
|
-
* Fibers without a ParentSpan will use this as their parent.
|
|
1594
|
-
*/
|
|
1595
|
-
setRootSpan(span) {
|
|
1596
|
-
this._rootSpan = span;
|
|
1597
|
-
logger.log(`@atrim/source-capture: Root span set - spanId=${span.spanContext().spanId}`);
|
|
1598
|
-
}
|
|
1599
|
-
/**
|
|
1600
|
-
* Get the root span (if set)
|
|
1601
|
-
*/
|
|
1602
|
-
get rootSpan() {
|
|
1603
|
-
return this._rootSpan;
|
|
1604
|
-
}
|
|
1605
|
-
/**
|
|
1606
|
-
* Get the tracer lazily from global OTel API
|
|
1607
|
-
*/
|
|
1608
|
-
get tracer() {
|
|
1609
|
-
if (!this._tracer) {
|
|
1610
|
-
logger.log("@atrim/source-capture: Getting tracer from global API");
|
|
1611
|
-
this._tracer = OtelApi__namespace.trace.getTracer("@atrim/source-capture", "1.0.0");
|
|
1612
|
-
}
|
|
1613
|
-
return this._tracer;
|
|
1614
|
-
}
|
|
1615
|
-
/**
|
|
1616
|
-
* Returns the current value (void for this supervisor)
|
|
1617
|
-
*/
|
|
1618
|
-
get value() {
|
|
1619
|
-
return effect.Effect.void;
|
|
1620
|
-
}
|
|
1621
|
-
/**
|
|
1622
|
-
* Called when a fiber starts executing
|
|
1623
|
-
*/
|
|
1624
|
-
onStart(_context, _effect, parent, fiber) {
|
|
1625
|
-
const fiberId = fiber.id().id;
|
|
1626
|
-
logger.log(`@atrim/source-capture: onStart called for fiber ${fiberId}`);
|
|
1627
|
-
const fiberRefsValue = fiber.getFiberRefs();
|
|
1628
|
-
const enabled = effect.FiberRefs.getOrDefault(fiberRefsValue, AutoTracingEnabled2);
|
|
1629
|
-
if (!enabled) {
|
|
1630
|
-
logger.log(`@atrim/source-capture: Auto-tracing disabled for fiber ${fiberId}`);
|
|
1631
|
-
return;
|
|
1632
|
-
}
|
|
1633
|
-
const samplingRate = this.config.performance?.sampling_rate ?? 1;
|
|
1634
|
-
if (samplingRate < 1 && Math.random() > samplingRate) {
|
|
1635
|
-
return;
|
|
1636
|
-
}
|
|
1637
|
-
const maxConcurrent = this.config.performance?.max_concurrent ?? 0;
|
|
1638
|
-
if (maxConcurrent > 0 && this.activeFiberCount >= maxConcurrent) {
|
|
1639
|
-
return;
|
|
1640
|
-
}
|
|
1641
|
-
const nameOverride = effect.FiberRefs.getOrDefault(fiberRefsValue, AutoTracingSpanName2);
|
|
1642
|
-
const nativeSourceLocation = effect.FiberRefs.getOrDefault(fiberRefsValue, currentSourceLocation);
|
|
1643
|
-
if (nativeSourceLocation) {
|
|
1644
|
-
logger.log(`@atrim/source-capture: Found native source location for fiber ${fiberId}`);
|
|
1645
|
-
logger.log(` file: ${nativeSourceLocation.file}`);
|
|
1646
|
-
logger.log(` line: ${nativeSourceLocation.line}`);
|
|
1647
|
-
logger.log(` column: ${nativeSourceLocation.column}`);
|
|
1648
|
-
logger.log(` functionName: ${nativeSourceLocation.functionName ?? "N/A"}`);
|
|
1649
|
-
} else {
|
|
1650
|
-
logger.log(
|
|
1651
|
-
`@atrim/source-capture: No source location for fiber ${fiberId} (source capture may be disabled)`
|
|
1652
|
-
);
|
|
1653
|
-
}
|
|
1654
|
-
let spanName;
|
|
1655
|
-
let extractedFuncName;
|
|
1656
|
-
if (effect.Option.isSome(nameOverride)) {
|
|
1657
|
-
spanName = nameOverride.value;
|
|
1658
|
-
} else if (nativeSourceLocation) {
|
|
1659
|
-
extractedFuncName = extractFunctionNameFromSource(nativeSourceLocation);
|
|
1660
|
-
const funcName = extractedFuncName ?? nativeSourceLocation.functionName ?? "anonymous";
|
|
1661
|
-
const fileName = nativeSourceLocation.file.split("/").pop() ?? "unknown";
|
|
1662
|
-
spanName = `effect.${funcName} (${fileName}:${nativeSourceLocation.line})`;
|
|
1663
|
-
if (extractedFuncName) {
|
|
1664
|
-
logger.log(
|
|
1665
|
-
`@atrim/source-capture: Extracted function name "${extractedFuncName}" from source`
|
|
1666
|
-
);
|
|
1667
|
-
}
|
|
1668
|
-
} else {
|
|
1669
|
-
spanName = `effect.fiber-${fiberId}`;
|
|
1670
|
-
}
|
|
1671
|
-
let parentContext = OtelApi__namespace.ROOT_CONTEXT;
|
|
1672
|
-
let parentFiberId;
|
|
1673
|
-
const inheritedOtelContext = effect.FiberRefs.getOrDefault(
|
|
1674
|
-
fiberRefsValue,
|
|
1675
|
-
TracerModule__namespace.currentOtelSpanContext
|
|
1676
|
-
);
|
|
1677
|
-
if (inheritedOtelContext) {
|
|
1678
|
-
parentContext = inheritedOtelContext;
|
|
1679
|
-
const inheritedSpan = OtelApi__namespace.trace.getSpan(inheritedOtelContext);
|
|
1680
|
-
if (inheritedSpan) {
|
|
1681
|
-
const spanCtx = inheritedSpan.spanContext();
|
|
1682
|
-
logger.log(
|
|
1683
|
-
`@atrim/source-capture: Using inherited OTel context - traceId=${spanCtx.traceId}, spanId=${spanCtx.spanId}`
|
|
1684
|
-
);
|
|
1685
|
-
}
|
|
1686
|
-
} else {
|
|
1687
|
-
const maybeEffectParentSpan = effect.Context.getOption(_context, effect.Tracer.ParentSpan);
|
|
1688
|
-
if (effect.Option.isSome(maybeEffectParentSpan)) {
|
|
1689
|
-
const effectSpan = maybeEffectParentSpan.value;
|
|
1690
|
-
logger.log(
|
|
1691
|
-
`@atrim/source-capture: Found ParentSpan - traceId=${effectSpan.traceId}, spanId=${effectSpan.spanId}`
|
|
1692
|
-
);
|
|
1693
|
-
const otelSpanContext = {
|
|
1694
|
-
traceId: effectSpan.traceId,
|
|
1695
|
-
spanId: effectSpan.spanId,
|
|
1696
|
-
traceFlags: effectSpan.sampled ? OtelApi__namespace.TraceFlags.SAMPLED : OtelApi__namespace.TraceFlags.NONE,
|
|
1697
|
-
isRemote: false
|
|
1698
|
-
};
|
|
1699
|
-
const wrappedSpan = OtelApi__namespace.trace.wrapSpanContext(otelSpanContext);
|
|
1700
|
-
parentContext = OtelApi__namespace.trace.setSpan(OtelApi__namespace.ROOT_CONTEXT, wrappedSpan);
|
|
1701
|
-
} else if (effect.Option.isSome(parent)) {
|
|
1702
|
-
parentFiberId = parent.value.id().id;
|
|
1703
|
-
const parentOtelContext = this.fiberContexts.get(parent.value);
|
|
1704
|
-
if (parentOtelContext) {
|
|
1705
|
-
parentContext = parentOtelContext;
|
|
1706
|
-
const parentSpan = OtelApi__namespace.trace.getSpan(parentOtelContext);
|
|
1707
|
-
if (parentSpan) {
|
|
1708
|
-
logger.log(
|
|
1709
|
-
`@atrim/source-capture: Using parent fiber's OTel context - traceId=${parentSpan.spanContext().traceId}`
|
|
1710
|
-
);
|
|
1711
|
-
}
|
|
1712
|
-
} else {
|
|
1713
|
-
const parentSpan = this.fiberSpans.get(parent.value);
|
|
1714
|
-
if (parentSpan) {
|
|
1715
|
-
parentContext = OtelApi__namespace.trace.setSpan(OtelApi__namespace.ROOT_CONTEXT, parentSpan);
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
if (effect.Option.isSome(parent)) {
|
|
1721
|
-
parentFiberId = parent.value.id().id;
|
|
1722
|
-
}
|
|
1723
|
-
const attributes = {
|
|
1724
|
-
"effect.auto_traced": true,
|
|
1725
|
-
"effect.source_capture": true,
|
|
1726
|
-
"effect.fiber.id": fiberId
|
|
1727
|
-
};
|
|
1728
|
-
if (nativeSourceLocation) {
|
|
1729
|
-
attributes["code.function"] = extractedFuncName ?? nativeSourceLocation.functionName ?? "anonymous";
|
|
1730
|
-
attributes["code.filepath"] = nativeSourceLocation.file;
|
|
1731
|
-
attributes["code.lineno"] = nativeSourceLocation.line;
|
|
1732
|
-
attributes["code.column"] = nativeSourceLocation.column;
|
|
1733
|
-
}
|
|
1734
|
-
if (parentFiberId !== void 0) {
|
|
1735
|
-
attributes["effect.fiber.parent_id"] = parentFiberId;
|
|
1736
|
-
}
|
|
1737
|
-
const span = this.tracer.startSpan(
|
|
1738
|
-
spanName,
|
|
1739
|
-
{
|
|
1740
|
-
kind: OtelApi__namespace.SpanKind.INTERNAL,
|
|
1741
|
-
attributes
|
|
1742
|
-
},
|
|
1743
|
-
parentContext
|
|
1744
|
-
);
|
|
1745
|
-
logger.log(`@atrim/source-capture: Created span "${spanName}" for fiber ${fiberId}`);
|
|
1746
|
-
const newContext = OtelApi__namespace.trace.setSpan(parentContext, span);
|
|
1747
|
-
this.fiberSpans.set(fiber, span);
|
|
1748
|
-
this.fiberContexts.set(fiber, newContext);
|
|
1749
|
-
this.fiberStartTimes.set(fiber, process.hrtime.bigint());
|
|
1750
|
-
this.activeFiberCount++;
|
|
1751
|
-
}
|
|
1752
|
-
/**
|
|
1753
|
-
* Called when a fiber completes (success or failure)
|
|
1754
|
-
*/
|
|
1755
|
-
onEnd(exit, fiber) {
|
|
1756
|
-
const fiberId = fiber.id().id;
|
|
1757
|
-
logger.log(`@atrim/source-capture: onEnd called for fiber ${fiberId}`);
|
|
1758
|
-
const span = this.fiberSpans.get(fiber);
|
|
1759
|
-
if (!span) {
|
|
1760
|
-
logger.log(`@atrim/source-capture: No span found for fiber ${fiberId} (skipped or filtered)`);
|
|
1761
|
-
return;
|
|
1762
|
-
}
|
|
1763
|
-
const startTime = this.fiberStartTimes.get(fiber);
|
|
1764
|
-
if (startTime) {
|
|
1765
|
-
const duration = process.hrtime.bigint() - startTime;
|
|
1766
|
-
const minDuration = this.parseMinDuration(this.config.performance?.min_duration);
|
|
1767
|
-
if (minDuration > 0 && duration < minDuration) {
|
|
1768
|
-
this.fiberSpans.delete(fiber);
|
|
1769
|
-
this.fiberContexts.delete(fiber);
|
|
1770
|
-
this.fiberStartTimes.delete(fiber);
|
|
1771
|
-
this.activeFiberCount--;
|
|
1772
|
-
return;
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1775
|
-
if (effect.Exit.isSuccess(exit)) {
|
|
1776
|
-
span.setStatus({ code: OtelApi__namespace.SpanStatusCode.OK });
|
|
1777
|
-
} else {
|
|
1778
|
-
span.setStatus({
|
|
1779
|
-
code: OtelApi__namespace.SpanStatusCode.ERROR,
|
|
1780
|
-
message: "Fiber failed"
|
|
1781
|
-
});
|
|
1782
|
-
span.setAttribute("effect.fiber.failed", true);
|
|
1783
|
-
}
|
|
1784
|
-
span.end();
|
|
1785
|
-
logger.log(`@atrim/source-capture: Ended span for fiber ${fiberId}`);
|
|
1786
|
-
this.fiberSpans.delete(fiber);
|
|
1787
|
-
this.fiberContexts.delete(fiber);
|
|
1788
|
-
this.fiberStartTimes.delete(fiber);
|
|
1789
|
-
this.activeFiberCount--;
|
|
1790
|
-
}
|
|
1791
|
-
/**
|
|
1792
|
-
* Parse min_duration string to nanoseconds
|
|
1793
|
-
*/
|
|
1794
|
-
parseMinDuration(duration) {
|
|
1795
|
-
if (!duration || duration === "0ms") return BigInt(0);
|
|
1796
|
-
const match = duration.match(/^(\d+)\s*(ms|millis|s|sec|seconds|us|micros)?$/i);
|
|
1797
|
-
if (!match) return BigInt(0);
|
|
1798
|
-
const value = parseInt(match[1] ?? "0", 10);
|
|
1799
|
-
const unit = (match[2] ?? "ms").toLowerCase();
|
|
1800
|
-
switch (unit) {
|
|
1801
|
-
case "us":
|
|
1802
|
-
case "micros":
|
|
1803
|
-
return BigInt(value) * BigInt(1e3);
|
|
1804
|
-
case "ms":
|
|
1805
|
-
case "millis":
|
|
1806
|
-
return BigInt(value) * BigInt(1e6);
|
|
1807
|
-
case "s":
|
|
1808
|
-
case "sec":
|
|
1809
|
-
case "seconds":
|
|
1810
|
-
return BigInt(value) * BigInt(1e9);
|
|
1811
|
-
default:
|
|
1812
|
-
return BigInt(value) * BigInt(1e6);
|
|
1813
|
-
}
|
|
1814
|
-
}
|
|
1815
|
-
};
|
|
1816
|
-
var setupGlobalTracerProvider = () => {
|
|
1817
|
-
const config = loadFullConfigSync();
|
|
1818
|
-
const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
|
|
1819
|
-
const serviceVersion = process.env.npm_package_version || "1.0.0";
|
|
1820
|
-
const exporterConfig = config.effect?.exporter_config ?? {
|
|
1821
|
-
type: "otlp",
|
|
1822
|
-
processor: "batch"
|
|
1823
|
-
};
|
|
1824
|
-
logger.log("@atrim/source-capture: Setting up global TracerProvider");
|
|
1825
|
-
logger.log(` Service: ${serviceName}`);
|
|
1826
|
-
logger.log(` Exporter: ${exporterConfig.type}`);
|
|
1827
|
-
if (exporterConfig.type === "none") {
|
|
1828
|
-
logger.log('@atrim/source-capture: Exporter type is "none", skipping provider setup');
|
|
1829
|
-
return null;
|
|
1830
|
-
}
|
|
1831
|
-
let exporter;
|
|
1832
|
-
if (exporterConfig.type === "console") {
|
|
1833
|
-
logger.log("@atrim/source-capture: Using ConsoleSpanExporter");
|
|
1834
|
-
exporter = new sdkTraceBase.ConsoleSpanExporter();
|
|
1835
|
-
} else {
|
|
1836
|
-
const endpoint = exporterConfig.endpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
|
|
1837
|
-
logger.log(`@atrim/source-capture: Using OTLPTraceExporter (${endpoint})`);
|
|
1838
|
-
const otlpConfig = {
|
|
1839
|
-
url: `${endpoint}/v1/traces`
|
|
1840
|
-
};
|
|
1841
|
-
if (exporterConfig.headers) {
|
|
1842
|
-
otlpConfig.headers = exporterConfig.headers;
|
|
1843
|
-
logger.log(
|
|
1844
|
-
`@atrim/source-capture: Using headers: ${Object.keys(exporterConfig.headers).join(", ")}`
|
|
1845
|
-
);
|
|
1846
|
-
}
|
|
1847
|
-
exporter = new exporterTraceOtlpHttp.OTLPTraceExporter(otlpConfig);
|
|
1848
|
-
}
|
|
1849
|
-
let spanProcessor;
|
|
1850
|
-
if (exporterConfig.processor === "simple" || exporterConfig.type === "console") {
|
|
1851
|
-
logger.log("@atrim/source-capture: Using SimpleSpanProcessor");
|
|
1852
|
-
spanProcessor = new sdkTraceBase.SimpleSpanProcessor(exporter);
|
|
1853
|
-
} else {
|
|
1854
|
-
const batchConfig = exporterConfig.batch ?? {
|
|
1855
|
-
scheduled_delay_millis: 1e3,
|
|
1856
|
-
max_export_batch_size: 100
|
|
1857
|
-
};
|
|
1858
|
-
logger.log("@atrim/source-capture: Using BatchSpanProcessor");
|
|
1859
|
-
spanProcessor = new sdkTraceBase.BatchSpanProcessor(exporter, {
|
|
1860
|
-
scheduledDelayMillis: batchConfig.scheduled_delay_millis,
|
|
1861
|
-
maxExportBatchSize: batchConfig.max_export_batch_size
|
|
1862
|
-
});
|
|
1863
|
-
}
|
|
1864
|
-
const provider = new sdkTraceBase.BasicTracerProvider({
|
|
1865
|
-
resource: resources.resourceFromAttributes({
|
|
1866
|
-
[semanticConventions.ATTR_SERVICE_NAME]: serviceName,
|
|
1867
|
-
[semanticConventions.ATTR_SERVICE_VERSION]: serviceVersion
|
|
1868
|
-
}),
|
|
1869
|
-
spanProcessors: [spanProcessor]
|
|
1870
|
-
});
|
|
1871
|
-
OtelApi__namespace.trace.setGlobalTracerProvider(provider);
|
|
1872
|
-
logger.log("@atrim/source-capture: Global TracerProvider registered");
|
|
1873
|
-
return { provider, spanProcessor };
|
|
1874
|
-
};
|
|
1875
|
-
var globalProviderSetup = setupGlobalTracerProvider();
|
|
1876
|
-
var createSourceCaptureTracingLayer = () => {
|
|
1877
|
-
const config = loadFullConfigSync();
|
|
1878
|
-
const autoConfig = config.effect?.auto_instrumentation ?? {
|
|
1879
|
-
enabled: true,
|
|
1880
|
-
granularity: "fiber",
|
|
1881
|
-
span_naming: {
|
|
1882
|
-
default: "effect.{function}",
|
|
1883
|
-
infer_from_source: true,
|
|
1884
|
-
rules: []
|
|
1885
|
-
},
|
|
1886
|
-
span_relationships: {
|
|
1887
|
-
type: "parent-child"
|
|
1888
|
-
},
|
|
1889
|
-
filter: { include: [], exclude: [] },
|
|
1890
|
-
performance: { sampling_rate: 1, min_duration: "0ms", max_concurrent: 0 },
|
|
1891
|
-
metadata: { fiber_info: true, source_location: true, parent_fiber: true }
|
|
1892
|
-
};
|
|
1893
|
-
if (!globalProviderSetup) {
|
|
1894
|
-
logger.log("@atrim/source-capture: No TracerProvider, using empty layer");
|
|
1895
|
-
return effect.Layer.empty;
|
|
1896
|
-
}
|
|
1897
|
-
const supervisor = new SourceCaptureSupervisor(autoConfig);
|
|
1898
|
-
const supervisorLayer = effect.Supervisor.addSupervisor(supervisor);
|
|
1899
|
-
logger.log("@atrim/source-capture: Creating layer");
|
|
1900
|
-
logger.log(" - Source capture: ENABLED via Layer.enableSourceCapture");
|
|
1901
|
-
logger.log(" - Effect.withSpan(): exports to OTel via our tracer");
|
|
1902
|
-
logger.log(" - Forked fibers: auto-traced via SourceCaptureSupervisor");
|
|
1903
|
-
const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
|
|
1904
|
-
const serviceVersion = process.env.npm_package_version || "1.0.0";
|
|
1905
|
-
const otelTracer = OtelApi__namespace.trace.getTracer(serviceName, serviceVersion);
|
|
1906
|
-
logger.log(`@atrim/source-capture: Using tracer "${serviceName}" from our provider`);
|
|
1907
|
-
const effectTracerLayer = opentelemetry.Tracer.layerWithoutOtelTracer.pipe(
|
|
1908
|
-
effect.Layer.provide(effect.Layer.succeed(opentelemetry.Tracer.OtelTracer, otelTracer))
|
|
1909
|
-
);
|
|
1910
|
-
return effect.Layer.mergeAll(effectTracerLayer, effect.Layer.enableSourceCapture, supervisorLayer);
|
|
1911
|
-
};
|
|
1912
|
-
var SourceCaptureTracingLive = createSourceCaptureTracingLayer();
|
|
1913
|
-
var withoutSourceCapture = (effect$1) => effect.Effect.withCaptureStackTraces(false)(effect$1);
|
|
1914
|
-
var flushAndShutdown = async () => {
|
|
1915
|
-
if (globalProviderSetup?.provider) {
|
|
1916
|
-
logger.log("@atrim/source-capture: Flushing and shutting down TracerProvider...");
|
|
1917
|
-
await globalProviderSetup.provider.forceFlush();
|
|
1918
|
-
await globalProviderSetup.provider.shutdown();
|
|
1919
|
-
logger.log("@atrim/source-capture: TracerProvider shutdown complete");
|
|
1920
|
-
}
|
|
1921
|
-
};
|
|
1922
|
-
var forceFlush = async () => {
|
|
1923
|
-
if (globalProviderSetup?.provider) {
|
|
1924
|
-
logger.log("@atrim/source-capture: Force flushing TracerProvider...");
|
|
1925
|
-
await globalProviderSetup.provider.forceFlush();
|
|
1926
|
-
logger.log("@atrim/source-capture: Force flush complete");
|
|
1927
|
-
}
|
|
1928
|
-
};
|
|
1929
|
-
var defaultSpanNaming = {
|
|
1930
|
-
includeLocation: true,
|
|
1931
|
-
template: "effect.{op} ({filename}:{line})"
|
|
1932
|
-
};
|
|
1933
|
-
function parseSourceLocation(stack) {
|
|
1934
|
-
const lines = stack.split("\n");
|
|
1935
|
-
for (const line of lines.slice(1)) {
|
|
1936
|
-
if (line.includes("node_modules") || line.includes("/effect/") || line.includes("fiberRuntime.ts") || line.includes("core.ts")) {
|
|
1937
|
-
continue;
|
|
1938
|
-
}
|
|
1939
|
-
const match = line.match(/at\s+(?:.*?\s+\()?(.+):(\d+):(\d+)\)?/);
|
|
1940
|
-
if (match && match[1] && match[2]) {
|
|
1941
|
-
return {
|
|
1942
|
-
file: match[1],
|
|
1943
|
-
line: parseInt(match[2], 10),
|
|
1944
|
-
column: match[3] ? parseInt(match[3], 10) : void 0
|
|
1945
|
-
};
|
|
1946
|
-
}
|
|
1947
|
-
}
|
|
1948
|
-
return void 0;
|
|
1366
|
+
// src/integrations/effect/auto/patch-fork.ts
|
|
1367
|
+
init_dist();
|
|
1368
|
+
var patchAttempted = false;
|
|
1369
|
+
function isEffectForkPatched() {
|
|
1370
|
+
return false;
|
|
1949
1371
|
}
|
|
1950
|
-
function
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
return trace5;
|
|
1372
|
+
function patchEffectFork() {
|
|
1373
|
+
if (patchAttempted) {
|
|
1374
|
+
return;
|
|
1954
1375
|
}
|
|
1955
|
-
|
|
1376
|
+
patchAttempted = true;
|
|
1377
|
+
logger.log("@atrim/auto-trace: Effect.fork auto-patching not available (ESM limitation)");
|
|
1378
|
+
logger.log("@atrim/auto-trace: Use tracedFork() for call-site capture");
|
|
1956
1379
|
}
|
|
1957
|
-
function
|
|
1958
|
-
|
|
1380
|
+
function unpatchEffectFork() {
|
|
1381
|
+
patchAttempted = false;
|
|
1959
1382
|
}
|
|
1960
|
-
var OperationTracingSupervisor = class extends effect.Supervisor.AbstractSupervisor {
|
|
1961
|
-
constructor(operations, spanNaming = defaultSpanNaming) {
|
|
1962
|
-
super();
|
|
1963
|
-
// Map of configured operations by name
|
|
1964
|
-
__publicField(this, "configuredOps");
|
|
1965
|
-
// Global span naming configuration
|
|
1966
|
-
__publicField(this, "spanNaming");
|
|
1967
|
-
// Track processed effects to avoid duplicate spans (using WeakSet for GC)
|
|
1968
|
-
__publicField(this, "processedEffects", /* @__PURE__ */ new WeakSet());
|
|
1969
|
-
// Track spans per fiber for proper lifecycle management
|
|
1970
|
-
__publicField(this, "fiberSpans", /* @__PURE__ */ new WeakMap());
|
|
1971
|
-
// OpenTelemetry tracer - lazily initialized
|
|
1972
|
-
__publicField(this, "_tracer", null);
|
|
1973
|
-
this.configuredOps = new Map(operations.map((op) => [op.name, op]));
|
|
1974
|
-
this.spanNaming = spanNaming;
|
|
1975
|
-
logger.log("@atrim/operation-tracing: Supervisor initialized");
|
|
1976
|
-
logger.log(` Configured operations: ${Array.from(this.configuredOps.keys()).join(", ")}`);
|
|
1977
|
-
}
|
|
1978
|
-
/**
|
|
1979
|
-
* Get the tracer lazily from global OTel API
|
|
1980
|
-
*/
|
|
1981
|
-
get tracer() {
|
|
1982
|
-
if (!this._tracer) {
|
|
1983
|
-
this._tracer = OtelApi__namespace.trace.getTracer("@atrim/operation-tracing", "1.0.0");
|
|
1984
|
-
}
|
|
1985
|
-
return this._tracer;
|
|
1986
|
-
}
|
|
1987
|
-
/**
|
|
1988
|
-
* Returns the current value (void for this supervisor)
|
|
1989
|
-
*/
|
|
1990
|
-
get value() {
|
|
1991
|
-
return effect.Effect.void;
|
|
1992
|
-
}
|
|
1993
|
-
/**
|
|
1994
|
-
* Called for EVERY Effect operation when OpSupervision is enabled.
|
|
1995
|
-
*
|
|
1996
|
-
* This is the key hook for operation-level tracing. We check if the effect
|
|
1997
|
-
* has OperationMeta and create a span if configured.
|
|
1998
|
-
*/
|
|
1999
|
-
onEffect(fiber, effect$1) {
|
|
2000
|
-
if (this.processedEffects.has(effect$1)) {
|
|
2001
|
-
return;
|
|
2002
|
-
}
|
|
2003
|
-
const meta = getOperationMeta(effect$1);
|
|
2004
|
-
if (!meta) {
|
|
2005
|
-
return;
|
|
2006
|
-
}
|
|
2007
|
-
const config = this.configuredOps.get(meta.op);
|
|
2008
|
-
if (!config) {
|
|
2009
|
-
return;
|
|
2010
|
-
}
|
|
2011
|
-
this.processedEffects.add(effect$1);
|
|
2012
|
-
const fiberId = fiber.id().id;
|
|
2013
|
-
logger.log(`@atrim/operation-tracing: Found operation "${meta.op}" in fiber ${fiberId}`);
|
|
2014
|
-
({
|
|
2015
|
-
"effect.operation": meta.op
|
|
2016
|
-
});
|
|
2017
|
-
if (config.includeCount && meta.count !== void 0) {
|
|
2018
|
-
meta.count;
|
|
2019
|
-
}
|
|
2020
|
-
const location = config.includeStack ? parseSourceLocation(meta.capturedAt) : void 0;
|
|
2021
|
-
if (config.includeStack && location) {
|
|
2022
|
-
location.file;
|
|
2023
|
-
location.line;
|
|
2024
|
-
if (location.column !== void 0) {
|
|
2025
|
-
location.column;
|
|
2026
|
-
}
|
|
2027
|
-
}
|
|
2028
|
-
let spanName;
|
|
2029
|
-
const templateVars = {
|
|
2030
|
-
op: meta.op,
|
|
2031
|
-
file: location?.file ?? "unknown",
|
|
2032
|
-
filename: location?.file?.split("/").pop() ?? "unknown",
|
|
2033
|
-
line: location?.line?.toString() ?? "0",
|
|
2034
|
-
column: location?.column?.toString() ?? "0"
|
|
2035
|
-
};
|
|
2036
|
-
if (config.spanNameTemplate) {
|
|
2037
|
-
spanName = applyTemplate2(config.spanNameTemplate, templateVars);
|
|
2038
|
-
} else if (this.spanNaming.includeLocation && location) {
|
|
2039
|
-
spanName = applyTemplate2(this.spanNaming.template, templateVars);
|
|
2040
|
-
} else {
|
|
2041
|
-
spanName = `effect.${meta.op}`;
|
|
2042
|
-
}
|
|
2043
|
-
let parentContext = OtelApi__namespace.ROOT_CONTEXT;
|
|
2044
|
-
const fiberContext = fiber.currentContext;
|
|
2045
|
-
const maybeEffectParentSpan = effect.Context.getOption(fiberContext, TracerModule__namespace.ParentSpan);
|
|
2046
|
-
if (effect.Option.isSome(maybeEffectParentSpan)) {
|
|
2047
|
-
const effectSpan = maybeEffectParentSpan.value;
|
|
2048
|
-
parentContext = OtelApi__namespace.trace.setSpan(
|
|
2049
|
-
OtelApi__namespace.ROOT_CONTEXT,
|
|
2050
|
-
OtelApi__namespace.trace.wrapSpanContext({
|
|
2051
|
-
traceId: effectSpan.traceId,
|
|
2052
|
-
spanId: effectSpan.spanId,
|
|
2053
|
-
traceFlags: OtelApi__namespace.TraceFlags.SAMPLED
|
|
2054
|
-
})
|
|
2055
|
-
);
|
|
2056
|
-
logger.log(
|
|
2057
|
-
`@atrim/operation-tracing: Using parent span traceId=${effectSpan.traceId.slice(0, 8)}...`
|
|
2058
|
-
);
|
|
2059
|
-
}
|
|
2060
|
-
const span = this.tracer.startSpan(
|
|
2061
|
-
spanName,
|
|
2062
|
-
{
|
|
2063
|
-
kind: OtelApi__namespace.SpanKind.INTERNAL
|
|
2064
|
-
},
|
|
2065
|
-
parentContext
|
|
2066
|
-
);
|
|
2067
|
-
span.setAttribute("effect.operation", meta.op);
|
|
2068
|
-
if (config.includeCount && meta.count !== void 0) {
|
|
2069
|
-
span.setAttribute("effect.item_count", meta.count);
|
|
2070
|
-
}
|
|
2071
|
-
if (config.includeStack && location) {
|
|
2072
|
-
span.setAttribute("code.filepath", location.file);
|
|
2073
|
-
span.setAttribute("code.lineno", location.line);
|
|
2074
|
-
if (location.column !== void 0) {
|
|
2075
|
-
span.setAttribute("code.column", location.column);
|
|
2076
|
-
}
|
|
2077
|
-
}
|
|
2078
|
-
logger.log(
|
|
2079
|
-
`@atrim/operation-tracing: Created span "${spanName}" - spanId=${span.spanContext().spanId}`
|
|
2080
|
-
);
|
|
2081
|
-
span.setStatus({ code: OtelApi__namespace.SpanStatusCode.OK });
|
|
2082
|
-
span.end();
|
|
2083
|
-
logger.log(`@atrim/operation-tracing: Ended span "${spanName}"`);
|
|
2084
|
-
let spanMap = this.fiberSpans.get(fiber);
|
|
2085
|
-
if (!spanMap) {
|
|
2086
|
-
spanMap = /* @__PURE__ */ new Map();
|
|
2087
|
-
this.fiberSpans.set(fiber, spanMap);
|
|
2088
|
-
}
|
|
2089
|
-
const spanKey = `${meta.op}-${meta.capturedAt.slice(0, 100)}`;
|
|
2090
|
-
spanMap.set(spanKey, span);
|
|
2091
|
-
}
|
|
2092
|
-
/**
|
|
2093
|
-
* Called when a fiber starts - we don't need this for operation tracing
|
|
2094
|
-
*/
|
|
2095
|
-
onStart() {
|
|
2096
|
-
}
|
|
2097
|
-
/**
|
|
2098
|
-
* Called when a fiber ends - end all spans for this fiber
|
|
2099
|
-
*/
|
|
2100
|
-
onEnd(exit, fiber) {
|
|
2101
|
-
const spanMap = this.fiberSpans.get(fiber);
|
|
2102
|
-
if (!spanMap) {
|
|
2103
|
-
return;
|
|
2104
|
-
}
|
|
2105
|
-
const isError = exit._tag === "Failure";
|
|
2106
|
-
const fiberId = fiber.id().id;
|
|
2107
|
-
for (const [_key, span] of spanMap) {
|
|
2108
|
-
span.setStatus({
|
|
2109
|
-
code: isError ? OtelApi__namespace.SpanStatusCode.ERROR : OtelApi__namespace.SpanStatusCode.OK
|
|
2110
|
-
});
|
|
2111
|
-
span.end();
|
|
2112
|
-
logger.log(
|
|
2113
|
-
`@atrim/operation-tracing: Ended span for fiber ${fiberId} (${isError ? "error" : "success"})`
|
|
2114
|
-
);
|
|
2115
|
-
}
|
|
2116
|
-
this.fiberSpans.delete(fiber);
|
|
2117
|
-
}
|
|
2118
|
-
};
|
|
2119
|
-
var defaultOperations = [
|
|
2120
|
-
{ name: "all", includeCount: true, includeStack: true },
|
|
2121
|
-
{ name: "forEach", includeCount: true, includeStack: true }
|
|
2122
|
-
];
|
|
2123
|
-
var makeOperationTracingLayer = (operations = defaultOperations, spanNaming = defaultSpanNaming) => {
|
|
2124
|
-
const supervisor = new OperationTracingSupervisor(operations, spanNaming);
|
|
2125
|
-
return effect.Supervisor.addSupervisor(supervisor);
|
|
2126
|
-
};
|
|
2127
|
-
var enableOpSupervision = (effect$1) => effect.Effect.withRuntimeFlagsPatch(effect$1, effect.RuntimeFlagsPatch.enable(effect.RuntimeFlags.OpSupervision));
|
|
2128
|
-
var loadOperationTracingLayer = () => effect.Layer.unwrapEffect(
|
|
2129
|
-
effect.Effect.gen(function* () {
|
|
2130
|
-
const config = yield* effect.Effect.tryPromise({
|
|
2131
|
-
try: () => loadConfigWithOptions(),
|
|
2132
|
-
catch: (error) => {
|
|
2133
|
-
logger.log(`@atrim/operation-tracing: Failed to load config: ${error}`);
|
|
2134
|
-
return error;
|
|
2135
|
-
}
|
|
2136
|
-
}).pipe(effect.Effect.catchAll(() => effect.Effect.succeed(null)));
|
|
2137
|
-
const opTracingConfig = config?.effect?.operation_tracing;
|
|
2138
|
-
if (opTracingConfig?.enabled === false) {
|
|
2139
|
-
logger.log("@atrim/operation-tracing: Operation tracing disabled in config");
|
|
2140
|
-
return effect.Layer.empty;
|
|
2141
|
-
}
|
|
2142
|
-
const spanNaming = {
|
|
2143
|
-
includeLocation: opTracingConfig?.span_naming?.include_location ?? true,
|
|
2144
|
-
template: opTracingConfig?.span_naming?.template ?? "effect.{op} ({filename}:{line})"
|
|
2145
|
-
};
|
|
2146
|
-
logger.log(
|
|
2147
|
-
`@atrim/operation-tracing: Span naming - includeLocation=${spanNaming.includeLocation}, template="${spanNaming.template}"`
|
|
2148
|
-
);
|
|
2149
|
-
const operations = opTracingConfig?.operations ? opTracingConfig.operations.map((op) => ({
|
|
2150
|
-
name: op.name,
|
|
2151
|
-
...op.span_name && { spanNameTemplate: op.span_name },
|
|
2152
|
-
includeCount: op.include_count ?? true,
|
|
2153
|
-
includeStack: op.include_stack ?? true
|
|
2154
|
-
})) : defaultOperations;
|
|
2155
|
-
const source = opTracingConfig?.operations ? "instrumentation.yaml" : "defaults";
|
|
2156
|
-
logger.log(
|
|
2157
|
-
`@atrim/operation-tracing: Loaded ${operations.length} operation configs from ${source}`
|
|
2158
|
-
);
|
|
2159
|
-
return makeOperationTracingLayer(operations, spanNaming);
|
|
2160
|
-
})
|
|
2161
|
-
);
|
|
2162
|
-
var OperationTracingLive = loadOperationTracingLayer();
|
|
2163
|
-
var withOperationTracing = (effect$1) => effect$1.pipe(enableOpSupervision, effect.Effect.provide(OperationTracingLive));
|
|
2164
1383
|
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
1384
|
+
// src/integrations/effect/auto/index.ts
|
|
1385
|
+
init_unified_tracing_supervisor();
|
|
1386
|
+
init_unified_tracing_supervisor();
|
|
1387
|
+
init_unified_tracing_supervisor();
|
|
1388
|
+
init_unified_tracing_supervisor();
|
|
1389
|
+
init_unified_tracing_supervisor();
|
|
1390
|
+
init_unified_tracing_supervisor();
|
|
1391
|
+
init_unified_tracing_supervisor();
|
|
1392
|
+
init_unified_tracing_supervisor();
|
|
1393
|
+
init_unified_tracing_supervisor();
|
|
1394
|
+
init_unified_tracing_supervisor();
|
|
1395
|
+
init_unified_tracing_supervisor();
|
|
1396
|
+
var createAutoTracingSupervisor = (config) => new (init_unified_tracing_supervisor(), __toCommonJS(unified_tracing_supervisor_exports)).UnifiedTracingSupervisor(config);
|
|
1397
|
+
|
|
1398
|
+
exports.AutoTracingLive = exports.UnifiedTracingLive;
|
|
1399
|
+
exports.AutoTracingSupervisor = exports.UnifiedTracingSupervisor;
|
|
2172
1400
|
exports.CapturedSourceLocation = CapturedSourceLocation;
|
|
2173
|
-
exports.CombinedTracingLive =
|
|
2174
|
-
exports.
|
|
2175
|
-
exports.
|
|
2176
|
-
exports.
|
|
2177
|
-
exports.
|
|
2178
|
-
exports.SourceCaptureSupervisor = SourceCaptureSupervisor;
|
|
2179
|
-
exports.SourceCaptureTracingLive = SourceCaptureTracingLive;
|
|
1401
|
+
exports.CombinedTracingLive = exports.UnifiedTracingLive;
|
|
1402
|
+
exports.FullAutoTracingLive = exports.UnifiedTracingLive;
|
|
1403
|
+
exports.OperationTracingLive = exports.UnifiedTracingLive;
|
|
1404
|
+
exports.SourceCaptureSupervisor = exports.UnifiedTracingSupervisor;
|
|
1405
|
+
exports.SourceCaptureTracingLive = exports.UnifiedTracingLive;
|
|
2180
1406
|
exports.captureCallSite = captureCallSite;
|
|
2181
|
-
exports.createAutoTracingLayer =
|
|
1407
|
+
exports.createAutoTracingLayer = exports.createUnifiedTracingLayer;
|
|
2182
1408
|
exports.createAutoTracingSupervisor = createAutoTracingSupervisor;
|
|
2183
|
-
exports.
|
|
2184
|
-
exports.createEffectTracingLayer = createEffectTracingLayer;
|
|
2185
|
-
exports.createFullAutoTracingLayer = createFullAutoTracingLayer;
|
|
2186
|
-
exports.createSourceCaptureTracingLayer = createSourceCaptureTracingLayer;
|
|
2187
|
-
exports.defaultAutoTracingConfig = defaultAutoTracingConfig;
|
|
2188
|
-
exports.enableOpSupervision = enableOpSupervision;
|
|
2189
|
-
exports.flushAndShutdown = flushAndShutdown;
|
|
2190
|
-
exports.forceFlush = forceFlush;
|
|
1409
|
+
exports.createFullAutoTracingLayer = exports.createUnifiedTracingLayer;
|
|
2191
1410
|
exports.inferSpanName = inferSpanName;
|
|
2192
1411
|
exports.isEffectForkPatched = isEffectForkPatched;
|
|
2193
|
-
exports.loadAutoTracingConfig = loadAutoTracingConfig;
|
|
2194
|
-
exports.loadAutoTracingConfigSync = loadAutoTracingConfigSync;
|
|
2195
|
-
exports.makeOperationTracingLayer = makeOperationTracingLayer;
|
|
2196
1412
|
exports.patchEffectFork = patchEffectFork;
|
|
2197
1413
|
exports.sanitizeSpanName = sanitizeSpanName;
|
|
2198
|
-
exports.setSpanName = setSpanName;
|
|
2199
1414
|
exports.tracedFork = tracedFork;
|
|
2200
1415
|
exports.tracedForkDaemon = tracedForkDaemon;
|
|
2201
1416
|
exports.unpatchEffectFork = unpatchEffectFork;
|
|
2202
|
-
exports.
|
|
2203
|
-
exports.withOperationTracing = withOperationTracing;
|
|
2204
|
-
exports.withoutAutoTracing = withoutAutoTracing;
|
|
2205
|
-
exports.withoutSourceCapture = withoutSourceCapture;
|
|
1417
|
+
exports.withOperationTracing = exports.withUnifiedTracing;
|
|
2206
1418
|
//# sourceMappingURL=index.cjs.map
|
|
2207
1419
|
//# sourceMappingURL=index.cjs.map
|