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