@agentuity/telemetry 3.0.0-alpha.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/dist/console.d.ts +33 -0
- package/dist/console.d.ts.map +1 -0
- package/dist/console.js +86 -0
- package/dist/console.js.map +1 -0
- package/dist/exporters/index.d.ts +4 -0
- package/dist/exporters/index.d.ts.map +1 -0
- package/dist/exporters/index.js +4 -0
- package/dist/exporters/index.js.map +1 -0
- package/dist/exporters/jsonl-log-exporter.d.ts +36 -0
- package/dist/exporters/jsonl-log-exporter.d.ts.map +1 -0
- package/dist/exporters/jsonl-log-exporter.js +103 -0
- package/dist/exporters/jsonl-log-exporter.js.map +1 -0
- package/dist/exporters/jsonl-metric-exporter.d.ts +40 -0
- package/dist/exporters/jsonl-metric-exporter.d.ts.map +1 -0
- package/dist/exporters/jsonl-metric-exporter.js +104 -0
- package/dist/exporters/jsonl-metric-exporter.js.map +1 -0
- package/dist/exporters/jsonl-trace-exporter.d.ts +36 -0
- package/dist/exporters/jsonl-trace-exporter.d.ts.map +1 -0
- package/dist/exporters/jsonl-trace-exporter.js +111 -0
- package/dist/exporters/jsonl-trace-exporter.js.map +1 -0
- package/dist/fetch.d.ts +12 -0
- package/dist/fetch.d.ts.map +1 -0
- package/dist/fetch.js +82 -0
- package/dist/fetch.js.map +1 -0
- package/dist/globals.d.ts +9 -0
- package/dist/globals.d.ts.map +1 -0
- package/dist/globals.js +13 -0
- package/dist/globals.js.map +1 -0
- package/dist/http.d.ts +16 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +44 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/dist/logger/console.d.ts +69 -0
- package/dist/logger/console.d.ts.map +1 -0
- package/dist/logger/console.js +278 -0
- package/dist/logger/console.js.map +1 -0
- package/dist/logger/index.d.ts +4 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/index.js +3 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/internal.d.ts +79 -0
- package/dist/logger/internal.d.ts.map +1 -0
- package/dist/logger/internal.js +133 -0
- package/dist/logger/internal.js.map +1 -0
- package/dist/logger/user.d.ts +8 -0
- package/dist/logger/user.d.ts.map +1 -0
- package/dist/logger/user.js +7 -0
- package/dist/logger/user.js.map +1 -0
- package/dist/logger/util.d.ts +11 -0
- package/dist/logger/util.d.ts.map +1 -0
- package/dist/logger/util.js +77 -0
- package/dist/logger/util.js.map +1 -0
- package/dist/logger.d.ts +40 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +259 -0
- package/dist/logger.js.map +1 -0
- package/dist/telemetry.d.ts +71 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +274 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/tracestate.d.ts +44 -0
- package/dist/tracestate.d.ts.map +1 -0
- package/dist/tracestate.js +84 -0
- package/dist/tracestate.js.map +1 -0
- package/package.json +58 -0
- package/src/console.ts +91 -0
- package/src/exporters/README.md +217 -0
- package/src/exporters/index.ts +3 -0
- package/src/exporters/jsonl-log-exporter.ts +113 -0
- package/src/exporters/jsonl-metric-exporter.ts +120 -0
- package/src/exporters/jsonl-trace-exporter.ts +121 -0
- package/src/fetch.ts +105 -0
- package/src/globals.ts +18 -0
- package/src/http.ts +53 -0
- package/src/index.ts +82 -0
- package/src/logger/console.ts +322 -0
- package/src/logger/index.ts +3 -0
- package/src/logger/internal.ts +165 -0
- package/src/logger/user.ts +15 -0
- package/src/logger/util.ts +80 -0
- package/src/logger.ts +285 -0
- package/src/telemetry.ts +403 -0
- package/src/tracestate.ts +108 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { type ExportResult, ExportResultCode } from '@opentelemetry/core';
|
|
2
|
+
import {
|
|
3
|
+
type PushMetricExporter,
|
|
4
|
+
type ResourceMetrics,
|
|
5
|
+
AggregationTemporality,
|
|
6
|
+
InstrumentType,
|
|
7
|
+
} from '@opentelemetry/sdk-metrics';
|
|
8
|
+
import { existsSync, appendFileSync, mkdirSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* JSONL implementation of the PushMetricExporter interface
|
|
14
|
+
* Writes metrics to a timestamped JSONL file
|
|
15
|
+
*/
|
|
16
|
+
export class JSONLMetricExporter implements PushMetricExporter {
|
|
17
|
+
private currentFile: string | null = null;
|
|
18
|
+
private readonly basePath: string;
|
|
19
|
+
private readonly filePrefix: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Creates a new JSONL metric exporter
|
|
23
|
+
* @param basePath - Directory to store the JSONL files
|
|
24
|
+
*/
|
|
25
|
+
constructor(basePath: string) {
|
|
26
|
+
this.basePath = basePath;
|
|
27
|
+
this.filePrefix = 'otel-metric';
|
|
28
|
+
this.ensureDirectory();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private ensureDirectory(): void {
|
|
32
|
+
if (!existsSync(this.basePath)) {
|
|
33
|
+
mkdirSync(this.basePath, { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private getOrCreateFile(): string {
|
|
38
|
+
// If current file exists, use it
|
|
39
|
+
if (this.currentFile && existsSync(this.currentFile)) {
|
|
40
|
+
return this.currentFile;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
this.currentFile = join(
|
|
44
|
+
this.basePath,
|
|
45
|
+
`${this.filePrefix}-${Date.now()}.${randomUUID()}.jsonl`
|
|
46
|
+
);
|
|
47
|
+
return this.currentFile;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Exports metrics to a JSONL file
|
|
52
|
+
*
|
|
53
|
+
* @param metrics - The resource metrics to export
|
|
54
|
+
* @param resultCallback - Callback function to report the export result
|
|
55
|
+
*/
|
|
56
|
+
export(metrics: ResourceMetrics, resultCallback: (result: ExportResult) => void): void {
|
|
57
|
+
try {
|
|
58
|
+
const file = this.getOrCreateFile();
|
|
59
|
+
|
|
60
|
+
const record = {
|
|
61
|
+
resource: metrics.resource.attributes,
|
|
62
|
+
scopeMetrics: metrics.scopeMetrics.map((sm) => ({
|
|
63
|
+
scope: sm.scope,
|
|
64
|
+
metrics: sm.metrics.map((m) => ({
|
|
65
|
+
descriptor: m.descriptor,
|
|
66
|
+
dataPointType: m.dataPointType,
|
|
67
|
+
dataPoints: m.dataPoints,
|
|
68
|
+
aggregationTemporality: m.aggregationTemporality,
|
|
69
|
+
})),
|
|
70
|
+
})),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const line = JSON.stringify(record) + '\n';
|
|
74
|
+
try {
|
|
75
|
+
appendFileSync(file, line, 'utf-8');
|
|
76
|
+
} catch (err) {
|
|
77
|
+
// File may have been deleted, reset and retry once
|
|
78
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
79
|
+
if (code === 'ENOENT') {
|
|
80
|
+
this.currentFile = null;
|
|
81
|
+
const newFile = this.getOrCreateFile();
|
|
82
|
+
appendFileSync(newFile, line, 'utf-8');
|
|
83
|
+
} else {
|
|
84
|
+
throw err;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
resultCallback({ code: ExportResultCode.SUCCESS });
|
|
89
|
+
} catch (error) {
|
|
90
|
+
resultCallback({
|
|
91
|
+
code: ExportResultCode.FAILED,
|
|
92
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Shuts down the exporter
|
|
99
|
+
*
|
|
100
|
+
* @returns A promise that resolves when shutdown is complete
|
|
101
|
+
*/
|
|
102
|
+
async shutdown(): Promise<void> {
|
|
103
|
+
this.currentFile = null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Forces a flush of any pending data
|
|
108
|
+
*/
|
|
109
|
+
async forceFlush(): Promise<void> {
|
|
110
|
+
// No-op for file-based exporter as writes are synchronous
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Selects the aggregation temporality for the given instrument type
|
|
115
|
+
*/
|
|
116
|
+
selectAggregationTemporality?(_instrumentType: InstrumentType): AggregationTemporality {
|
|
117
|
+
// Default to cumulative temporality
|
|
118
|
+
return AggregationTemporality.CUMULATIVE;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { type ExportResult, ExportResultCode } from '@opentelemetry/core';
|
|
2
|
+
import type { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';
|
|
3
|
+
import { existsSync, appendFileSync, mkdirSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* JSONL implementation of the SpanExporter interface
|
|
9
|
+
* Writes traces to a timestamped JSONL file
|
|
10
|
+
*/
|
|
11
|
+
export class JSONLTraceExporter implements SpanExporter {
|
|
12
|
+
private currentFile: string | null = null;
|
|
13
|
+
private readonly basePath: string;
|
|
14
|
+
private readonly filePrefix: string;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates a new JSONL trace exporter
|
|
18
|
+
* @param basePath - Directory to store the JSONL files
|
|
19
|
+
*/
|
|
20
|
+
constructor(basePath: string) {
|
|
21
|
+
this.basePath = basePath;
|
|
22
|
+
this.filePrefix = 'otel-trace';
|
|
23
|
+
this.ensureDirectory();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private ensureDirectory(): void {
|
|
27
|
+
if (!existsSync(this.basePath)) {
|
|
28
|
+
mkdirSync(this.basePath, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private getOrCreateFile(): string {
|
|
33
|
+
// If current file exists, use it
|
|
34
|
+
if (this.currentFile && existsSync(this.currentFile)) {
|
|
35
|
+
return this.currentFile;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this.currentFile = join(
|
|
39
|
+
this.basePath,
|
|
40
|
+
`${this.filePrefix}-${Date.now()}.${randomUUID()}.jsonl`
|
|
41
|
+
);
|
|
42
|
+
return this.currentFile;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Exports spans to a JSONL file
|
|
47
|
+
*
|
|
48
|
+
* @param spans - The spans to export
|
|
49
|
+
* @param resultCallback - Callback function to report the export result
|
|
50
|
+
*/
|
|
51
|
+
export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void {
|
|
52
|
+
try {
|
|
53
|
+
if (spans.length === 0) {
|
|
54
|
+
resultCallback({ code: ExportResultCode.SUCCESS });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const file = this.getOrCreateFile();
|
|
58
|
+
const lines: string[] = [];
|
|
59
|
+
for (const span of spans) {
|
|
60
|
+
const record = {
|
|
61
|
+
traceId: span.spanContext().traceId,
|
|
62
|
+
spanId: span.spanContext().spanId,
|
|
63
|
+
traceState: span.spanContext().traceState?.serialize(),
|
|
64
|
+
name: span.name,
|
|
65
|
+
kind: span.kind,
|
|
66
|
+
startTime: span.startTime,
|
|
67
|
+
endTime: span.endTime,
|
|
68
|
+
attributes: span.attributes,
|
|
69
|
+
status: span.status,
|
|
70
|
+
events: span.events,
|
|
71
|
+
links: span.links,
|
|
72
|
+
resource: span.resource.attributes,
|
|
73
|
+
droppedAttributesCount: span.droppedAttributesCount,
|
|
74
|
+
droppedEventsCount: span.droppedEventsCount,
|
|
75
|
+
droppedLinksCount: span.droppedLinksCount,
|
|
76
|
+
duration: span.duration,
|
|
77
|
+
ended: span.ended,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
lines.push(JSON.stringify(record));
|
|
81
|
+
}
|
|
82
|
+
const payload = `${lines.join('\n')}\n`;
|
|
83
|
+
try {
|
|
84
|
+
appendFileSync(file, payload, 'utf-8');
|
|
85
|
+
} catch (err) {
|
|
86
|
+
// File may have been deleted, reset and retry once
|
|
87
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
88
|
+
if (code === 'ENOENT') {
|
|
89
|
+
this.currentFile = null;
|
|
90
|
+
const newFile = this.getOrCreateFile();
|
|
91
|
+
appendFileSync(newFile, payload, 'utf-8');
|
|
92
|
+
} else {
|
|
93
|
+
throw err;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
resultCallback({ code: ExportResultCode.SUCCESS });
|
|
98
|
+
} catch (error) {
|
|
99
|
+
resultCallback({
|
|
100
|
+
code: ExportResultCode.FAILED,
|
|
101
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Shuts down the exporter
|
|
108
|
+
*
|
|
109
|
+
* @returns A promise that resolves when shutdown is complete
|
|
110
|
+
*/
|
|
111
|
+
async shutdown(): Promise<void> {
|
|
112
|
+
this.currentFile = null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Forces a flush of any pending data
|
|
117
|
+
*/
|
|
118
|
+
async forceFlush(): Promise<void> {
|
|
119
|
+
// No-op for file-based exporter as writes are synchronous
|
|
120
|
+
}
|
|
121
|
+
}
|
package/src/fetch.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { context, propagation, SpanStatusCode, trace } from '@opentelemetry/api';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reference to the original fetch function before instrumentation
|
|
5
|
+
*/
|
|
6
|
+
export const __originalFetch = fetch; // save the original fetch before we patch it
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Instruments the global fetch function with OpenTelemetry tracing
|
|
10
|
+
*
|
|
11
|
+
* Replaces the global fetch with an instrumented version that creates spans
|
|
12
|
+
* for each HTTP request and propagates trace context in headers
|
|
13
|
+
*/
|
|
14
|
+
export function instrumentFetch() {
|
|
15
|
+
const patch = async (
|
|
16
|
+
input: string | Request | URL,
|
|
17
|
+
init: RequestInit | undefined
|
|
18
|
+
): Promise<Response> => {
|
|
19
|
+
const url =
|
|
20
|
+
typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;
|
|
21
|
+
|
|
22
|
+
const method =
|
|
23
|
+
init?.method ||
|
|
24
|
+
(typeof input !== 'string' && !(input instanceof URL) ? input.method || 'GET' : 'GET');
|
|
25
|
+
|
|
26
|
+
// Get the active span if it exists
|
|
27
|
+
const activeSpan = trace.getActiveSpan();
|
|
28
|
+
|
|
29
|
+
// If there's no active span, just call the original fetch
|
|
30
|
+
if (!activeSpan) {
|
|
31
|
+
return __originalFetch(input, init);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Get the current active context
|
|
35
|
+
const currentContext = context.active();
|
|
36
|
+
const _url = new URL(url);
|
|
37
|
+
|
|
38
|
+
// Create a child span using the current context
|
|
39
|
+
const childSpan = trace.getTracer('fetch').startSpan(
|
|
40
|
+
`${method} ${_url.pathname}`,
|
|
41
|
+
{
|
|
42
|
+
attributes: {
|
|
43
|
+
'http.url': url,
|
|
44
|
+
'http.path': _url.pathname,
|
|
45
|
+
'http.method': method,
|
|
46
|
+
host: _url.host,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
currentContext
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
// Prepare trace context injection
|
|
54
|
+
const carrier: Record<string, string> = {};
|
|
55
|
+
|
|
56
|
+
// Create a new context with the child span
|
|
57
|
+
const newContext = trace.setSpan(currentContext, childSpan);
|
|
58
|
+
|
|
59
|
+
// Use the new context for propagation
|
|
60
|
+
propagation.inject(newContext, carrier);
|
|
61
|
+
|
|
62
|
+
// Preserve original headers and add trace context
|
|
63
|
+
// Handle headers from both Request input and init parameter
|
|
64
|
+
const baseHeaders =
|
|
65
|
+
typeof input !== 'string' && !(input instanceof URL) && input instanceof Request
|
|
66
|
+
? input.headers
|
|
67
|
+
: undefined;
|
|
68
|
+
const headers = new Headers(baseHeaders ?? init?.headers ?? {});
|
|
69
|
+
|
|
70
|
+
// Add trace context headers (overwriting any already present)
|
|
71
|
+
for (const [key, value] of Object.entries(carrier)) {
|
|
72
|
+
headers.set(key, value);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Create new init object with updated headers
|
|
76
|
+
const newInit = {
|
|
77
|
+
...init,
|
|
78
|
+
headers,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const response = await __originalFetch(input, newInit);
|
|
82
|
+
|
|
83
|
+
// Add response attributes to span
|
|
84
|
+
childSpan.setAttributes({
|
|
85
|
+
'http.status_code': response.status,
|
|
86
|
+
'http.user_agent': response.headers.get('user-agent') || '',
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
childSpan.setStatus({ code: SpanStatusCode.ERROR });
|
|
91
|
+
} else {
|
|
92
|
+
childSpan.setStatus({ code: SpanStatusCode.OK });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return response;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
childSpan.recordException(error as Error);
|
|
98
|
+
childSpan.setStatus({ code: SpanStatusCode.ERROR });
|
|
99
|
+
throw error;
|
|
100
|
+
} finally {
|
|
101
|
+
childSpan.end();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
globalThis.fetch = patch as typeof fetch;
|
|
105
|
+
}
|
package/src/globals.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global state for Telemetry instance (survives hot reloads)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { TelemetryResponse } from './telemetry';
|
|
6
|
+
|
|
7
|
+
const telemetryInstanceKey = Symbol.for('@agentuity/telemetry:instance');
|
|
8
|
+
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
const g = globalThis as any;
|
|
11
|
+
|
|
12
|
+
export const telemetry = {
|
|
13
|
+
get: (): TelemetryResponse | undefined =>
|
|
14
|
+
g[telemetryInstanceKey] as TelemetryResponse | undefined,
|
|
15
|
+
set: (v: TelemetryResponse): void => {
|
|
16
|
+
g[telemetryInstanceKey] = v;
|
|
17
|
+
},
|
|
18
|
+
};
|
package/src/http.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { context, propagation } from '@opentelemetry/api';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Injects trace context into response headers using the OpenTelemetry propagation API
|
|
5
|
+
*
|
|
6
|
+
* @param headers - Optional existing headers to include
|
|
7
|
+
* @returns A record of headers with trace context injected
|
|
8
|
+
*/
|
|
9
|
+
export function injectTraceContextToHeaders(
|
|
10
|
+
headers: Record<string, string> | Headers = {}
|
|
11
|
+
): Record<string, string> {
|
|
12
|
+
let _headers: Record<string, string>;
|
|
13
|
+
if (headers instanceof Headers) {
|
|
14
|
+
_headers = {};
|
|
15
|
+
headers.forEach((v, k) => {
|
|
16
|
+
_headers[k] = v;
|
|
17
|
+
});
|
|
18
|
+
} else {
|
|
19
|
+
_headers = { ...headers };
|
|
20
|
+
}
|
|
21
|
+
// Create a carrier object for the headers
|
|
22
|
+
const carrier: Record<string, string> = { ..._headers } as Record<string, string>;
|
|
23
|
+
|
|
24
|
+
// Get the current context
|
|
25
|
+
const currentContext = context.active();
|
|
26
|
+
|
|
27
|
+
// Inject trace context into the carrier
|
|
28
|
+
propagation.inject(currentContext, carrier);
|
|
29
|
+
|
|
30
|
+
return carrier;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Extracts trace context from Bun Request headers
|
|
35
|
+
*
|
|
36
|
+
* @param req - The Bun Request object
|
|
37
|
+
* @returns The context with trace information
|
|
38
|
+
*/
|
|
39
|
+
export function extractTraceContextFromRequest(
|
|
40
|
+
req: Request
|
|
41
|
+
): ReturnType<typeof propagation.extract> {
|
|
42
|
+
// Create a carrier object from the headers
|
|
43
|
+
const carrier: Record<string, string> = {};
|
|
44
|
+
|
|
45
|
+
// Convert headers to the format expected by the propagator
|
|
46
|
+
req.headers.forEach((value, key) => {
|
|
47
|
+
carrier[key.toLowerCase()] = value;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Extract the context using the global propagator
|
|
51
|
+
const activeContext = context.active();
|
|
52
|
+
return propagation.extract(activeContext, carrier);
|
|
53
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @agentuity/telemetry - OpenTelemetry telemetry for Agentuity
|
|
3
|
+
*
|
|
4
|
+
* Auto-initializes from environment variables on import (Vercel-style).
|
|
5
|
+
*
|
|
6
|
+
* @example Automatic initialization (recommended)
|
|
7
|
+
* ```typescript
|
|
8
|
+
* // Just import - auto-configures from AGENTUITY_* env vars
|
|
9
|
+
* import '@agentuity/telemetry';
|
|
10
|
+
*
|
|
11
|
+
* // Then access the globals anywhere
|
|
12
|
+
* import { tracer, logger, meter } from '@agentuity/telemetry';
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* @example Explicit configuration
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { register } from '@agentuity/telemetry';
|
|
18
|
+
*
|
|
19
|
+
* register({
|
|
20
|
+
* name: 'my-app',
|
|
21
|
+
* version: '1.0.0',
|
|
22
|
+
* // ... optional overrides
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
// Re-export types
|
|
28
|
+
export type { Logger } from './logger';
|
|
29
|
+
|
|
30
|
+
// Re-export console reference for custom loggers
|
|
31
|
+
export { __originalConsole } from './logger';
|
|
32
|
+
export type { TelemetryConfig, TelemetryResponse } from './telemetry';
|
|
33
|
+
|
|
34
|
+
// Re-export HTTP utilities for trace context propagation
|
|
35
|
+
export { injectTraceContextToHeaders, extractTraceContextFromRequest } from './http';
|
|
36
|
+
|
|
37
|
+
// Re-export trace state utilities
|
|
38
|
+
export {
|
|
39
|
+
enrichContextWithTraceState,
|
|
40
|
+
generateTraceId,
|
|
41
|
+
generateSpanId,
|
|
42
|
+
type TraceStateEntries,
|
|
43
|
+
} from './tracestate';
|
|
44
|
+
|
|
45
|
+
// Core registration function
|
|
46
|
+
export { register, registerTelemetry, getTelemetry, ensureInitialized } from './telemetry';
|
|
47
|
+
|
|
48
|
+
// Lazy-loaded exports - auto-initialized from env vars
|
|
49
|
+
import type { Tracer, Meter } from '@opentelemetry/api';
|
|
50
|
+
import type { Logger } from './logger';
|
|
51
|
+
import { ensureInitialized } from './telemetry';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get the OpenTelemetry tracer (auto-initialized)
|
|
55
|
+
*/
|
|
56
|
+
export const tracer: Tracer = new Proxy({} as Tracer, {
|
|
57
|
+
get: (_, prop) => ensureInitialized().tracer[prop as keyof Tracer],
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get the OpenTelemetry meter (auto-initialized)
|
|
62
|
+
*/
|
|
63
|
+
export const meter: Meter = new Proxy({} as Meter, {
|
|
64
|
+
get: (_, prop) => ensureInitialized().meter[prop as keyof Meter],
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get the Logger instance (auto-initialized)
|
|
69
|
+
*/
|
|
70
|
+
export const logger: Logger = new Proxy({} as Logger, {
|
|
71
|
+
get: (_, prop) => ensureInitialized().logger[prop as keyof Logger],
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Shutdown telemetry (call on process exit)
|
|
76
|
+
*/
|
|
77
|
+
export async function shutdown(): Promise<void> {
|
|
78
|
+
const telemetry = ensureInitialized();
|
|
79
|
+
if (telemetry?.shutdown) {
|
|
80
|
+
await telemetry.shutdown();
|
|
81
|
+
}
|
|
82
|
+
}
|