@agentuity/runtime 0.0.43 → 0.0.44
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/AGENTS.md +11 -9
- package/README.md +4 -4
- package/dist/_context.d.ts +12 -4
- package/dist/_context.d.ts.map +1 -1
- package/dist/_server.d.ts +7 -4
- package/dist/_server.d.ts.map +1 -1
- package/dist/_services.d.ts +13 -2
- package/dist/_services.d.ts.map +1 -1
- package/dist/_util.d.ts +1 -1
- package/dist/_util.d.ts.map +1 -1
- package/dist/_waituntil.d.ts +1 -3
- package/dist/_waituntil.d.ts.map +1 -1
- package/dist/agent.d.ts +41 -14
- package/dist/agent.d.ts.map +1 -1
- package/dist/app.d.ts +90 -8
- package/dist/app.d.ts.map +1 -1
- package/dist/eval.d.ts +79 -0
- package/dist/eval.d.ts.map +1 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/io/email.d.ts +77 -0
- package/dist/io/email.d.ts.map +1 -0
- package/dist/logger/console.d.ts +7 -1
- package/dist/logger/console.d.ts.map +1 -1
- package/dist/logger/user.d.ts.map +1 -1
- package/dist/otel/config.d.ts +3 -1
- package/dist/otel/config.d.ts.map +1 -1
- package/dist/otel/console.d.ts +2 -1
- package/dist/otel/console.d.ts.map +1 -1
- package/dist/otel/exporters/index.d.ts +4 -0
- package/dist/otel/exporters/index.d.ts.map +1 -0
- package/dist/otel/exporters/jsonl-log-exporter.d.ts +36 -0
- package/dist/otel/exporters/jsonl-log-exporter.d.ts.map +1 -0
- package/dist/otel/exporters/jsonl-metric-exporter.d.ts +40 -0
- package/dist/otel/exporters/jsonl-metric-exporter.d.ts.map +1 -0
- package/dist/otel/exporters/jsonl-trace-exporter.d.ts +36 -0
- package/dist/otel/exporters/jsonl-trace-exporter.d.ts.map +1 -0
- package/dist/otel/http.d.ts.map +1 -1
- package/dist/otel/logger.d.ts +8 -6
- package/dist/otel/logger.d.ts.map +1 -1
- package/dist/otel/otel.d.ts +8 -2
- package/dist/otel/otel.d.ts.map +1 -1
- package/dist/router.d.ts +4 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/services/evalrun/composite.d.ts +21 -0
- package/dist/services/evalrun/composite.d.ts.map +1 -0
- package/dist/services/evalrun/http.d.ts +24 -0
- package/dist/services/evalrun/http.d.ts.map +1 -0
- package/dist/services/evalrun/index.d.ts +5 -0
- package/dist/services/evalrun/index.d.ts.map +1 -0
- package/dist/services/evalrun/json.d.ts +21 -0
- package/dist/services/evalrun/json.d.ts.map +1 -0
- package/dist/services/evalrun/local.d.ts +19 -0
- package/dist/services/evalrun/local.d.ts.map +1 -0
- package/dist/services/local/_db.d.ts +4 -0
- package/dist/services/local/_db.d.ts.map +1 -0
- package/dist/services/local/_router.d.ts +3 -0
- package/dist/services/local/_router.d.ts.map +1 -0
- package/dist/services/local/_util.d.ts +18 -0
- package/dist/services/local/_util.d.ts.map +1 -0
- package/dist/services/local/index.d.ts +8 -0
- package/dist/services/local/index.d.ts.map +1 -0
- package/dist/services/local/keyvalue.d.ts +10 -0
- package/dist/services/local/keyvalue.d.ts.map +1 -0
- package/dist/services/local/objectstore.d.ts +11 -0
- package/dist/services/local/objectstore.d.ts.map +1 -0
- package/dist/services/local/stream.d.ts +10 -0
- package/dist/services/local/stream.d.ts.map +1 -0
- package/dist/services/local/vector.d.ts +13 -0
- package/dist/services/local/vector.d.ts.map +1 -0
- package/dist/services/session/composite.d.ts +21 -0
- package/dist/services/session/composite.d.ts.map +1 -0
- package/dist/services/session/http.d.ts +23 -0
- package/dist/services/session/http.d.ts.map +1 -0
- package/dist/services/session/index.d.ts +5 -0
- package/dist/services/session/index.d.ts.map +1 -0
- package/dist/services/session/json.d.ts +22 -0
- package/dist/services/session/json.d.ts.map +1 -0
- package/dist/services/session/local.d.ts +19 -0
- package/dist/services/session/local.d.ts.map +1 -0
- package/dist/session.d.ts +70 -0
- package/dist/session.d.ts.map +1 -0
- package/package.json +10 -6
- package/src/_config.ts +1 -1
- package/src/_context.ts +19 -16
- package/src/_server.ts +284 -42
- package/src/_services.ts +147 -34
- package/src/_util.ts +2 -3
- package/src/_waituntil.ts +5 -153
- package/src/agent.ts +667 -65
- package/src/app.ts +159 -13
- package/src/eval.ts +95 -0
- package/src/index.ts +6 -1
- package/src/io/email.ts +173 -0
- package/src/logger/console.ts +196 -17
- package/src/logger/user.ts +7 -3
- package/src/otel/config.ts +7 -44
- package/src/otel/console.ts +8 -4
- package/src/otel/exporters/README.md +217 -0
- package/src/otel/exporters/index.ts +3 -0
- package/src/otel/exporters/jsonl-log-exporter.ts +113 -0
- package/src/otel/exporters/jsonl-metric-exporter.ts +120 -0
- package/src/otel/exporters/jsonl-trace-exporter.ts +121 -0
- package/src/otel/http.ts +3 -1
- package/src/otel/logger.ts +87 -37
- package/src/otel/otel.ts +43 -22
- package/src/router.ts +44 -4
- package/src/services/evalrun/composite.ts +34 -0
- package/src/services/evalrun/http.ts +112 -0
- package/src/services/evalrun/index.ts +4 -0
- package/src/services/evalrun/json.ts +46 -0
- package/src/services/evalrun/local.ts +28 -0
- package/src/services/local/README.md +1576 -0
- package/src/services/local/_db.ts +182 -0
- package/src/services/local/_router.ts +86 -0
- package/src/services/local/_util.ts +49 -0
- package/src/services/local/index.ts +7 -0
- package/src/services/local/keyvalue.ts +118 -0
- package/src/services/local/objectstore.ts +152 -0
- package/src/services/local/stream.ts +296 -0
- package/src/services/local/vector.ts +264 -0
- package/src/services/session/composite.ts +33 -0
- package/src/services/session/http.ts +64 -0
- package/src/services/session/index.ts +4 -0
- package/src/services/session/json.ts +42 -0
- package/src/services/session/local.ts +28 -0
- package/src/session.ts +284 -0
- package/dist/_unauthenticated.d.ts +0 -26
- package/dist/_unauthenticated.d.ts.map +0 -1
- package/src/_unauthenticated.ts +0 -126
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { type ExportResult, ExportResultCode } from '@opentelemetry/core';
|
|
2
|
+
import type { LogRecordExporter, ReadableLogRecord } from '@opentelemetry/sdk-logs';
|
|
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 LogRecordExporter interface
|
|
9
|
+
* Writes logs to a timestamped JSONL file
|
|
10
|
+
*/
|
|
11
|
+
export class JSONLLogExporter implements LogRecordExporter {
|
|
12
|
+
private currentFile: string | null = null;
|
|
13
|
+
private readonly basePath: string;
|
|
14
|
+
private readonly filePrefix: string;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates a new JSONL log record exporter
|
|
18
|
+
* @param basePath - Directory to store the JSONL files
|
|
19
|
+
*/
|
|
20
|
+
constructor(basePath: string) {
|
|
21
|
+
this.basePath = basePath;
|
|
22
|
+
this.filePrefix = 'otel-log';
|
|
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 log records to a JSONL file
|
|
47
|
+
*
|
|
48
|
+
* @param logs - The log records to export
|
|
49
|
+
* @param resultCallback - Callback function to report the export result
|
|
50
|
+
*/
|
|
51
|
+
export(logs: ReadableLogRecord[], resultCallback: (result: ExportResult) => void): void {
|
|
52
|
+
try {
|
|
53
|
+
if (logs.length === 0) {
|
|
54
|
+
resultCallback({ code: ExportResultCode.SUCCESS });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const file = this.getOrCreateFile();
|
|
58
|
+
const lines: string[] = [];
|
|
59
|
+
for (const log of logs) {
|
|
60
|
+
const record = {
|
|
61
|
+
timestamp: log.hrTime,
|
|
62
|
+
observedTimestamp: log.hrTimeObserved,
|
|
63
|
+
severityNumber: log.severityNumber,
|
|
64
|
+
severityText: log.severityText,
|
|
65
|
+
body: log.body,
|
|
66
|
+
attributes: log.attributes,
|
|
67
|
+
resource: log.resource.attributes,
|
|
68
|
+
instrumentationScope: log.instrumentationScope,
|
|
69
|
+
spanContext: log.spanContext,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
lines.push(JSON.stringify(record));
|
|
73
|
+
}
|
|
74
|
+
const payload = `${lines.join('\n')}\n`;
|
|
75
|
+
try {
|
|
76
|
+
appendFileSync(file, payload, 'utf-8');
|
|
77
|
+
} catch (err) {
|
|
78
|
+
// File may have been deleted, reset and retry once
|
|
79
|
+
const code = (err as NodeJS.ErrnoException).code;
|
|
80
|
+
if (code === 'ENOENT') {
|
|
81
|
+
this.currentFile = null;
|
|
82
|
+
const newFile = this.getOrCreateFile();
|
|
83
|
+
appendFileSync(newFile, payload, 'utf-8');
|
|
84
|
+
} else {
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
resultCallback({ code: ExportResultCode.SUCCESS });
|
|
90
|
+
} catch (error) {
|
|
91
|
+
resultCallback({
|
|
92
|
+
code: ExportResultCode.FAILED,
|
|
93
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Shuts down the exporter
|
|
100
|
+
*
|
|
101
|
+
* @returns A promise that resolves when shutdown is complete
|
|
102
|
+
*/
|
|
103
|
+
async shutdown(): Promise<void> {
|
|
104
|
+
this.currentFile = null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Forces a flush of any pending data
|
|
109
|
+
*/
|
|
110
|
+
async forceFlush(): Promise<void> {
|
|
111
|
+
// No-op for file-based exporter as writes are synchronous
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -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/otel/http.ts
CHANGED
|
@@ -12,7 +12,9 @@ export function injectTraceContextToHeaders(
|
|
|
12
12
|
let _headers: Record<string, string>;
|
|
13
13
|
if (headers instanceof Headers) {
|
|
14
14
|
_headers = {};
|
|
15
|
-
headers.forEach((v, k) =>
|
|
15
|
+
headers.forEach((v, k) => {
|
|
16
|
+
_headers[k] = v;
|
|
17
|
+
});
|
|
16
18
|
} else {
|
|
17
19
|
_headers = { ...headers };
|
|
18
20
|
}
|
package/src/otel/logger.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { format } from 'node:util';
|
|
2
|
-
import { safeStringify } from '@agentuity/core';
|
|
2
|
+
import { safeStringify, type LogLevel } from '@agentuity/core';
|
|
3
3
|
import * as LogsAPI from '@opentelemetry/api-logs';
|
|
4
4
|
import type { Logger } from '../logger';
|
|
5
5
|
import ConsoleLogger from '../logger/console';
|
|
@@ -11,22 +11,21 @@ import { getAgentContext } from '../_context';
|
|
|
11
11
|
export const __originalConsole = Object.create(console); // save the original console before we patch it
|
|
12
12
|
|
|
13
13
|
export class OtelLogger implements Logger {
|
|
14
|
-
private readonly
|
|
14
|
+
private readonly delegate: LogsAPI.Logger;
|
|
15
15
|
private readonly context: Record<string, unknown> | undefined;
|
|
16
16
|
private readonly logger: ConsoleLogger | undefined;
|
|
17
|
+
private readonly logLevel: LogLevel;
|
|
17
18
|
|
|
18
19
|
constructor(
|
|
19
20
|
useConsole: boolean,
|
|
20
|
-
|
|
21
|
-
context?: Record<string, unknown> | undefined
|
|
21
|
+
delegate: LogsAPI.Logger,
|
|
22
|
+
context?: Record<string, unknown> | undefined,
|
|
23
|
+
logLevel?: LogLevel
|
|
22
24
|
) {
|
|
23
|
-
this.
|
|
25
|
+
this.delegate = delegate;
|
|
24
26
|
this.context = context;
|
|
25
|
-
this.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
addDelegate(delegate: LogsAPI.Logger) {
|
|
29
|
-
this.delegates.push(delegate);
|
|
27
|
+
this.logLevel = logLevel ?? 'info';
|
|
28
|
+
this.logger = useConsole ? new ConsoleLogger(context, false, this.logLevel) : undefined;
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
private formatMessage(message: unknown) {
|
|
@@ -62,25 +61,51 @@ export class OtelLogger implements Logger {
|
|
|
62
61
|
return this.context;
|
|
63
62
|
}
|
|
64
63
|
|
|
65
|
-
private
|
|
64
|
+
private emit(severityNumber: LogsAPI.SeverityNumber, severityText: string, body: string) {
|
|
66
65
|
const attributes = this.getAttributes();
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
67
|
+
try {
|
|
68
|
+
this.delegate.emit({
|
|
69
|
+
severityNumber,
|
|
70
|
+
severityText,
|
|
71
|
+
body,
|
|
72
|
+
attributes: attributes as LogsAPI.LogRecord['attributes'],
|
|
73
|
+
});
|
|
74
|
+
} catch (error) {
|
|
75
|
+
// Log error to console if available, but don't fail the entire operation
|
|
76
|
+
this.logger?.error('Failed to emit log to OTLP instance:', error);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private shouldLog(level: LogsAPI.SeverityNumber): boolean {
|
|
81
|
+
switch (this.logLevel) {
|
|
82
|
+
case 'trace':
|
|
83
|
+
return true;
|
|
84
|
+
case 'debug':
|
|
85
|
+
return (
|
|
86
|
+
level === LogsAPI.SeverityNumber.DEBUG ||
|
|
87
|
+
level == LogsAPI.SeverityNumber.INFO ||
|
|
88
|
+
level == LogsAPI.SeverityNumber.WARN ||
|
|
89
|
+
level == LogsAPI.SeverityNumber.ERROR
|
|
90
|
+
);
|
|
91
|
+
case 'info':
|
|
92
|
+
return (
|
|
93
|
+
level == LogsAPI.SeverityNumber.INFO ||
|
|
94
|
+
level == LogsAPI.SeverityNumber.WARN ||
|
|
95
|
+
level == LogsAPI.SeverityNumber.ERROR
|
|
96
|
+
);
|
|
97
|
+
case 'warn':
|
|
98
|
+
return level == LogsAPI.SeverityNumber.WARN || level == LogsAPI.SeverityNumber.ERROR;
|
|
99
|
+
case 'error':
|
|
100
|
+
return level == LogsAPI.SeverityNumber.ERROR;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
81
103
|
}
|
|
82
104
|
|
|
83
105
|
trace(message: unknown, ...args: unknown[]) {
|
|
106
|
+
if (!this.shouldLog(LogsAPI.SeverityNumber.TRACE)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
84
109
|
this.logger?.trace(message, ...args);
|
|
85
110
|
let body: string;
|
|
86
111
|
try {
|
|
@@ -89,9 +114,12 @@ export class OtelLogger implements Logger {
|
|
|
89
114
|
// Fallback if format causes recursion
|
|
90
115
|
body = `${this.formatMessage(message)} ${args.map((arg) => String(arg)).join(' ')}`;
|
|
91
116
|
}
|
|
92
|
-
this.
|
|
117
|
+
this.emit(LogsAPI.SeverityNumber.TRACE, 'TRACE', body);
|
|
93
118
|
}
|
|
94
119
|
debug(message: unknown, ...args: unknown[]) {
|
|
120
|
+
if (!this.shouldLog(LogsAPI.SeverityNumber.DEBUG)) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
95
123
|
this.logger?.debug(message, ...args);
|
|
96
124
|
let body: string;
|
|
97
125
|
try {
|
|
@@ -100,9 +128,12 @@ export class OtelLogger implements Logger {
|
|
|
100
128
|
// Fallback if format causes recursion
|
|
101
129
|
body = `${this.formatMessage(message)} ${args.map((arg) => String(arg)).join(' ')}`;
|
|
102
130
|
}
|
|
103
|
-
this.
|
|
131
|
+
this.emit(LogsAPI.SeverityNumber.DEBUG, 'DEBUG', body);
|
|
104
132
|
}
|
|
105
133
|
info(message: unknown, ...args: unknown[]) {
|
|
134
|
+
if (!this.shouldLog(LogsAPI.SeverityNumber.INFO)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
106
137
|
this.logger?.info(message, ...args);
|
|
107
138
|
let body: string;
|
|
108
139
|
try {
|
|
@@ -111,9 +142,12 @@ export class OtelLogger implements Logger {
|
|
|
111
142
|
// Fallback if format causes recursion
|
|
112
143
|
body = `${this.formatMessage(message)} ${args.map((arg) => String(arg)).join(' ')}`;
|
|
113
144
|
}
|
|
114
|
-
this.
|
|
145
|
+
this.emit(LogsAPI.SeverityNumber.INFO, 'INFO', body);
|
|
115
146
|
}
|
|
116
147
|
warn(message: unknown, ...args: unknown[]) {
|
|
148
|
+
if (!this.shouldLog(LogsAPI.SeverityNumber.WARN)) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
117
151
|
this.logger?.warn(message, ...args);
|
|
118
152
|
let body: string;
|
|
119
153
|
try {
|
|
@@ -122,9 +156,12 @@ export class OtelLogger implements Logger {
|
|
|
122
156
|
// Fallback if format causes recursion
|
|
123
157
|
body = `${this.formatMessage(message)} ${args.map((arg) => String(arg)).join(' ')}`;
|
|
124
158
|
}
|
|
125
|
-
this.
|
|
159
|
+
this.emit(LogsAPI.SeverityNumber.WARN, 'WARN', body);
|
|
126
160
|
}
|
|
127
161
|
error(message: unknown, ...args: unknown[]) {
|
|
162
|
+
if (!this.shouldLog(LogsAPI.SeverityNumber.ERROR)) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
128
165
|
this.logger?.error(message, ...args);
|
|
129
166
|
let body: string;
|
|
130
167
|
try {
|
|
@@ -133,17 +170,22 @@ export class OtelLogger implements Logger {
|
|
|
133
170
|
// Fallback if format causes recursion
|
|
134
171
|
body = `${this.formatMessage(message)} ${args.map((arg) => String(arg)).join(' ')}`;
|
|
135
172
|
}
|
|
136
|
-
this.
|
|
173
|
+
this.emit(LogsAPI.SeverityNumber.ERROR, 'ERROR', body);
|
|
137
174
|
}
|
|
138
175
|
fatal(message: unknown, ...args: unknown[]): never {
|
|
139
176
|
this.error(message, ...args);
|
|
140
177
|
process.exit(1);
|
|
141
178
|
}
|
|
142
179
|
child(opts: Record<string, unknown>): Logger {
|
|
143
|
-
return new OtelLogger(
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
180
|
+
return new OtelLogger(
|
|
181
|
+
!!this.logger,
|
|
182
|
+
this.delegate,
|
|
183
|
+
{
|
|
184
|
+
...(this.context ?? {}),
|
|
185
|
+
...opts,
|
|
186
|
+
},
|
|
187
|
+
this.logLevel
|
|
188
|
+
);
|
|
147
189
|
}
|
|
148
190
|
}
|
|
149
191
|
|
|
@@ -154,11 +196,15 @@ export class OtelLogger implements Logger {
|
|
|
154
196
|
* @param context - Additional context to include with log records
|
|
155
197
|
* @returns A logger instance
|
|
156
198
|
*/
|
|
157
|
-
export function createLogger(
|
|
199
|
+
export function createLogger(
|
|
200
|
+
useConsole: boolean,
|
|
201
|
+
context?: Record<string, unknown>,
|
|
202
|
+
logLevel?: LogLevel
|
|
203
|
+
): Logger {
|
|
158
204
|
const delegate = LogsAPI.logs.getLogger('default', undefined, {
|
|
159
205
|
scopeAttributes: context as LogsAPI.LoggerOptions['scopeAttributes'],
|
|
160
206
|
});
|
|
161
|
-
return new OtelLogger(useConsole,
|
|
207
|
+
return new OtelLogger(useConsole, delegate, context, logLevel);
|
|
162
208
|
}
|
|
163
209
|
|
|
164
210
|
/**
|
|
@@ -166,12 +212,16 @@ export function createLogger(useConsole: boolean, context?: Record<string, unkno
|
|
|
166
212
|
*
|
|
167
213
|
* @param attributes - Attributes to include with all console log records
|
|
168
214
|
*/
|
|
169
|
-
export function patchConsole(
|
|
215
|
+
export function patchConsole(
|
|
216
|
+
enabled: boolean,
|
|
217
|
+
attributes: Record<string, unknown>,
|
|
218
|
+
logLevel: LogLevel
|
|
219
|
+
) {
|
|
170
220
|
if (!enabled) {
|
|
171
221
|
return;
|
|
172
222
|
}
|
|
173
223
|
const _patch = { ...__originalConsole };
|
|
174
|
-
const delegate = createLogger(true, attributes);
|
|
224
|
+
const delegate = createLogger(true, attributes, logLevel);
|
|
175
225
|
|
|
176
226
|
// Patch individual console methods instead of reassigning the whole object
|
|
177
227
|
_patch.log = (...args: unknown[]) => {
|