@atrim/instrument-node 0.7.1-dev.14fdea7.20260108220010 → 0.7.1-dev.14fdea7.20260108231232
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 +1 -1
- package/target/dist/index.cjs +3 -0
- package/target/dist/index.cjs.map +1 -1
- package/target/dist/index.js +3 -0
- package/target/dist/index.js.map +1 -1
- package/target/dist/integrations/effect/auto/index.cjs +193 -121
- package/target/dist/integrations/effect/auto/index.cjs.map +1 -1
- package/target/dist/integrations/effect/auto/index.d.cts +79 -1
- package/target/dist/integrations/effect/auto/index.d.ts +79 -1
- package/target/dist/integrations/effect/auto/index.js +192 -122
- package/target/dist/integrations/effect/auto/index.js.map +1 -1
- package/target/dist/integrations/effect/index.cjs +3 -0
- package/target/dist/integrations/effect/index.cjs.map +1 -1
- package/target/dist/integrations/effect/index.js +3 -0
- package/target/dist/integrations/effect/index.js.map +1 -1
|
@@ -3,6 +3,84 @@ import * as OtelApi from '@opentelemetry/api';
|
|
|
3
3
|
import { AutoInstrumentationConfig, InstrumentationConfig } from '@atrim/instrument-core';
|
|
4
4
|
export { AutoInstrumentationConfig } from '@atrim/instrument-core';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Effect-Native Tracing Layer
|
|
8
|
+
*
|
|
9
|
+
* Uses @effect/opentelemetry's NodeSdk.layer for proper Effect integration.
|
|
10
|
+
* This provides automatic HTTP request tracing and Effect.withSpan() support.
|
|
11
|
+
*
|
|
12
|
+
* Requires @effect/opentelemetry peer dependency to be installed.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create a layer that provides Effect-native tracing via NodeSdk.layer
|
|
17
|
+
*
|
|
18
|
+
* This integrates with Effect's built-in tracing system, which means:
|
|
19
|
+
* - HTTP requests are automatically traced by @effect/platform
|
|
20
|
+
* - Effect.withSpan() creates proper OTel spans
|
|
21
|
+
* - Parent-child span relationships work correctly
|
|
22
|
+
* - No need to fork fibers for tracing to work
|
|
23
|
+
*
|
|
24
|
+
* Configuration is loaded from instrumentation.yaml (exporter_config section).
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* import { EffectTracingLive } from '@atrim/instrument-node/effect/auto'
|
|
29
|
+
*
|
|
30
|
+
* // HTTP requests automatically traced!
|
|
31
|
+
* const HttpLive = router.pipe(
|
|
32
|
+
* HttpServer.serve(),
|
|
33
|
+
* Layer.provide(ServerLive),
|
|
34
|
+
* Layer.provide(EffectTracingLive),
|
|
35
|
+
* )
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
declare const createEffectTracingLayer: () => Layer.Layer<never>;
|
|
39
|
+
/**
|
|
40
|
+
* Effect-native tracing layer using @effect/opentelemetry
|
|
41
|
+
*
|
|
42
|
+
* This is the **recommended** layer for Effect HTTP applications. It provides:
|
|
43
|
+
* - Automatic HTTP request tracing (built into @effect/platform)
|
|
44
|
+
* - Full Effect.withSpan() support for manual spans
|
|
45
|
+
* - Proper parent-child span relationships
|
|
46
|
+
* - No need to fork fibers manually
|
|
47
|
+
*
|
|
48
|
+
* Configuration is loaded from your instrumentation.yaml file.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```yaml
|
|
52
|
+
* # instrumentation.yaml
|
|
53
|
+
* effect:
|
|
54
|
+
* exporter_config:
|
|
55
|
+
* type: otlp # or 'console' for dev
|
|
56
|
+
* endpoint: http://localhost:4318
|
|
57
|
+
* headers:
|
|
58
|
+
* x-api-key: your-key
|
|
59
|
+
* processor: batch # or 'simple' for immediate export
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* import { EffectTracingLive } from '@atrim/instrument-node/effect/auto'
|
|
65
|
+
* import { HttpRouter, HttpServer } from "@effect/platform"
|
|
66
|
+
* import { NodeHttpServer, NodeRuntime } from "@effect/platform-node"
|
|
67
|
+
*
|
|
68
|
+
* const router = HttpRouter.empty.pipe(
|
|
69
|
+
* HttpRouter.get("/", Effect.succeed(HttpServerResponse.text("Hello!"))),
|
|
70
|
+
* )
|
|
71
|
+
*
|
|
72
|
+
* const HttpLive = router.pipe(
|
|
73
|
+
* HttpServer.serve(),
|
|
74
|
+
* Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })),
|
|
75
|
+
* Layer.provide(EffectTracingLive), // <- Just add this!
|
|
76
|
+
* )
|
|
77
|
+
*
|
|
78
|
+
* NodeRuntime.runMain(Layer.launch(HttpLive))
|
|
79
|
+
* // All HTTP requests now automatically traced!
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
declare const EffectTracingLive: Layer.Layer<never>;
|
|
83
|
+
|
|
6
84
|
/**
|
|
7
85
|
* Auto-Tracing Supervisor for Effect-TS
|
|
8
86
|
*
|
|
@@ -356,4 +434,4 @@ declare function inferSpanName(fiberId: number, sourceInfo: SourceInfo | undefin
|
|
|
356
434
|
*/
|
|
357
435
|
declare function sanitizeSpanName(name: string): string;
|
|
358
436
|
|
|
359
|
-
export { AutoTracingConfig, AutoTracingConfigLayer, AutoTracingConfigLive, AutoTracingEnabled, AutoTracingLive, AutoTracingSpanName, AutoTracingSupervisor, FullAutoTracingLive, type SourceInfo, type TemplateVariables, createAutoTracingLayer, createAutoTracingSupervisor, createFullAutoTracingLayer, defaultAutoTracingConfig, inferSpanName, loadAutoTracingConfig, loadAutoTracingConfigSync, sanitizeSpanName, setSpanName, withAutoTracing, withoutAutoTracing };
|
|
437
|
+
export { AutoTracingConfig, AutoTracingConfigLayer, AutoTracingConfigLive, AutoTracingEnabled, AutoTracingLive, AutoTracingSpanName, AutoTracingSupervisor, EffectTracingLive, FullAutoTracingLive, type SourceInfo, type TemplateVariables, createAutoTracingLayer, createAutoTracingSupervisor, createEffectTracingLayer, createFullAutoTracingLayer, defaultAutoTracingConfig, inferSpanName, loadAutoTracingConfig, loadAutoTracingConfigSync, sanitizeSpanName, setSpanName, withAutoTracing, withoutAutoTracing };
|
|
@@ -3,6 +3,84 @@ import * as OtelApi from '@opentelemetry/api';
|
|
|
3
3
|
import { AutoInstrumentationConfig, InstrumentationConfig } from '@atrim/instrument-core';
|
|
4
4
|
export { AutoInstrumentationConfig } from '@atrim/instrument-core';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Effect-Native Tracing Layer
|
|
8
|
+
*
|
|
9
|
+
* Uses @effect/opentelemetry's NodeSdk.layer for proper Effect integration.
|
|
10
|
+
* This provides automatic HTTP request tracing and Effect.withSpan() support.
|
|
11
|
+
*
|
|
12
|
+
* Requires @effect/opentelemetry peer dependency to be installed.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create a layer that provides Effect-native tracing via NodeSdk.layer
|
|
17
|
+
*
|
|
18
|
+
* This integrates with Effect's built-in tracing system, which means:
|
|
19
|
+
* - HTTP requests are automatically traced by @effect/platform
|
|
20
|
+
* - Effect.withSpan() creates proper OTel spans
|
|
21
|
+
* - Parent-child span relationships work correctly
|
|
22
|
+
* - No need to fork fibers for tracing to work
|
|
23
|
+
*
|
|
24
|
+
* Configuration is loaded from instrumentation.yaml (exporter_config section).
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* import { EffectTracingLive } from '@atrim/instrument-node/effect/auto'
|
|
29
|
+
*
|
|
30
|
+
* // HTTP requests automatically traced!
|
|
31
|
+
* const HttpLive = router.pipe(
|
|
32
|
+
* HttpServer.serve(),
|
|
33
|
+
* Layer.provide(ServerLive),
|
|
34
|
+
* Layer.provide(EffectTracingLive),
|
|
35
|
+
* )
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
declare const createEffectTracingLayer: () => Layer.Layer<never>;
|
|
39
|
+
/**
|
|
40
|
+
* Effect-native tracing layer using @effect/opentelemetry
|
|
41
|
+
*
|
|
42
|
+
* This is the **recommended** layer for Effect HTTP applications. It provides:
|
|
43
|
+
* - Automatic HTTP request tracing (built into @effect/platform)
|
|
44
|
+
* - Full Effect.withSpan() support for manual spans
|
|
45
|
+
* - Proper parent-child span relationships
|
|
46
|
+
* - No need to fork fibers manually
|
|
47
|
+
*
|
|
48
|
+
* Configuration is loaded from your instrumentation.yaml file.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```yaml
|
|
52
|
+
* # instrumentation.yaml
|
|
53
|
+
* effect:
|
|
54
|
+
* exporter_config:
|
|
55
|
+
* type: otlp # or 'console' for dev
|
|
56
|
+
* endpoint: http://localhost:4318
|
|
57
|
+
* headers:
|
|
58
|
+
* x-api-key: your-key
|
|
59
|
+
* processor: batch # or 'simple' for immediate export
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* import { EffectTracingLive } from '@atrim/instrument-node/effect/auto'
|
|
65
|
+
* import { HttpRouter, HttpServer } from "@effect/platform"
|
|
66
|
+
* import { NodeHttpServer, NodeRuntime } from "@effect/platform-node"
|
|
67
|
+
*
|
|
68
|
+
* const router = HttpRouter.empty.pipe(
|
|
69
|
+
* HttpRouter.get("/", Effect.succeed(HttpServerResponse.text("Hello!"))),
|
|
70
|
+
* )
|
|
71
|
+
*
|
|
72
|
+
* const HttpLive = router.pipe(
|
|
73
|
+
* HttpServer.serve(),
|
|
74
|
+
* Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })),
|
|
75
|
+
* Layer.provide(EffectTracingLive), // <- Just add this!
|
|
76
|
+
* )
|
|
77
|
+
*
|
|
78
|
+
* NodeRuntime.runMain(Layer.launch(HttpLive))
|
|
79
|
+
* // All HTTP requests now automatically traced!
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
declare const EffectTracingLive: Layer.Layer<never>;
|
|
83
|
+
|
|
6
84
|
/**
|
|
7
85
|
* Auto-Tracing Supervisor for Effect-TS
|
|
8
86
|
*
|
|
@@ -356,4 +434,4 @@ declare function inferSpanName(fiberId: number, sourceInfo: SourceInfo | undefin
|
|
|
356
434
|
*/
|
|
357
435
|
declare function sanitizeSpanName(name: string): string;
|
|
358
436
|
|
|
359
|
-
export { AutoTracingConfig, AutoTracingConfigLayer, AutoTracingConfigLive, AutoTracingEnabled, AutoTracingLive, AutoTracingSpanName, AutoTracingSupervisor, FullAutoTracingLive, type SourceInfo, type TemplateVariables, createAutoTracingLayer, createAutoTracingSupervisor, createFullAutoTracingLayer, defaultAutoTracingConfig, inferSpanName, loadAutoTracingConfig, loadAutoTracingConfigSync, sanitizeSpanName, setSpanName, withAutoTracing, withoutAutoTracing };
|
|
437
|
+
export { AutoTracingConfig, AutoTracingConfigLayer, AutoTracingConfigLive, AutoTracingEnabled, AutoTracingLive, AutoTracingSpanName, AutoTracingSupervisor, EffectTracingLive, FullAutoTracingLive, type SourceInfo, type TemplateVariables, createAutoTracingLayer, createAutoTracingSupervisor, createEffectTracingLayer, createFullAutoTracingLayer, defaultAutoTracingConfig, inferSpanName, loadAutoTracingConfig, loadAutoTracingConfigSync, sanitizeSpanName, setSpanName, withAutoTracing, withoutAutoTracing };
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { Data, Context, Effect, Layer, FiberRef, Option, Supervisor, FiberRefs, Exit } from 'effect';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import { NodeSdk } from '@effect/opentelemetry';
|
|
3
|
+
import { SimpleSpanProcessor, ConsoleSpanExporter, BatchSpanProcessor, BasicTracerProvider } from '@opentelemetry/sdk-trace-base';
|
|
4
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
5
|
import { FileSystem } from '@effect/platform/FileSystem';
|
|
8
6
|
import * as HttpClient from '@effect/platform/HttpClient';
|
|
9
7
|
import * as HttpClientRequest from '@effect/platform/HttpClientRequest';
|
|
10
8
|
import { parse } from 'yaml';
|
|
11
9
|
import { z } from 'zod';
|
|
10
|
+
import * as OtelApi from '@opentelemetry/api';
|
|
11
|
+
import { resourceFromAttributes } from '@opentelemetry/resources';
|
|
12
|
+
import { ATTR_SERVICE_VERSION, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
|
|
12
13
|
import * as path from 'path';
|
|
13
14
|
|
|
14
15
|
var __defProp = Object.defineProperty;
|
|
@@ -137,6 +138,9 @@ var ExporterConfigSchema = z.object({
|
|
|
137
138
|
// OTLP endpoint URL (for type: otlp)
|
|
138
139
|
// Defaults to OTEL_EXPORTER_OTLP_ENDPOINT env var or http://localhost:4318
|
|
139
140
|
endpoint: z.string().optional(),
|
|
141
|
+
// Custom headers to send with OTLP requests (for type: otlp)
|
|
142
|
+
// Useful for authentication (x-api-key, Authorization, etc.)
|
|
143
|
+
headers: z.record(z.string()).optional(),
|
|
140
144
|
// Span processor type
|
|
141
145
|
// - 'batch': Batch spans for export (production, lower overhead)
|
|
142
146
|
// - 'simple': Export immediately (development, no batching delay)
|
|
@@ -463,121 +467,6 @@ var Logger = class {
|
|
|
463
467
|
}
|
|
464
468
|
};
|
|
465
469
|
var logger = new Logger();
|
|
466
|
-
var compiledRulesCache = /* @__PURE__ */ new WeakMap();
|
|
467
|
-
function compileNamingRules(rules) {
|
|
468
|
-
const cached = compiledRulesCache.get(rules);
|
|
469
|
-
if (cached) return cached;
|
|
470
|
-
const compiled = rules.map((rule) => ({
|
|
471
|
-
original: rule,
|
|
472
|
-
filePattern: rule.match.file ? new RegExp(rule.match.file) : void 0,
|
|
473
|
-
functionPattern: rule.match.function ? new RegExp(rule.match.function) : void 0,
|
|
474
|
-
modulePattern: rule.match.module ? new RegExp(rule.match.module) : void 0
|
|
475
|
-
}));
|
|
476
|
-
compiledRulesCache.set(rules, compiled);
|
|
477
|
-
return compiled;
|
|
478
|
-
}
|
|
479
|
-
function extractModuleName(filePath) {
|
|
480
|
-
const basename2 = path.basename(filePath, path.extname(filePath));
|
|
481
|
-
return basename2.replace(/\.(service|controller|handler|util|helper|model|repo|repository)$/i, "").replace(/(Service|Controller|Handler|Util|Helper|Model|Repo|Repository)$/i, "");
|
|
482
|
-
}
|
|
483
|
-
function applyTemplate(template, variables, matchGroups) {
|
|
484
|
-
let result = template;
|
|
485
|
-
for (const [key, value] of Object.entries(variables)) {
|
|
486
|
-
result = result.replace(new RegExp(`\\{${key}\\}`, "g"), value);
|
|
487
|
-
}
|
|
488
|
-
if (matchGroups) {
|
|
489
|
-
result = result.replace(
|
|
490
|
-
/\{match:(\w+):(\d+)\}/g,
|
|
491
|
-
(_fullMatch, field, index) => {
|
|
492
|
-
const groups = matchGroups.get(field);
|
|
493
|
-
const idx = parseInt(index, 10);
|
|
494
|
-
if (groups && groups[idx]) {
|
|
495
|
-
return groups[idx];
|
|
496
|
-
}
|
|
497
|
-
return "";
|
|
498
|
-
}
|
|
499
|
-
);
|
|
500
|
-
}
|
|
501
|
-
return result;
|
|
502
|
-
}
|
|
503
|
-
function findMatchingRule(sourceInfo, config) {
|
|
504
|
-
const rules = config.span_naming?.rules;
|
|
505
|
-
if (!rules || rules.length === 0) return void 0;
|
|
506
|
-
const compiledRules = compileNamingRules(rules);
|
|
507
|
-
for (const rule of compiledRules) {
|
|
508
|
-
const matchGroups = /* @__PURE__ */ new Map();
|
|
509
|
-
let allMatched = true;
|
|
510
|
-
if (rule.filePattern) {
|
|
511
|
-
if (sourceInfo?.file) {
|
|
512
|
-
const match = sourceInfo.file.match(rule.filePattern);
|
|
513
|
-
if (match) {
|
|
514
|
-
matchGroups.set("file", match.slice(1));
|
|
515
|
-
} else {
|
|
516
|
-
allMatched = false;
|
|
517
|
-
}
|
|
518
|
-
} else {
|
|
519
|
-
allMatched = false;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
if (rule.functionPattern && allMatched) {
|
|
523
|
-
if (sourceInfo?.function) {
|
|
524
|
-
const match = sourceInfo.function.match(rule.functionPattern);
|
|
525
|
-
if (match) {
|
|
526
|
-
matchGroups.set("function", match.slice(1));
|
|
527
|
-
} else {
|
|
528
|
-
allMatched = false;
|
|
529
|
-
}
|
|
530
|
-
} else {
|
|
531
|
-
allMatched = false;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
if (rule.modulePattern && allMatched) {
|
|
535
|
-
const moduleName = sourceInfo?.file ? extractModuleName(sourceInfo.file) : "";
|
|
536
|
-
if (moduleName) {
|
|
537
|
-
const match = moduleName.match(rule.modulePattern);
|
|
538
|
-
if (match) {
|
|
539
|
-
matchGroups.set("module", match.slice(1));
|
|
540
|
-
} else {
|
|
541
|
-
allMatched = false;
|
|
542
|
-
}
|
|
543
|
-
} else {
|
|
544
|
-
allMatched = false;
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
if (allMatched) {
|
|
548
|
-
return { rule, matchGroups };
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
return void 0;
|
|
552
|
-
}
|
|
553
|
-
function inferSpanName(fiberId, sourceInfo, config) {
|
|
554
|
-
const variables = {
|
|
555
|
-
fiber_id: String(fiberId),
|
|
556
|
-
function: sourceInfo?.function || "anonymous",
|
|
557
|
-
module: sourceInfo?.file ? extractModuleName(sourceInfo.file) : "unknown",
|
|
558
|
-
file: sourceInfo?.file || "unknown",
|
|
559
|
-
line: sourceInfo?.line ? String(sourceInfo.line) : "0",
|
|
560
|
-
operator: "gen"
|
|
561
|
-
// Default operator, could be enhanced with more context
|
|
562
|
-
};
|
|
563
|
-
const match = findMatchingRule(sourceInfo, config);
|
|
564
|
-
if (match) {
|
|
565
|
-
return applyTemplate(match.rule.original.name, variables, match.matchGroups);
|
|
566
|
-
}
|
|
567
|
-
const defaultTemplate = config.span_naming?.default || "effect.fiber.{fiber_id}";
|
|
568
|
-
return applyTemplate(defaultTemplate, variables);
|
|
569
|
-
}
|
|
570
|
-
function sanitizeSpanName(name) {
|
|
571
|
-
if (!name) return "effect.fiber.unknown";
|
|
572
|
-
let sanitized = name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
573
|
-
sanitized = sanitized.replace(/_+/g, "_");
|
|
574
|
-
sanitized = sanitized.replace(/^_+|_+$/g, "");
|
|
575
|
-
if (!sanitized) return "effect.fiber.unknown";
|
|
576
|
-
if (sanitized.length > 256) {
|
|
577
|
-
sanitized = sanitized.substring(0, 256);
|
|
578
|
-
}
|
|
579
|
-
return sanitized;
|
|
580
|
-
}
|
|
581
470
|
async function loadFromFile(filePath) {
|
|
582
471
|
const { readFile } = await import('fs/promises');
|
|
583
472
|
const content = await readFile(filePath, "utf-8");
|
|
@@ -698,6 +587,182 @@ var loadFullConfig = (options) => Effect.gen(function* () {
|
|
|
698
587
|
return config;
|
|
699
588
|
});
|
|
700
589
|
|
|
590
|
+
// src/integrations/effect/auto/effect-tracing.ts
|
|
591
|
+
var createEffectTracingLayer = () => {
|
|
592
|
+
return Layer.unwrapEffect(
|
|
593
|
+
Effect.gen(function* () {
|
|
594
|
+
const config = yield* loadFullConfig();
|
|
595
|
+
const serviceName = process.env.OTEL_SERVICE_NAME || "effect-service";
|
|
596
|
+
const serviceVersion = process.env.npm_package_version || "1.0.0";
|
|
597
|
+
const exporterConfig = config.effect?.exporter_config ?? {
|
|
598
|
+
type: "otlp",
|
|
599
|
+
processor: "batch"
|
|
600
|
+
};
|
|
601
|
+
logger.log("@atrim/auto-trace: Effect-native tracing enabled");
|
|
602
|
+
logger.log(` Service: ${serviceName}`);
|
|
603
|
+
logger.log(` Exporter: ${exporterConfig.type}`);
|
|
604
|
+
if (exporterConfig.type === "none") {
|
|
605
|
+
logger.log('@atrim/auto-trace: Exporter type is "none", using empty layer');
|
|
606
|
+
return Layer.empty;
|
|
607
|
+
}
|
|
608
|
+
const createSpanProcessor = () => {
|
|
609
|
+
if (exporterConfig.type === "console") {
|
|
610
|
+
logger.log("@atrim/auto-trace: Using ConsoleSpanExporter with SimpleSpanProcessor");
|
|
611
|
+
return new SimpleSpanProcessor(new ConsoleSpanExporter());
|
|
612
|
+
}
|
|
613
|
+
const endpoint = exporterConfig.endpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
|
|
614
|
+
logger.log(`@atrim/auto-trace: Using OTLPTraceExporter (${endpoint})`);
|
|
615
|
+
const otlpConfig = {
|
|
616
|
+
url: `${endpoint}/v1/traces`
|
|
617
|
+
};
|
|
618
|
+
if (exporterConfig.headers) {
|
|
619
|
+
otlpConfig.headers = exporterConfig.headers;
|
|
620
|
+
logger.log(`@atrim/auto-trace: Using custom headers: ${Object.keys(exporterConfig.headers).join(", ")}`);
|
|
621
|
+
}
|
|
622
|
+
const exporter = new OTLPTraceExporter(otlpConfig);
|
|
623
|
+
if (exporterConfig.processor === "simple") {
|
|
624
|
+
logger.log("@atrim/auto-trace: Using SimpleSpanProcessor");
|
|
625
|
+
return new SimpleSpanProcessor(exporter);
|
|
626
|
+
}
|
|
627
|
+
const batchConfig = exporterConfig.batch ?? {
|
|
628
|
+
scheduled_delay_millis: 1e3,
|
|
629
|
+
max_export_batch_size: 100
|
|
630
|
+
};
|
|
631
|
+
logger.log("@atrim/auto-trace: Using BatchSpanProcessor");
|
|
632
|
+
return new BatchSpanProcessor(exporter, {
|
|
633
|
+
scheduledDelayMillis: batchConfig.scheduled_delay_millis,
|
|
634
|
+
maxExportBatchSize: batchConfig.max_export_batch_size
|
|
635
|
+
});
|
|
636
|
+
};
|
|
637
|
+
const sdkLayer = NodeSdk.layer(() => ({
|
|
638
|
+
resource: {
|
|
639
|
+
serviceName,
|
|
640
|
+
serviceVersion
|
|
641
|
+
},
|
|
642
|
+
spanProcessor: createSpanProcessor()
|
|
643
|
+
}));
|
|
644
|
+
logger.log("@atrim/auto-trace: NodeSdk layer created - HTTP requests will be auto-traced");
|
|
645
|
+
return Layer.discard(sdkLayer);
|
|
646
|
+
})
|
|
647
|
+
);
|
|
648
|
+
};
|
|
649
|
+
var EffectTracingLive = createEffectTracingLayer();
|
|
650
|
+
var compiledRulesCache = /* @__PURE__ */ new WeakMap();
|
|
651
|
+
function compileNamingRules(rules) {
|
|
652
|
+
const cached = compiledRulesCache.get(rules);
|
|
653
|
+
if (cached) return cached;
|
|
654
|
+
const compiled = rules.map((rule) => ({
|
|
655
|
+
original: rule,
|
|
656
|
+
filePattern: rule.match.file ? new RegExp(rule.match.file) : void 0,
|
|
657
|
+
functionPattern: rule.match.function ? new RegExp(rule.match.function) : void 0,
|
|
658
|
+
modulePattern: rule.match.module ? new RegExp(rule.match.module) : void 0
|
|
659
|
+
}));
|
|
660
|
+
compiledRulesCache.set(rules, compiled);
|
|
661
|
+
return compiled;
|
|
662
|
+
}
|
|
663
|
+
function extractModuleName(filePath) {
|
|
664
|
+
const basename2 = path.basename(filePath, path.extname(filePath));
|
|
665
|
+
return basename2.replace(/\.(service|controller|handler|util|helper|model|repo|repository)$/i, "").replace(/(Service|Controller|Handler|Util|Helper|Model|Repo|Repository)$/i, "");
|
|
666
|
+
}
|
|
667
|
+
function applyTemplate(template, variables, matchGroups) {
|
|
668
|
+
let result = template;
|
|
669
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
670
|
+
result = result.replace(new RegExp(`\\{${key}\\}`, "g"), value);
|
|
671
|
+
}
|
|
672
|
+
if (matchGroups) {
|
|
673
|
+
result = result.replace(
|
|
674
|
+
/\{match:(\w+):(\d+)\}/g,
|
|
675
|
+
(_fullMatch, field, index) => {
|
|
676
|
+
const groups = matchGroups.get(field);
|
|
677
|
+
const idx = parseInt(index, 10);
|
|
678
|
+
if (groups && groups[idx]) {
|
|
679
|
+
return groups[idx];
|
|
680
|
+
}
|
|
681
|
+
return "";
|
|
682
|
+
}
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
return result;
|
|
686
|
+
}
|
|
687
|
+
function findMatchingRule(sourceInfo, config) {
|
|
688
|
+
const rules = config.span_naming?.rules;
|
|
689
|
+
if (!rules || rules.length === 0) return void 0;
|
|
690
|
+
const compiledRules = compileNamingRules(rules);
|
|
691
|
+
for (const rule of compiledRules) {
|
|
692
|
+
const matchGroups = /* @__PURE__ */ new Map();
|
|
693
|
+
let allMatched = true;
|
|
694
|
+
if (rule.filePattern) {
|
|
695
|
+
if (sourceInfo?.file) {
|
|
696
|
+
const match = sourceInfo.file.match(rule.filePattern);
|
|
697
|
+
if (match) {
|
|
698
|
+
matchGroups.set("file", match.slice(1));
|
|
699
|
+
} else {
|
|
700
|
+
allMatched = false;
|
|
701
|
+
}
|
|
702
|
+
} else {
|
|
703
|
+
allMatched = false;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if (rule.functionPattern && allMatched) {
|
|
707
|
+
if (sourceInfo?.function) {
|
|
708
|
+
const match = sourceInfo.function.match(rule.functionPattern);
|
|
709
|
+
if (match) {
|
|
710
|
+
matchGroups.set("function", match.slice(1));
|
|
711
|
+
} else {
|
|
712
|
+
allMatched = false;
|
|
713
|
+
}
|
|
714
|
+
} else {
|
|
715
|
+
allMatched = false;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
if (rule.modulePattern && allMatched) {
|
|
719
|
+
const moduleName = sourceInfo?.file ? extractModuleName(sourceInfo.file) : "";
|
|
720
|
+
if (moduleName) {
|
|
721
|
+
const match = moduleName.match(rule.modulePattern);
|
|
722
|
+
if (match) {
|
|
723
|
+
matchGroups.set("module", match.slice(1));
|
|
724
|
+
} else {
|
|
725
|
+
allMatched = false;
|
|
726
|
+
}
|
|
727
|
+
} else {
|
|
728
|
+
allMatched = false;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
if (allMatched) {
|
|
732
|
+
return { rule, matchGroups };
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return void 0;
|
|
736
|
+
}
|
|
737
|
+
function inferSpanName(fiberId, sourceInfo, config) {
|
|
738
|
+
const variables = {
|
|
739
|
+
fiber_id: String(fiberId),
|
|
740
|
+
function: sourceInfo?.function || "anonymous",
|
|
741
|
+
module: sourceInfo?.file ? extractModuleName(sourceInfo.file) : "unknown",
|
|
742
|
+
file: sourceInfo?.file || "unknown",
|
|
743
|
+
line: sourceInfo?.line ? String(sourceInfo.line) : "0",
|
|
744
|
+
operator: "gen"
|
|
745
|
+
// Default operator, could be enhanced with more context
|
|
746
|
+
};
|
|
747
|
+
const match = findMatchingRule(sourceInfo, config);
|
|
748
|
+
if (match) {
|
|
749
|
+
return applyTemplate(match.rule.original.name, variables, match.matchGroups);
|
|
750
|
+
}
|
|
751
|
+
const defaultTemplate = config.span_naming?.default || "effect.fiber.{fiber_id}";
|
|
752
|
+
return applyTemplate(defaultTemplate, variables);
|
|
753
|
+
}
|
|
754
|
+
function sanitizeSpanName(name) {
|
|
755
|
+
if (!name) return "effect.fiber.unknown";
|
|
756
|
+
let sanitized = name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
757
|
+
sanitized = sanitized.replace(/_+/g, "_");
|
|
758
|
+
sanitized = sanitized.replace(/^_+|_+$/g, "");
|
|
759
|
+
if (!sanitized) return "effect.fiber.unknown";
|
|
760
|
+
if (sanitized.length > 256) {
|
|
761
|
+
sanitized = sanitized.substring(0, 256);
|
|
762
|
+
}
|
|
763
|
+
return sanitized;
|
|
764
|
+
}
|
|
765
|
+
|
|
701
766
|
// src/integrations/effect/auto/supervisor.ts
|
|
702
767
|
var AutoTracingEnabled = FiberRef.unsafeMake(true);
|
|
703
768
|
var AutoTracingSpanName = FiberRef.unsafeMake(Option.none());
|
|
@@ -998,9 +1063,14 @@ var createExporterLayer = (exporterConfig, serviceName, serviceVersion) => {
|
|
|
998
1063
|
}
|
|
999
1064
|
const endpoint = config.endpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://localhost:4318";
|
|
1000
1065
|
logger.log(`@atrim/auto-trace: Using OTLPTraceExporter (${endpoint})`);
|
|
1001
|
-
|
|
1066
|
+
const exporterConfig2 = {
|
|
1002
1067
|
url: `${endpoint}/v1/traces`
|
|
1003
|
-
}
|
|
1068
|
+
};
|
|
1069
|
+
if (config.headers) {
|
|
1070
|
+
exporterConfig2.headers = config.headers;
|
|
1071
|
+
logger.log(`@atrim/auto-trace: Using custom headers: ${Object.keys(config.headers).join(", ")}`);
|
|
1072
|
+
}
|
|
1073
|
+
return new OTLPTraceExporter(exporterConfig2);
|
|
1004
1074
|
};
|
|
1005
1075
|
const createSpanProcessor = () => {
|
|
1006
1076
|
const exporter = createSpanExporter();
|
|
@@ -1061,6 +1131,6 @@ var createFullAutoTracingLayer = () => {
|
|
|
1061
1131
|
};
|
|
1062
1132
|
var FullAutoTracingLive = createFullAutoTracingLayer();
|
|
1063
1133
|
|
|
1064
|
-
export { AutoTracingConfig, AutoTracingConfigLayer, AutoTracingConfigLive, AutoTracingEnabled, AutoTracingLive, AutoTracingSpanName, AutoTracingSupervisor, FullAutoTracingLive, createAutoTracingLayer, createAutoTracingSupervisor, createFullAutoTracingLayer, defaultAutoTracingConfig, inferSpanName, loadAutoTracingConfig, loadAutoTracingConfigSync, sanitizeSpanName, setSpanName, withAutoTracing, withoutAutoTracing };
|
|
1134
|
+
export { AutoTracingConfig, AutoTracingConfigLayer, AutoTracingConfigLive, AutoTracingEnabled, AutoTracingLive, AutoTracingSpanName, AutoTracingSupervisor, EffectTracingLive, FullAutoTracingLive, createAutoTracingLayer, createAutoTracingSupervisor, createEffectTracingLayer, createFullAutoTracingLayer, defaultAutoTracingConfig, inferSpanName, loadAutoTracingConfig, loadAutoTracingConfigSync, sanitizeSpanName, setSpanName, withAutoTracing, withoutAutoTracing };
|
|
1065
1135
|
//# sourceMappingURL=index.js.map
|
|
1066
1136
|
//# sourceMappingURL=index.js.map
|