@atrim/instrument-node 0.4.1 → 0.5.0-14fdea7-20260109020503
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -269
- package/package.json +27 -21
- package/target/dist/index.cjs +241 -61
- package/target/dist/index.cjs.map +1 -1
- package/target/dist/index.d.cts +28 -13
- package/target/dist/index.d.ts +28 -13
- package/target/dist/index.js +240 -61
- package/target/dist/index.js.map +1 -1
- package/target/dist/integrations/effect/auto/index.cjs +1276 -0
- package/target/dist/integrations/effect/auto/index.cjs.map +1 -0
- package/target/dist/integrations/effect/auto/index.d.cts +484 -0
- package/target/dist/integrations/effect/auto/index.d.ts +484 -0
- package/target/dist/integrations/effect/auto/index.js +1229 -0
- package/target/dist/integrations/effect/auto/index.js.map +1 -0
- package/target/dist/integrations/effect/index.cjs +378 -131
- package/target/dist/integrations/effect/index.cjs.map +1 -1
- package/target/dist/integrations/effect/index.d.cts +206 -36
- package/target/dist/integrations/effect/index.d.ts +206 -36
- package/target/dist/integrations/effect/index.js +377 -135
- package/target/dist/integrations/effect/index.js.map +1 -1
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var effect = require('effect');
|
|
4
|
+
var Tracer = require('@effect/opentelemetry/Tracer');
|
|
5
|
+
var Resource = require('@effect/opentelemetry/Resource');
|
|
4
6
|
var Otlp = require('@effect/opentelemetry/Otlp');
|
|
5
7
|
var platform = require('@effect/platform');
|
|
6
8
|
var api = require('@opentelemetry/api');
|
|
9
|
+
var semanticConventions = require('@opentelemetry/semantic-conventions');
|
|
7
10
|
var FileSystem = require('@effect/platform/FileSystem');
|
|
8
11
|
var HttpClient = require('@effect/platform/HttpClient');
|
|
9
12
|
var HttpClientRequest = require('@effect/platform/HttpClientRequest');
|
|
10
13
|
var yaml = require('yaml');
|
|
11
14
|
var zod = require('zod');
|
|
12
|
-
var platformNode = require('@effect/platform-node');
|
|
13
15
|
|
|
14
16
|
function _interopNamespace(e) {
|
|
15
17
|
if (e && e.__esModule) return e;
|
|
@@ -29,6 +31,8 @@ function _interopNamespace(e) {
|
|
|
29
31
|
return Object.freeze(n);
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
var Tracer__namespace = /*#__PURE__*/_interopNamespace(Tracer);
|
|
35
|
+
var Resource__namespace = /*#__PURE__*/_interopNamespace(Resource);
|
|
32
36
|
var Otlp__namespace = /*#__PURE__*/_interopNamespace(Otlp);
|
|
33
37
|
var HttpClient__namespace = /*#__PURE__*/_interopNamespace(HttpClient);
|
|
34
38
|
var HttpClientRequest__namespace = /*#__PURE__*/_interopNamespace(HttpClientRequest);
|
|
@@ -64,13 +68,113 @@ var AutoIsolationConfigSchema = zod.z.object({
|
|
|
64
68
|
add_metadata: zod.z.boolean().default(true)
|
|
65
69
|
}).default({})
|
|
66
70
|
});
|
|
71
|
+
var SpanNamingRuleSchema = zod.z.object({
|
|
72
|
+
// Match criteria (all specified criteria must match)
|
|
73
|
+
match: zod.z.object({
|
|
74
|
+
// Regex pattern to match file path
|
|
75
|
+
file: zod.z.string().optional(),
|
|
76
|
+
// Regex pattern to match function name
|
|
77
|
+
function: zod.z.string().optional(),
|
|
78
|
+
// Regex pattern to match module name
|
|
79
|
+
module: zod.z.string().optional()
|
|
80
|
+
}),
|
|
81
|
+
// Span name template with variables:
|
|
82
|
+
// {fiber_id} - Fiber ID
|
|
83
|
+
// {function} - Function name
|
|
84
|
+
// {module} - Module name
|
|
85
|
+
// {file} - File path
|
|
86
|
+
// {line} - Line number
|
|
87
|
+
// {operator} - Effect operator (gen, all, forEach, etc.)
|
|
88
|
+
// {match:field:N} - Captured regex group from match
|
|
89
|
+
name: zod.z.string()
|
|
90
|
+
});
|
|
91
|
+
var AutoInstrumentationConfigSchema = zod.z.object({
|
|
92
|
+
// Enable/disable auto-instrumentation
|
|
93
|
+
enabled: zod.z.boolean().default(false),
|
|
94
|
+
// Tracing granularity
|
|
95
|
+
// - 'fiber': Trace at fiber creation (recommended, lower overhead)
|
|
96
|
+
// - 'operator': Trace each Effect operator (higher granularity, more overhead)
|
|
97
|
+
granularity: zod.z.enum(["fiber", "operator"]).default("fiber"),
|
|
98
|
+
// Smart span naming configuration
|
|
99
|
+
span_naming: zod.z.object({
|
|
100
|
+
// Default span name template when no rules match
|
|
101
|
+
default: zod.z.string().default("effect.fiber.{fiber_id}"),
|
|
102
|
+
// Infer span names from source code (requires stack trace parsing)
|
|
103
|
+
// Adds ~50-100μs overhead per fiber
|
|
104
|
+
infer_from_source: zod.z.boolean().default(true),
|
|
105
|
+
// Naming rules (first match wins)
|
|
106
|
+
rules: zod.z.array(SpanNamingRuleSchema).default([])
|
|
107
|
+
}).default({}),
|
|
108
|
+
// Pattern-based filtering
|
|
109
|
+
filter: zod.z.object({
|
|
110
|
+
// Only trace spans matching these patterns (empty = trace all)
|
|
111
|
+
include: zod.z.array(zod.z.string()).default([]),
|
|
112
|
+
// Never trace spans matching these patterns
|
|
113
|
+
exclude: zod.z.array(zod.z.string()).default([])
|
|
114
|
+
}).default({}),
|
|
115
|
+
// Performance controls
|
|
116
|
+
performance: zod.z.object({
|
|
117
|
+
// Sample rate (0.0 - 1.0)
|
|
118
|
+
sampling_rate: zod.z.number().min(0).max(1).default(1),
|
|
119
|
+
// Skip fibers shorter than this duration (e.g., "10ms", "100 millis")
|
|
120
|
+
min_duration: zod.z.string().default("0ms"),
|
|
121
|
+
// Maximum concurrent traced fibers (0 = unlimited)
|
|
122
|
+
max_concurrent: zod.z.number().default(0)
|
|
123
|
+
}).default({}),
|
|
124
|
+
// Automatic metadata extraction
|
|
125
|
+
metadata: zod.z.object({
|
|
126
|
+
// Extract Effect fiber information
|
|
127
|
+
fiber_info: zod.z.boolean().default(true),
|
|
128
|
+
// Extract source location (file:line)
|
|
129
|
+
source_location: zod.z.boolean().default(true),
|
|
130
|
+
// Extract parent fiber information
|
|
131
|
+
parent_fiber: zod.z.boolean().default(true)
|
|
132
|
+
}).default({})
|
|
133
|
+
});
|
|
67
134
|
var HttpFilteringConfigSchema = zod.z.object({
|
|
68
135
|
// Patterns to ignore for outgoing HTTP requests (string patterns only in YAML)
|
|
69
136
|
ignore_outgoing_urls: zod.z.array(zod.z.string()).optional(),
|
|
70
137
|
// Patterns to ignore for incoming HTTP requests (string patterns only in YAML)
|
|
71
138
|
ignore_incoming_paths: zod.z.array(zod.z.string()).optional(),
|
|
72
139
|
// Require parent span for outgoing requests (prevents root spans for HTTP calls)
|
|
73
|
-
require_parent_for_outgoing_spans: zod.z.boolean().optional()
|
|
140
|
+
require_parent_for_outgoing_spans: zod.z.boolean().optional(),
|
|
141
|
+
// Trace context propagation configuration
|
|
142
|
+
// Controls which cross-origin requests receive W3C Trace Context headers (traceparent, tracestate)
|
|
143
|
+
propagate_trace_context: zod.z.object({
|
|
144
|
+
// Strategy for trace propagation
|
|
145
|
+
// - "all": Propagate to all cross-origin requests (may cause CORS errors)
|
|
146
|
+
// - "none": Never propagate trace headers
|
|
147
|
+
// - "same-origin": Only propagate to same-origin requests (default, safe)
|
|
148
|
+
// - "patterns": Propagate based on include_urls patterns
|
|
149
|
+
strategy: zod.z.enum(["all", "none", "same-origin", "patterns"]).default("same-origin"),
|
|
150
|
+
// URL patterns to include when strategy is "patterns"
|
|
151
|
+
// Supports regex patterns (e.g., "^https://api\\.myapp\\.com")
|
|
152
|
+
include_urls: zod.z.array(zod.z.string()).optional()
|
|
153
|
+
}).optional()
|
|
154
|
+
});
|
|
155
|
+
var ExporterConfigSchema = zod.z.object({
|
|
156
|
+
// Exporter type: 'otlp' | 'console' | 'none'
|
|
157
|
+
// - 'otlp': Export to OTLP endpoint (production)
|
|
158
|
+
// - 'console': Log spans to console (development)
|
|
159
|
+
// - 'none': No export (disable tracing)
|
|
160
|
+
type: zod.z.enum(["otlp", "console", "none"]).default("otlp"),
|
|
161
|
+
// OTLP endpoint URL (for type: otlp)
|
|
162
|
+
// Defaults to OTEL_EXPORTER_OTLP_ENDPOINT env var or http://localhost:4318
|
|
163
|
+
endpoint: zod.z.string().optional(),
|
|
164
|
+
// Custom headers to send with OTLP requests (for type: otlp)
|
|
165
|
+
// Useful for authentication (x-api-key, Authorization, etc.)
|
|
166
|
+
headers: zod.z.record(zod.z.string()).optional(),
|
|
167
|
+
// Span processor type
|
|
168
|
+
// - 'batch': Batch spans for export (production, lower overhead)
|
|
169
|
+
// - 'simple': Export immediately (development, no batching delay)
|
|
170
|
+
processor: zod.z.enum(["batch", "simple"]).default("batch"),
|
|
171
|
+
// Batch processor settings (for processor: batch)
|
|
172
|
+
batch: zod.z.object({
|
|
173
|
+
// Max time to wait before exporting (milliseconds)
|
|
174
|
+
scheduled_delay_millis: zod.z.number().default(1e3),
|
|
175
|
+
// Max batch size
|
|
176
|
+
max_export_batch_size: zod.z.number().default(100)
|
|
177
|
+
}).optional()
|
|
74
178
|
});
|
|
75
179
|
var InstrumentationConfigSchema = zod.z.object({
|
|
76
180
|
version: zod.z.string(),
|
|
@@ -82,11 +186,54 @@ var InstrumentationConfigSchema = zod.z.object({
|
|
|
82
186
|
ignore_patterns: zod.z.array(PatternConfigSchema)
|
|
83
187
|
}),
|
|
84
188
|
effect: zod.z.object({
|
|
189
|
+
// Enable/disable Effect tracing entirely
|
|
190
|
+
// When false, EffectInstrumentationLive returns Layer.empty
|
|
191
|
+
enabled: zod.z.boolean().default(true),
|
|
192
|
+
// Exporter mode (legacy - use exporter.type instead):
|
|
193
|
+
// - "unified": Use global TracerProvider from Node SDK (recommended, enables filtering)
|
|
194
|
+
// - "standalone": Use Effect's own OTLP exporter (bypasses Node SDK filtering)
|
|
195
|
+
exporter: zod.z.enum(["unified", "standalone"]).default("unified"),
|
|
196
|
+
// Exporter configuration (for auto-instrumentation)
|
|
197
|
+
exporter_config: ExporterConfigSchema.optional(),
|
|
85
198
|
auto_extract_metadata: zod.z.boolean(),
|
|
86
|
-
auto_isolation: AutoIsolationConfigSchema.optional()
|
|
199
|
+
auto_isolation: AutoIsolationConfigSchema.optional(),
|
|
200
|
+
// Auto-instrumentation: automatic tracing of all Effect fibers
|
|
201
|
+
auto_instrumentation: AutoInstrumentationConfigSchema.optional()
|
|
87
202
|
}).optional(),
|
|
88
203
|
http: HttpFilteringConfigSchema.optional()
|
|
89
204
|
});
|
|
205
|
+
var defaultConfig = {
|
|
206
|
+
version: "1.0",
|
|
207
|
+
instrumentation: {
|
|
208
|
+
enabled: true,
|
|
209
|
+
logging: "on",
|
|
210
|
+
description: "Default instrumentation configuration",
|
|
211
|
+
instrument_patterns: [
|
|
212
|
+
{ pattern: "^app\\.", enabled: true, description: "Application operations" },
|
|
213
|
+
{ pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
|
|
214
|
+
{ pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
|
|
215
|
+
],
|
|
216
|
+
ignore_patterns: [
|
|
217
|
+
{ pattern: "^test\\.", description: "Test utilities" },
|
|
218
|
+
{ pattern: "^internal\\.", description: "Internal operations" },
|
|
219
|
+
{ pattern: "^health\\.", description: "Health checks" }
|
|
220
|
+
]
|
|
221
|
+
},
|
|
222
|
+
effect: {
|
|
223
|
+
enabled: true,
|
|
224
|
+
exporter: "unified",
|
|
225
|
+
auto_extract_metadata: true
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
function parseAndValidateConfig(content) {
|
|
229
|
+
let parsed;
|
|
230
|
+
if (typeof content === "string") {
|
|
231
|
+
parsed = yaml.parse(content);
|
|
232
|
+
} else {
|
|
233
|
+
parsed = content;
|
|
234
|
+
}
|
|
235
|
+
return InstrumentationConfigSchema.parse(parsed);
|
|
236
|
+
}
|
|
90
237
|
(class extends effect.Data.TaggedError("ConfigError") {
|
|
91
238
|
get message() {
|
|
92
239
|
return this.reason;
|
|
@@ -264,7 +411,7 @@ var makeConfigLoader = effect.Effect.gen(function* () {
|
|
|
264
411
|
})
|
|
265
412
|
});
|
|
266
413
|
});
|
|
267
|
-
|
|
414
|
+
effect.Layer.effect(ConfigLoader, makeConfigLoader);
|
|
268
415
|
var PatternMatcher = class {
|
|
269
416
|
constructor(config) {
|
|
270
417
|
__publicField(this, "ignorePatterns", []);
|
|
@@ -412,83 +559,58 @@ var Logger = class {
|
|
|
412
559
|
}
|
|
413
560
|
};
|
|
414
561
|
var logger = new Logger();
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
);
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
}).pipe(effect.Effect.provide(NodeConfigLoaderLive))
|
|
425
|
-
);
|
|
562
|
+
async function loadFromFile(filePath) {
|
|
563
|
+
const { readFile } = await import('fs/promises');
|
|
564
|
+
const content = await readFile(filePath, "utf-8");
|
|
565
|
+
return parseAndValidateConfig(content);
|
|
566
|
+
}
|
|
567
|
+
async function loadFromUrl(url) {
|
|
568
|
+
const response = await fetch(url);
|
|
569
|
+
if (!response.ok) {
|
|
570
|
+
throw new Error(`Failed to fetch config from ${url}: ${response.statusText}`);
|
|
426
571
|
}
|
|
427
|
-
|
|
572
|
+
const content = await response.text();
|
|
573
|
+
return parseAndValidateConfig(content);
|
|
428
574
|
}
|
|
429
|
-
async function loadConfig(uri,
|
|
430
|
-
if (
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
return
|
|
575
|
+
async function loadConfig(uri, _options) {
|
|
576
|
+
if (uri.startsWith("http://") || uri.startsWith("https://")) {
|
|
577
|
+
return loadFromUrl(uri);
|
|
578
|
+
}
|
|
579
|
+
if (uri.startsWith("file://")) {
|
|
580
|
+
const filePath = uri.slice(7);
|
|
581
|
+
return loadFromFile(filePath);
|
|
436
582
|
}
|
|
437
|
-
|
|
438
|
-
return effect.Effect.runPromise(loader.loadFromUri(uri));
|
|
583
|
+
return loadFromFile(uri);
|
|
439
584
|
}
|
|
440
585
|
async function loadConfigFromInline(content) {
|
|
441
|
-
|
|
442
|
-
return effect.Effect.runPromise(loader.loadFromInline(content));
|
|
443
|
-
}
|
|
444
|
-
function getDefaultConfig() {
|
|
445
|
-
return {
|
|
446
|
-
version: "1.0",
|
|
447
|
-
instrumentation: {
|
|
448
|
-
enabled: true,
|
|
449
|
-
logging: "on",
|
|
450
|
-
description: "Default instrumentation configuration",
|
|
451
|
-
instrument_patterns: [
|
|
452
|
-
{ pattern: "^app\\.", enabled: true, description: "Application operations" },
|
|
453
|
-
{ pattern: "^http\\.server\\.", enabled: true, description: "HTTP server operations" },
|
|
454
|
-
{ pattern: "^http\\.client\\.", enabled: true, description: "HTTP client operations" }
|
|
455
|
-
],
|
|
456
|
-
ignore_patterns: [
|
|
457
|
-
{ pattern: "^test\\.", description: "Test utilities" },
|
|
458
|
-
{ pattern: "^internal\\.", description: "Internal operations" },
|
|
459
|
-
{ pattern: "^health\\.", description: "Health checks" }
|
|
460
|
-
]
|
|
461
|
-
},
|
|
462
|
-
effect: {
|
|
463
|
-
auto_extract_metadata: true
|
|
464
|
-
}
|
|
465
|
-
};
|
|
586
|
+
return parseAndValidateConfig(content);
|
|
466
587
|
}
|
|
467
588
|
async function loadConfigWithOptions(options = {}) {
|
|
468
|
-
const loadOptions = options.cacheTimeout !== void 0 ? { cacheTimeout: options.cacheTimeout } : void 0;
|
|
469
589
|
if (options.config) {
|
|
470
590
|
return loadConfigFromInline(options.config);
|
|
471
591
|
}
|
|
472
592
|
const envConfigPath = process.env.ATRIM_INSTRUMENTATION_CONFIG;
|
|
473
593
|
if (envConfigPath) {
|
|
474
|
-
return loadConfig(envConfigPath
|
|
594
|
+
return loadConfig(envConfigPath);
|
|
475
595
|
}
|
|
476
596
|
if (options.configUrl) {
|
|
477
|
-
return loadConfig(options.configUrl
|
|
597
|
+
return loadConfig(options.configUrl);
|
|
478
598
|
}
|
|
479
599
|
if (options.configPath) {
|
|
480
|
-
return loadConfig(options.configPath
|
|
600
|
+
return loadConfig(options.configPath);
|
|
481
601
|
}
|
|
482
602
|
const { existsSync } = await import('fs');
|
|
483
603
|
const { join } = await import('path');
|
|
484
604
|
const defaultPath = join(process.cwd(), "instrumentation.yaml");
|
|
485
605
|
if (existsSync(defaultPath)) {
|
|
486
|
-
return loadConfig(defaultPath
|
|
606
|
+
return loadConfig(defaultPath);
|
|
487
607
|
}
|
|
488
|
-
return
|
|
608
|
+
return defaultConfig;
|
|
489
609
|
}
|
|
490
610
|
|
|
491
611
|
// src/integrations/effect/effect-tracer.ts
|
|
612
|
+
var SDK_NAME = "@effect/opentelemetry";
|
|
613
|
+
var ATTR_TELEMETRY_EXPORTER_MODE = "telemetry.exporter.mode";
|
|
492
614
|
function createEffectInstrumentation(options = {}) {
|
|
493
615
|
return effect.Layer.unwrapEffect(
|
|
494
616
|
effect.Effect.gen(function* () {
|
|
@@ -499,106 +621,228 @@ function createEffectInstrumentation(options = {}) {
|
|
|
499
621
|
message: error instanceof Error ? error.message : String(error)
|
|
500
622
|
})
|
|
501
623
|
});
|
|
624
|
+
const effectEnabled = process.env.OTEL_EFFECT_ENABLED !== "false" && (config.effect?.enabled ?? true);
|
|
625
|
+
if (!effectEnabled) {
|
|
626
|
+
logger.log("@atrim/instrumentation/effect: Effect tracing disabled via config");
|
|
627
|
+
return effect.Layer.empty;
|
|
628
|
+
}
|
|
502
629
|
yield* effect.Effect.sync(() => {
|
|
503
630
|
const loggingLevel = config.instrumentation.logging || "on";
|
|
504
631
|
logger.setLevel(loggingLevel);
|
|
505
632
|
});
|
|
506
633
|
yield* effect.Effect.sync(() => initializePatternMatcher(config));
|
|
507
|
-
const otlpEndpoint = options.otlpEndpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
|
|
508
634
|
const serviceName = options.serviceName || process.env.OTEL_SERVICE_NAME || "effect-service";
|
|
509
635
|
const serviceVersion = options.serviceVersion || process.env.npm_package_version || "1.0.0";
|
|
510
|
-
const
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
636
|
+
const exporterMode = options.exporterMode ?? config.effect?.exporter ?? "unified";
|
|
637
|
+
const resourceAttributes = {
|
|
638
|
+
"platform.component": "effect",
|
|
639
|
+
[semanticConventions.ATTR_TELEMETRY_SDK_LANGUAGE]: semanticConventions.TELEMETRY_SDK_LANGUAGE_VALUE_NODEJS,
|
|
640
|
+
[semanticConventions.ATTR_TELEMETRY_SDK_NAME]: SDK_NAME,
|
|
641
|
+
[ATTR_TELEMETRY_EXPORTER_MODE]: exporterMode
|
|
642
|
+
};
|
|
643
|
+
if (exporterMode === "standalone") {
|
|
644
|
+
const otlpEndpoint = options.otlpEndpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
|
|
645
|
+
logger.log("Effect OpenTelemetry instrumentation (standalone)");
|
|
646
|
+
logger.log(` Service: ${serviceName}`);
|
|
647
|
+
logger.log(` Endpoint: ${otlpEndpoint}`);
|
|
648
|
+
logger.log(" WARNING: Standalone mode bypasses Node SDK filtering");
|
|
649
|
+
return Otlp__namespace.layer({
|
|
650
|
+
baseUrl: otlpEndpoint,
|
|
651
|
+
resource: {
|
|
652
|
+
serviceName,
|
|
653
|
+
serviceVersion,
|
|
654
|
+
attributes: resourceAttributes
|
|
655
|
+
},
|
|
656
|
+
// Bridge Effect context to OpenTelemetry global context
|
|
657
|
+
tracerContext: (f, span) => {
|
|
658
|
+
if (span._tag !== "Span") {
|
|
659
|
+
return f();
|
|
660
|
+
}
|
|
661
|
+
const spanContext = {
|
|
662
|
+
traceId: span.traceId,
|
|
663
|
+
spanId: span.spanId,
|
|
664
|
+
traceFlags: span.sampled ? api.TraceFlags.SAMPLED : api.TraceFlags.NONE
|
|
665
|
+
};
|
|
666
|
+
const otelSpan = api.trace.wrapSpanContext(spanContext);
|
|
667
|
+
return api.context.with(api.trace.setSpan(api.context.active(), otelSpan), f);
|
|
533
668
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
669
|
+
}).pipe(effect.Layer.provide(platform.FetchHttpClient.layer));
|
|
670
|
+
} else {
|
|
671
|
+
logger.log("Effect OpenTelemetry instrumentation (unified)");
|
|
672
|
+
logger.log(` Service: ${serviceName}`);
|
|
673
|
+
logger.log(" Using global TracerProvider for span export");
|
|
674
|
+
return Tracer__namespace.layerGlobal.pipe(
|
|
675
|
+
effect.Layer.provide(
|
|
676
|
+
Resource__namespace.layer({
|
|
677
|
+
serviceName,
|
|
678
|
+
serviceVersion,
|
|
679
|
+
attributes: resourceAttributes
|
|
680
|
+
})
|
|
681
|
+
)
|
|
682
|
+
);
|
|
545
683
|
}
|
|
546
|
-
return otlpLayer;
|
|
547
684
|
})
|
|
548
685
|
).pipe(effect.Layer.orDie);
|
|
549
686
|
}
|
|
550
687
|
var EffectInstrumentationLive = effect.Effect.sync(() => {
|
|
551
|
-
const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
|
|
552
688
|
const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
|
|
553
689
|
const serviceVersion = process.env.npm_package_version || "1.0.0";
|
|
554
690
|
logger.minimal(`@atrim/instrumentation/effect: Effect tracing enabled (${serviceName})`);
|
|
555
|
-
logger.log("
|
|
556
|
-
logger.log(`
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
return f();
|
|
572
|
-
}
|
|
573
|
-
const spanContext = {
|
|
574
|
-
traceId: span.traceId,
|
|
575
|
-
spanId: span.spanId,
|
|
576
|
-
traceFlags: span.sampled ? api.TraceFlags.SAMPLED : api.TraceFlags.NONE
|
|
577
|
-
};
|
|
578
|
-
const otelSpan = api.trace.wrapSpanContext(spanContext);
|
|
579
|
-
return api.context.with(api.trace.setSpan(api.context.active(), otelSpan), f);
|
|
580
|
-
}
|
|
581
|
-
}).pipe(effect.Layer.provide(platform.FetchHttpClient.layer));
|
|
691
|
+
logger.log("Effect OpenTelemetry tracer (unified)");
|
|
692
|
+
logger.log(` Service: ${serviceName}`);
|
|
693
|
+
return Tracer__namespace.layerGlobal.pipe(
|
|
694
|
+
effect.Layer.provide(
|
|
695
|
+
Resource__namespace.layer({
|
|
696
|
+
serviceName,
|
|
697
|
+
serviceVersion,
|
|
698
|
+
attributes: {
|
|
699
|
+
"platform.component": "effect",
|
|
700
|
+
[semanticConventions.ATTR_TELEMETRY_SDK_LANGUAGE]: semanticConventions.TELEMETRY_SDK_LANGUAGE_VALUE_NODEJS,
|
|
701
|
+
[semanticConventions.ATTR_TELEMETRY_SDK_NAME]: SDK_NAME,
|
|
702
|
+
[ATTR_TELEMETRY_EXPORTER_MODE]: "unified"
|
|
703
|
+
}
|
|
704
|
+
})
|
|
705
|
+
)
|
|
706
|
+
);
|
|
582
707
|
}).pipe(effect.Layer.unwrapEffect);
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
708
|
+
function annotateUser(userId, email, username) {
|
|
709
|
+
const attributes = {
|
|
710
|
+
"user.id": userId
|
|
711
|
+
};
|
|
712
|
+
if (email) attributes["user.email"] = email;
|
|
713
|
+
if (username) attributes["user.name"] = username;
|
|
714
|
+
return effect.Effect.annotateCurrentSpan(attributes);
|
|
586
715
|
}
|
|
587
|
-
function annotateDataSize(
|
|
716
|
+
function annotateDataSize(bytes, items, compressionRatio) {
|
|
717
|
+
const attributes = {
|
|
718
|
+
"data.size.bytes": bytes,
|
|
719
|
+
"data.size.items": items
|
|
720
|
+
};
|
|
721
|
+
if (compressionRatio !== void 0) {
|
|
722
|
+
attributes["data.compression.ratio"] = compressionRatio;
|
|
723
|
+
}
|
|
724
|
+
return effect.Effect.annotateCurrentSpan(attributes);
|
|
588
725
|
}
|
|
589
|
-
function annotateBatch(
|
|
726
|
+
function annotateBatch(totalItems, batchSize, successCount, failureCount) {
|
|
727
|
+
const attributes = {
|
|
728
|
+
"batch.size": batchSize,
|
|
729
|
+
"batch.total_items": totalItems,
|
|
730
|
+
"batch.count": Math.ceil(totalItems / batchSize)
|
|
731
|
+
};
|
|
732
|
+
if (successCount !== void 0) {
|
|
733
|
+
attributes["batch.success_count"] = successCount;
|
|
734
|
+
}
|
|
735
|
+
if (failureCount !== void 0) {
|
|
736
|
+
attributes["batch.failure_count"] = failureCount;
|
|
737
|
+
}
|
|
738
|
+
return effect.Effect.annotateCurrentSpan(attributes);
|
|
590
739
|
}
|
|
591
|
-
function annotateLLM(
|
|
740
|
+
function annotateLLM(model, provider, tokens) {
|
|
741
|
+
const attributes = {
|
|
742
|
+
"llm.model": model,
|
|
743
|
+
"llm.provider": provider
|
|
744
|
+
};
|
|
745
|
+
if (tokens) {
|
|
746
|
+
if (tokens.prompt !== void 0) attributes["llm.tokens.prompt"] = tokens.prompt;
|
|
747
|
+
if (tokens.completion !== void 0) attributes["llm.tokens.completion"] = tokens.completion;
|
|
748
|
+
if (tokens.total !== void 0) attributes["llm.tokens.total"] = tokens.total;
|
|
749
|
+
}
|
|
750
|
+
return effect.Effect.annotateCurrentSpan(attributes);
|
|
592
751
|
}
|
|
593
|
-
function annotateQuery(
|
|
752
|
+
function annotateQuery(query, duration, rowCount, database) {
|
|
753
|
+
const attributes = {
|
|
754
|
+
"db.statement": query.length > 1e3 ? query.substring(0, 1e3) + "..." : query
|
|
755
|
+
};
|
|
756
|
+
if (duration !== void 0) attributes["db.duration.ms"] = duration;
|
|
757
|
+
if (rowCount !== void 0) attributes["db.row_count"] = rowCount;
|
|
758
|
+
if (database) attributes["db.name"] = database;
|
|
759
|
+
return effect.Effect.annotateCurrentSpan(attributes);
|
|
594
760
|
}
|
|
595
|
-
function annotateHttpRequest(
|
|
761
|
+
function annotateHttpRequest(method, url, statusCode, contentLength) {
|
|
762
|
+
const attributes = {
|
|
763
|
+
"http.method": method,
|
|
764
|
+
"http.url": url
|
|
765
|
+
};
|
|
766
|
+
if (statusCode !== void 0) attributes["http.status_code"] = statusCode;
|
|
767
|
+
if (contentLength !== void 0) attributes["http.response.content_length"] = contentLength;
|
|
768
|
+
return effect.Effect.annotateCurrentSpan(attributes);
|
|
596
769
|
}
|
|
597
|
-
function annotateError(
|
|
770
|
+
function annotateError(error, recoverable, errorType) {
|
|
771
|
+
const errorMessage = typeof error === "string" ? error : error.message;
|
|
772
|
+
const errorStack = typeof error === "string" ? void 0 : error.stack;
|
|
773
|
+
const attributes = {
|
|
774
|
+
"error.message": errorMessage,
|
|
775
|
+
"error.recoverable": recoverable
|
|
776
|
+
};
|
|
777
|
+
if (errorType) attributes["error.type"] = errorType;
|
|
778
|
+
if (errorStack) attributes["error.stack"] = errorStack;
|
|
779
|
+
return effect.Effect.annotateCurrentSpan(attributes);
|
|
598
780
|
}
|
|
599
|
-
function annotatePriority(
|
|
781
|
+
function annotatePriority(priority, reason) {
|
|
782
|
+
const attributes = {
|
|
783
|
+
"operation.priority": priority
|
|
784
|
+
};
|
|
785
|
+
if (reason) attributes["operation.priority.reason"] = reason;
|
|
786
|
+
return effect.Effect.annotateCurrentSpan(attributes);
|
|
600
787
|
}
|
|
601
|
-
function annotateCache(
|
|
788
|
+
function annotateCache(hit, key, ttl) {
|
|
789
|
+
const attributes = {
|
|
790
|
+
"cache.hit": hit,
|
|
791
|
+
"cache.key": key
|
|
792
|
+
};
|
|
793
|
+
if (ttl !== void 0) attributes["cache.ttl.seconds"] = ttl;
|
|
794
|
+
return effect.Effect.annotateCurrentSpan(attributes);
|
|
795
|
+
}
|
|
796
|
+
function extractEffectMetadata() {
|
|
797
|
+
return effect.Effect.gen(function* () {
|
|
798
|
+
const metadata = {};
|
|
799
|
+
const currentFiber = effect.Fiber.getCurrentFiber();
|
|
800
|
+
if (effect.Option.isSome(currentFiber)) {
|
|
801
|
+
const fiber = currentFiber.value;
|
|
802
|
+
const fiberId = fiber.id();
|
|
803
|
+
metadata["effect.fiber.id"] = effect.FiberId.threadName(fiberId);
|
|
804
|
+
const status = yield* effect.Fiber.status(fiber);
|
|
805
|
+
if (status._tag) {
|
|
806
|
+
metadata["effect.fiber.status"] = status._tag;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
const parentSpanResult = yield* effect.Effect.currentSpan.pipe(
|
|
810
|
+
effect.Effect.option
|
|
811
|
+
// Convert NoSuchElementException to Option
|
|
812
|
+
);
|
|
813
|
+
if (effect.Option.isSome(parentSpanResult)) {
|
|
814
|
+
const parentSpan = parentSpanResult.value;
|
|
815
|
+
metadata["effect.operation.nested"] = true;
|
|
816
|
+
metadata["effect.operation.root"] = false;
|
|
817
|
+
if (parentSpan.spanId) {
|
|
818
|
+
metadata["effect.parent.span.id"] = parentSpan.spanId;
|
|
819
|
+
}
|
|
820
|
+
if (parentSpan.name) {
|
|
821
|
+
metadata["effect.parent.span.name"] = parentSpan.name;
|
|
822
|
+
}
|
|
823
|
+
if (parentSpan.traceId) {
|
|
824
|
+
metadata["effect.parent.trace.id"] = parentSpan.traceId;
|
|
825
|
+
}
|
|
826
|
+
} else {
|
|
827
|
+
metadata["effect.operation.nested"] = false;
|
|
828
|
+
metadata["effect.operation.root"] = true;
|
|
829
|
+
}
|
|
830
|
+
return metadata;
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
function autoEnrichSpan() {
|
|
834
|
+
return effect.Effect.gen(function* () {
|
|
835
|
+
const metadata = yield* extractEffectMetadata();
|
|
836
|
+
yield* effect.Effect.annotateCurrentSpan(metadata);
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
function withAutoEnrichedSpan(spanName, options) {
|
|
840
|
+
return (self) => {
|
|
841
|
+
return effect.Effect.gen(function* () {
|
|
842
|
+
yield* autoEnrichSpan();
|
|
843
|
+
return yield* self;
|
|
844
|
+
}).pipe(effect.Effect.withSpan(spanName, options));
|
|
845
|
+
};
|
|
602
846
|
}
|
|
603
847
|
var createLogicalParentLink = (parentSpan, useSpanLinks) => {
|
|
604
848
|
if (!useSpanLinks) {
|
|
@@ -737,8 +981,11 @@ exports.annotatePriority = annotatePriority;
|
|
|
737
981
|
exports.annotateQuery = annotateQuery;
|
|
738
982
|
exports.annotateSpawnedTasks = annotateSpawnedTasks;
|
|
739
983
|
exports.annotateUser = annotateUser;
|
|
984
|
+
exports.autoEnrichSpan = autoEnrichSpan;
|
|
740
985
|
exports.createEffectInstrumentation = createEffectInstrumentation;
|
|
986
|
+
exports.extractEffectMetadata = extractEffectMetadata;
|
|
741
987
|
exports.runIsolated = runIsolated;
|
|
742
988
|
exports.runWithSpan = runWithSpan;
|
|
989
|
+
exports.withAutoEnrichedSpan = withAutoEnrichedSpan;
|
|
743
990
|
//# sourceMappingURL=index.cjs.map
|
|
744
991
|
//# sourceMappingURL=index.cjs.map
|