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