@agentuity/runtime 0.0.43 → 0.0.45
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
package/src/logger/console.ts
CHANGED
|
@@ -1,12 +1,114 @@
|
|
|
1
|
+
import type { LogLevel } from '@agentuity/core';
|
|
1
2
|
import { __originalConsole } from '../otel/logger';
|
|
2
3
|
import type { Logger } from './logger';
|
|
3
4
|
import { formatMessage } from './util';
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
const BOLD = '\x1b[1m';
|
|
7
|
+
const RESET = '\x1b[0m';
|
|
8
|
+
|
|
9
|
+
// Helper to convert hex color to ANSI 24-bit color code
|
|
10
|
+
function hexToAnsi(hex: string): string {
|
|
11
|
+
const r = parseInt(hex.slice(1, 3), 16);
|
|
12
|
+
const g = parseInt(hex.slice(3, 5), 16);
|
|
13
|
+
const b = parseInt(hex.slice(5, 7), 16);
|
|
14
|
+
return `\x1b[38;2;${r};${g};${b}m`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface LogColors {
|
|
18
|
+
level: string;
|
|
19
|
+
message: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function shouldUseColors(): boolean {
|
|
23
|
+
// Check for NO_COLOR environment variable (any non-empty value disables colors)
|
|
24
|
+
if (process.env.NO_COLOR) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Check for TERM=dumb
|
|
29
|
+
if (process.env.TERM === 'dumb') {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Check if stdout is a TTY
|
|
34
|
+
if (!process.stdout || typeof process.stdout.isTTY === 'undefined') {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (process.stdout && typeof process.stdout.isTTY !== 'undefined' && !process.stdout.isTTY) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type ColorScheme = 'light' | 'dark';
|
|
46
|
+
|
|
47
|
+
function getLogColors(scheme: ColorScheme): Record<LogLevel, LogColors> {
|
|
48
|
+
if (scheme === 'light') {
|
|
49
|
+
// Darker, high-contrast colors for light backgrounds
|
|
50
|
+
return {
|
|
51
|
+
trace: {
|
|
52
|
+
level: hexToAnsi('#008B8B') + BOLD, // Dark cyan
|
|
53
|
+
message: hexToAnsi('#4B4B4B'), // Dark gray
|
|
54
|
+
},
|
|
55
|
+
debug: {
|
|
56
|
+
level: hexToAnsi('#0000CD') + BOLD, // Medium blue
|
|
57
|
+
message: hexToAnsi('#006400'), // Dark green
|
|
58
|
+
},
|
|
59
|
+
info: {
|
|
60
|
+
level: hexToAnsi('#FF8C00') + BOLD, // Dark orange
|
|
61
|
+
message: hexToAnsi('#0066CC') + BOLD, // Strong blue
|
|
62
|
+
},
|
|
63
|
+
warn: {
|
|
64
|
+
level: hexToAnsi('#9400D3') + BOLD, // Dark violet
|
|
65
|
+
message: hexToAnsi('#8B008B'), // Dark magenta
|
|
66
|
+
},
|
|
67
|
+
error: {
|
|
68
|
+
level: hexToAnsi('#DC143C') + BOLD, // Crimson
|
|
69
|
+
message: hexToAnsi('#8B0000') + BOLD, // Dark red
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Dark mode colors (brighter for dark backgrounds)
|
|
75
|
+
return {
|
|
76
|
+
trace: {
|
|
77
|
+
level: hexToAnsi('#00FFFF') + BOLD, // Cyan
|
|
78
|
+
message: hexToAnsi('#A0A0A0'), // Light gray
|
|
79
|
+
},
|
|
80
|
+
debug: {
|
|
81
|
+
level: hexToAnsi('#5C9CFF') + BOLD, // Blue
|
|
82
|
+
message: hexToAnsi('#90EE90'), // Light green
|
|
83
|
+
},
|
|
84
|
+
info: {
|
|
85
|
+
level: hexToAnsi('#FFD700') + BOLD, // Gold/Yellow
|
|
86
|
+
message: hexToAnsi('#FFFFFF') + BOLD, // White
|
|
87
|
+
},
|
|
88
|
+
warn: {
|
|
89
|
+
level: hexToAnsi('#FF00FF') + BOLD, // Magenta
|
|
90
|
+
message: hexToAnsi('#FF00FF'), // Magenta
|
|
91
|
+
},
|
|
92
|
+
error: {
|
|
93
|
+
level: hexToAnsi('#FF4444') + BOLD, // Red
|
|
94
|
+
message: hexToAnsi('#FF4444'), // Red
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Detect color scheme from environment
|
|
100
|
+
function detectColorScheme(): ColorScheme {
|
|
101
|
+
const scheme = process.env.COLOR_SCHEME?.toLowerCase();
|
|
102
|
+
if (scheme === 'light' || scheme === 'dark') {
|
|
103
|
+
return scheme;
|
|
104
|
+
}
|
|
105
|
+
if (process.env.CI) {
|
|
106
|
+
return 'light';
|
|
107
|
+
}
|
|
108
|
+
return 'dark'; // Default to dark mode
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const NOCOLORS = Object.freeze({ level: '', reset: '', message: '' });
|
|
10
112
|
|
|
11
113
|
/**
|
|
12
114
|
* Console implementation of the Logger interface
|
|
@@ -14,15 +116,44 @@ const reset = '\x1b[0m';
|
|
|
14
116
|
export default class ConsoleLogger implements Logger {
|
|
15
117
|
private context: Record<string, unknown>;
|
|
16
118
|
private formatContext: boolean;
|
|
119
|
+
private logLevel: LogLevel;
|
|
120
|
+
private colors: Record<LogLevel, LogColors>;
|
|
121
|
+
private detectedTraceLoopLog: boolean | undefined;
|
|
122
|
+
private useColors: boolean;
|
|
17
123
|
|
|
18
124
|
/**
|
|
19
125
|
* Creates a new console logger
|
|
20
126
|
*
|
|
21
127
|
* @param context - Initial context for the logger
|
|
22
128
|
*/
|
|
23
|
-
constructor(
|
|
129
|
+
constructor(
|
|
130
|
+
context: Record<string, unknown> = {},
|
|
131
|
+
formatContext = true,
|
|
132
|
+
logLevel: LogLevel = 'info'
|
|
133
|
+
) {
|
|
24
134
|
this.context = context;
|
|
25
135
|
this.formatContext = formatContext;
|
|
136
|
+
this.logLevel = logLevel;
|
|
137
|
+
this.useColors = shouldUseColors();
|
|
138
|
+
this.colors = this.useColors
|
|
139
|
+
? getLogColors(detectColorScheme())
|
|
140
|
+
: ({} as Record<LogLevel, LogColors>);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private shouldLog(level: LogLevel): boolean {
|
|
144
|
+
switch (this.logLevel) {
|
|
145
|
+
case 'trace':
|
|
146
|
+
return true;
|
|
147
|
+
case 'debug':
|
|
148
|
+
return level === 'debug' || level === 'info' || level === 'warn' || level === 'error';
|
|
149
|
+
case 'info':
|
|
150
|
+
return level === 'info' || level === 'warn' || level === 'error';
|
|
151
|
+
case 'warn':
|
|
152
|
+
return level === 'warn' || level === 'error';
|
|
153
|
+
case 'error':
|
|
154
|
+
return level === 'error';
|
|
155
|
+
}
|
|
156
|
+
return false;
|
|
26
157
|
}
|
|
27
158
|
|
|
28
159
|
/**
|
|
@@ -32,12 +163,19 @@ export default class ConsoleLogger implements Logger {
|
|
|
32
163
|
* @param args - Additional arguments to log
|
|
33
164
|
*/
|
|
34
165
|
trace(message: unknown, ...args: unknown[]): void {
|
|
166
|
+
if (!this.shouldLog('trace')) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
35
169
|
try {
|
|
170
|
+
const colors = this.useColors ? this.colors.trace : NOCOLORS;
|
|
36
171
|
const formattedMessage = formatMessage(this.formatContext, this.context, message, args);
|
|
37
|
-
__originalConsole.debug(
|
|
172
|
+
__originalConsole.debug(
|
|
173
|
+
`${colors.level}[TRACE]${RESET} ${colors.message}${formattedMessage}${RESET}`
|
|
174
|
+
);
|
|
38
175
|
} catch (err) {
|
|
39
176
|
// Fallback to direct logging if formatting fails
|
|
40
|
-
|
|
177
|
+
const colors = this.colors.trace;
|
|
178
|
+
__originalConsole.debug(`${colors.level}[TRACE]${RESET} ${message}`, ...args);
|
|
41
179
|
__originalConsole.error('Error formatting log message:', err);
|
|
42
180
|
}
|
|
43
181
|
}
|
|
@@ -49,12 +187,19 @@ export default class ConsoleLogger implements Logger {
|
|
|
49
187
|
* @param args - Additional arguments to log
|
|
50
188
|
*/
|
|
51
189
|
debug(message: unknown, ...args: unknown[]): void {
|
|
190
|
+
if (!this.shouldLog('debug')) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
52
193
|
try {
|
|
194
|
+
const colors = this.useColors ? this.colors.debug : NOCOLORS;
|
|
53
195
|
const formattedMessage = formatMessage(this.formatContext, this.context, message, args);
|
|
54
|
-
__originalConsole.debug(
|
|
196
|
+
__originalConsole.debug(
|
|
197
|
+
`${colors.level}[DEBUG]${RESET} ${colors.message}${formattedMessage}${RESET}`
|
|
198
|
+
);
|
|
55
199
|
} catch (err) {
|
|
56
200
|
// Fallback to direct logging if formatting fails
|
|
57
|
-
|
|
201
|
+
const colors = this.colors.debug;
|
|
202
|
+
__originalConsole.debug(`${colors.level}[DEBUG]${RESET} ${message}`, ...args);
|
|
58
203
|
__originalConsole.error('Error formatting log message:', err);
|
|
59
204
|
}
|
|
60
205
|
}
|
|
@@ -66,12 +211,31 @@ export default class ConsoleLogger implements Logger {
|
|
|
66
211
|
* @param args - Additional arguments to log
|
|
67
212
|
*/
|
|
68
213
|
info(message: unknown, ...args: unknown[]): void {
|
|
214
|
+
if (!this.shouldLog('info')) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
// suppress the default traceloop message at info level
|
|
218
|
+
if (
|
|
219
|
+
!this.detectedTraceLoopLog &&
|
|
220
|
+
typeof message === 'string' &&
|
|
221
|
+
message.includes('Traceloop exporting traces to')
|
|
222
|
+
) {
|
|
223
|
+
this.detectedTraceLoopLog = true;
|
|
224
|
+
if (this.shouldLog('debug')) {
|
|
225
|
+
this.debug(message, ...args);
|
|
226
|
+
}
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
69
229
|
try {
|
|
230
|
+
const colors = this.useColors ? this.colors.info : NOCOLORS;
|
|
70
231
|
const formattedMessage = formatMessage(this.formatContext, this.context, message, args);
|
|
71
|
-
__originalConsole.info(
|
|
232
|
+
__originalConsole.info(
|
|
233
|
+
`${colors.level}[INFO]${RESET} ${colors.message}${formattedMessage}${RESET}`
|
|
234
|
+
);
|
|
72
235
|
} catch (err) {
|
|
73
236
|
// Fallback to direct logging if formatting fails
|
|
74
|
-
|
|
237
|
+
const colors = this.colors.info;
|
|
238
|
+
__originalConsole.info(`${colors.level}[INFO]${RESET} ${message}`, ...args);
|
|
75
239
|
__originalConsole.error('Error formatting log message:', err);
|
|
76
240
|
}
|
|
77
241
|
}
|
|
@@ -83,12 +247,19 @@ export default class ConsoleLogger implements Logger {
|
|
|
83
247
|
* @param args - Additional arguments to log
|
|
84
248
|
*/
|
|
85
249
|
warn(message: unknown, ...args: unknown[]): void {
|
|
250
|
+
if (!this.shouldLog('warn')) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
86
253
|
try {
|
|
254
|
+
const colors = this.useColors ? this.colors.warn : NOCOLORS;
|
|
87
255
|
const formattedMessage = formatMessage(this.formatContext, this.context, message, args);
|
|
88
|
-
__originalConsole.warn(
|
|
256
|
+
__originalConsole.warn(
|
|
257
|
+
`${colors.level}[WARN]${RESET} ${colors.message}${formattedMessage}${RESET}`
|
|
258
|
+
);
|
|
89
259
|
} catch (err) {
|
|
90
260
|
// Fallback to direct logging if formatting fails
|
|
91
|
-
|
|
261
|
+
const colors = this.colors.warn;
|
|
262
|
+
__originalConsole.warn(`${colors.level}[WARN]${RESET} ${message}`, ...args);
|
|
92
263
|
__originalConsole.error('Error formatting log message:', err);
|
|
93
264
|
}
|
|
94
265
|
}
|
|
@@ -100,12 +271,19 @@ export default class ConsoleLogger implements Logger {
|
|
|
100
271
|
* @param args - Additional arguments to log
|
|
101
272
|
*/
|
|
102
273
|
error(message: unknown, ...args: unknown[]): void {
|
|
274
|
+
if (!this.shouldLog('error')) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
103
277
|
try {
|
|
278
|
+
const colors = this.useColors ? this.colors.error : NOCOLORS;
|
|
104
279
|
const formattedMessage = formatMessage(this.formatContext, this.context, message, args);
|
|
105
|
-
__originalConsole.error(
|
|
280
|
+
__originalConsole.error(
|
|
281
|
+
`${colors.level}[ERROR]${RESET} ${colors.message}${formattedMessage}${RESET}`
|
|
282
|
+
);
|
|
106
283
|
} catch (err) {
|
|
107
284
|
// Fallback to direct logging if formatting fails
|
|
108
|
-
|
|
285
|
+
const colors = this.colors.error;
|
|
286
|
+
__originalConsole.error(`${colors.level}[ERROR]${RESET} ${message}`, ...args);
|
|
109
287
|
__originalConsole.error('Error formatting log message:', err);
|
|
110
288
|
}
|
|
111
289
|
}
|
|
@@ -133,7 +311,8 @@ export default class ConsoleLogger implements Logger {
|
|
|
133
311
|
...this.context,
|
|
134
312
|
...opts,
|
|
135
313
|
},
|
|
136
|
-
this.formatContext
|
|
314
|
+
this.formatContext,
|
|
315
|
+
this.logLevel
|
|
137
316
|
);
|
|
138
317
|
}
|
|
139
318
|
}
|
package/src/logger/user.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import { createLogger } from '@agentuity/server';
|
|
2
|
-
import type { Logger } from '@agentuity/core';
|
|
1
|
+
import { type ColorScheme, createLogger } from '@agentuity/server';
|
|
2
|
+
import type { LogLevel, Logger } from '@agentuity/core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* User-facing logger instance
|
|
6
6
|
* This is the logger that SDK consumers should use
|
|
7
7
|
*/
|
|
8
|
-
export const logger: Logger = createLogger(
|
|
8
|
+
export const logger: Logger = createLogger(
|
|
9
|
+
(process.env.AGENTUITY_LOG_LEVEL || 'info') as LogLevel,
|
|
10
|
+
false,
|
|
11
|
+
(process.env.COLOR_SCHEME ?? 'dark') as ColorScheme
|
|
12
|
+
);
|
|
9
13
|
|
|
10
14
|
// Re-export the Logger type for convenience
|
|
11
15
|
export type { Logger } from '@agentuity/core';
|
package/src/otel/config.ts
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { resourceFromAttributes } from '@opentelemetry/resources';
|
|
3
|
-
import { OtelLogger } from '../otel/logger';
|
|
4
|
-
import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
|
|
5
|
-
import type { LoggerProvider } from '@opentelemetry/sdk-logs';
|
|
6
|
-
import type { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
|
|
7
|
-
import type { LogRecordProcessor } from '@opentelemetry/sdk-logs';
|
|
8
|
-
import type { OtelResponse, OtelConfig } from './otel';
|
|
1
|
+
import type { LogLevel } from '@agentuity/core';
|
|
9
2
|
import type { SpanProcessor } from '@opentelemetry/sdk-trace-base';
|
|
10
3
|
import * as runtimeConfig from '../_config';
|
|
4
|
+
import type { OtelConfig, OtelResponse } from './otel';
|
|
5
|
+
import { registerOtel } from './otel';
|
|
11
6
|
|
|
12
7
|
/**
|
|
13
8
|
* Configuration for user provided OpenTelemetry
|
|
@@ -23,6 +18,7 @@ export interface CustomizedOtelConfig {
|
|
|
23
18
|
|
|
24
19
|
interface OtelRegisterConfig {
|
|
25
20
|
processors?: SpanProcessor[];
|
|
21
|
+
logLevel?: LogLevel;
|
|
26
22
|
}
|
|
27
23
|
|
|
28
24
|
export function register(registerConfig: OtelRegisterConfig): OtelResponse {
|
|
@@ -38,44 +34,11 @@ export function register(registerConfig: OtelRegisterConfig): OtelResponse {
|
|
|
38
34
|
projectId: runtimeConfig.getProjectId(),
|
|
39
35
|
deploymentId: runtimeConfig.getDeploymentId(),
|
|
40
36
|
environment: runtimeConfig.getEnvironment(),
|
|
37
|
+
logLevel: registerConfig.logLevel,
|
|
38
|
+
jsonlBasePath: process.env.AGENTUITY_CLOUD_EXPORT_DIR,
|
|
41
39
|
bearerToken,
|
|
42
40
|
url,
|
|
43
41
|
};
|
|
44
|
-
let userOtelConf: CustomizedOtelConfig | undefined;
|
|
45
|
-
if (process.env.AGENTUITY_USER_OTEL_CONF) {
|
|
46
|
-
try {
|
|
47
|
-
userOtelConf = JSON.parse(process.env.AGENTUITY_USER_OTEL_CONF);
|
|
48
|
-
} catch (error) {
|
|
49
|
-
console.warn(
|
|
50
|
-
`[WARN] Failed to parse AGENTUITY_USER_OTEL_CONF: ${error instanceof Error ? error.message : String(error)}`
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
42
|
|
|
55
|
-
|
|
56
|
-
let userLoggerProvider:
|
|
57
|
-
| {
|
|
58
|
-
provider: LoggerProvider;
|
|
59
|
-
exporter: OTLPLogExporter;
|
|
60
|
-
processor: LogRecordProcessor;
|
|
61
|
-
}
|
|
62
|
-
| undefined;
|
|
63
|
-
if (userOtelConf) {
|
|
64
|
-
const resource = resourceFromAttributes({
|
|
65
|
-
...createResource(config).attributes,
|
|
66
|
-
...userOtelConf.resourceAttributes,
|
|
67
|
-
[ATTR_SERVICE_NAME]: userOtelConf.serviceName,
|
|
68
|
-
});
|
|
69
|
-
userLoggerProvider = createUserLoggerProvider({
|
|
70
|
-
url: userOtelConf.endpoint,
|
|
71
|
-
headers: userOtelConf.headers,
|
|
72
|
-
resource,
|
|
73
|
-
});
|
|
74
|
-
if (otel.logger instanceof OtelLogger) {
|
|
75
|
-
otel.logger.addDelegate(userLoggerProvider.provider.getLogger('default'));
|
|
76
|
-
} else {
|
|
77
|
-
console.warn('[WARN] user OTEL logger not attached: logger does not support addDelegate');
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return otel;
|
|
43
|
+
return registerOtel(config);
|
|
81
44
|
}
|
package/src/otel/console.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { SeverityNumber } from '@opentelemetry/api-logs';
|
|
2
2
|
import { type ExportResult, ExportResultCode } from '@opentelemetry/core';
|
|
3
3
|
import type { LogRecordExporter, ReadableLogRecord } from '@opentelemetry/sdk-logs';
|
|
4
|
-
import { createLogger } from '@agentuity/server';
|
|
5
|
-
import type { Logger } from '@agentuity/core';
|
|
4
|
+
import { type ColorScheme, createLogger } from '@agentuity/server';
|
|
5
|
+
import type { Logger, LogLevel } from '@agentuity/core';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Console implementation of the LogRecordExporter interface
|
|
@@ -13,8 +13,12 @@ export class ConsoleLogRecordExporter implements LogRecordExporter {
|
|
|
13
13
|
/**
|
|
14
14
|
* Creates a new console log record exporter
|
|
15
15
|
*/
|
|
16
|
-
constructor() {
|
|
17
|
-
this.logger = createLogger(
|
|
16
|
+
constructor(logLevel: LogLevel) {
|
|
17
|
+
this.logger = createLogger(
|
|
18
|
+
logLevel,
|
|
19
|
+
false,
|
|
20
|
+
(process.env.COLOR_SCHEME as ColorScheme) ?? 'dark'
|
|
21
|
+
);
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
/**
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# JSONL Exporters
|
|
2
|
+
|
|
3
|
+
Custom OpenTelemetry exporters that write telemetry data (logs, traces, metrics) to JSONL (JSON Lines) files instead of sending directly to an OTLP endpoint.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
These exporters write telemetry data to local files in JSONL format. Each line in the file represents a single telemetry item in JSON format. This allows for:
|
|
8
|
+
|
|
9
|
+
1. **Decoupled Processing**: Telemetry data is buffered locally and processed separately
|
|
10
|
+
2. **Reliability**: Data persists even if the OTLP endpoint is temporarily unavailable
|
|
11
|
+
3. **Batch Processing**: A separate cron job can read and send data in batches
|
|
12
|
+
4. **Easy Debugging**: JSONL files can be inspected directly
|
|
13
|
+
|
|
14
|
+
## How It Works
|
|
15
|
+
|
|
16
|
+
### 1. Writing Telemetry Data
|
|
17
|
+
|
|
18
|
+
The exporters write telemetry data to timestamped JSONL files:
|
|
19
|
+
|
|
20
|
+
- **Logs**: `./otel-data/logs-<timestamp>.jsonl`
|
|
21
|
+
- **Traces**: `./otel-data/traces-<timestamp>.jsonl`
|
|
22
|
+
- **Metrics**: `./otel-data/metrics-<timestamp>.jsonl`
|
|
23
|
+
|
|
24
|
+
Files are named with an ISO timestamp (with colons and periods replaced by hyphens) to ensure uniqueness. The exporters will continue writing to the same file as long as it exists.
|
|
25
|
+
|
|
26
|
+
### 2. Reading and Forwarding Data (External Process)
|
|
27
|
+
|
|
28
|
+
A separate cron job (recommended: every 30 seconds) should:
|
|
29
|
+
|
|
30
|
+
1. Read the JSONL files
|
|
31
|
+
2. Parse each line as a JSON object
|
|
32
|
+
3. Send the telemetry data to your OTLP endpoint
|
|
33
|
+
4. Delete the file after successful transmission
|
|
34
|
+
|
|
35
|
+
This decouples the application from the OTLP endpoint and provides resilience.
|
|
36
|
+
|
|
37
|
+
## Configuration
|
|
38
|
+
|
|
39
|
+
### Enabling JSONL Exporters
|
|
40
|
+
|
|
41
|
+
By default, JSONL exporters are enabled. You can configure them via the `OtelConfig`:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { registerOtel } from '@agentuity/runtime/otel';
|
|
45
|
+
|
|
46
|
+
registerOtel({
|
|
47
|
+
name: 'my-app',
|
|
48
|
+
version: '1.0.0',
|
|
49
|
+
url: 'https://otel.example.com',
|
|
50
|
+
useJsonlExporter: true, // Enable JSONL exporters (default: true)
|
|
51
|
+
jsonlBasePath: './.agentuity/otel-data', // Directory for JSONL files
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Disabling JSONL Exporters
|
|
56
|
+
|
|
57
|
+
To use the original OTLP exporters (direct network calls):
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
registerOtel({
|
|
61
|
+
name: 'my-app',
|
|
62
|
+
version: '1.0.0',
|
|
63
|
+
url: 'https://otel.example.com',
|
|
64
|
+
useJsonlExporter: false, // Disable JSONL, use OTLP directly
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## File Format
|
|
69
|
+
|
|
70
|
+
### Logs
|
|
71
|
+
|
|
72
|
+
Each log entry contains:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"timestamp": [seconds, nanoseconds],
|
|
77
|
+
"observedTimestamp": [seconds, nanoseconds],
|
|
78
|
+
"severityNumber": 9,
|
|
79
|
+
"severityText": "INFO",
|
|
80
|
+
"body": "Log message",
|
|
81
|
+
"attributes": { "key": "value" },
|
|
82
|
+
"resource": { "@agentuity/orgId": "...", ... },
|
|
83
|
+
"instrumentationScope": { "name": "...", "version": "..." },
|
|
84
|
+
"spanContext": { "traceId": "...", "spanId": "...", ... }
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Traces
|
|
89
|
+
|
|
90
|
+
Each span contains:
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"traceId": "...",
|
|
95
|
+
"spanId": "...",
|
|
96
|
+
"traceState": "...",
|
|
97
|
+
"name": "operation-name",
|
|
98
|
+
"kind": 1,
|
|
99
|
+
"startTime": [seconds, nanoseconds],
|
|
100
|
+
"endTime": [seconds, nanoseconds],
|
|
101
|
+
"attributes": { "key": "value" },
|
|
102
|
+
"status": { "code": 0 },
|
|
103
|
+
"events": [],
|
|
104
|
+
"links": [],
|
|
105
|
+
"resource": { "@agentuity/orgId": "...", ... },
|
|
106
|
+
"droppedAttributesCount": 0,
|
|
107
|
+
"droppedEventsCount": 0,
|
|
108
|
+
"droppedLinksCount": 0,
|
|
109
|
+
"duration": [seconds, nanoseconds],
|
|
110
|
+
"ended": true
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Metrics
|
|
115
|
+
|
|
116
|
+
Each metric batch contains:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"resource": { "@agentuity/orgId": "...", ... },
|
|
121
|
+
"scopeMetrics": [
|
|
122
|
+
{
|
|
123
|
+
"scope": { "name": "...", "version": "..." },
|
|
124
|
+
"metrics": [
|
|
125
|
+
{
|
|
126
|
+
"descriptor": { "name": "...", "description": "...", ... },
|
|
127
|
+
"dataPointType": 0,
|
|
128
|
+
"dataPoints": [...],
|
|
129
|
+
"aggregationTemporality": 1
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Example Cron Job
|
|
138
|
+
|
|
139
|
+
Here's an example script that reads JSONL files and forwards them to OTLP:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
#!/usr/bin/env bun
|
|
143
|
+
|
|
144
|
+
import { readdir, readFile, unlink } from 'node:fs/promises';
|
|
145
|
+
import { join } from 'node:path';
|
|
146
|
+
|
|
147
|
+
const OTEL_ENDPOINT = process.env.OTEL_ENDPOINT || 'https://otel.agentuity.cloud';
|
|
148
|
+
const OTEL_TOKEN = process.env.OTEL_TOKEN;
|
|
149
|
+
const DATA_DIR = process.env.DATA_DIR || './.agentuity/otel-data';
|
|
150
|
+
|
|
151
|
+
async function processFiles() {
|
|
152
|
+
const files = await readdir(DATA_DIR);
|
|
153
|
+
|
|
154
|
+
for (const file of files) {
|
|
155
|
+
if (file.endsWith('.jsonl')) {
|
|
156
|
+
const filePath = join(DATA_DIR, file);
|
|
157
|
+
const content = await readFile(filePath, 'utf-8');
|
|
158
|
+
const lines = content.trim().split('\n');
|
|
159
|
+
|
|
160
|
+
const type = file.startsWith('logs-')
|
|
161
|
+
? 'logs'
|
|
162
|
+
: file.startsWith('traces-')
|
|
163
|
+
? 'traces'
|
|
164
|
+
: 'metrics';
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
// Send to OTLP endpoint
|
|
168
|
+
await fetch(`${OTEL_ENDPOINT}/v1/${type}`, {
|
|
169
|
+
method: 'POST',
|
|
170
|
+
headers: {
|
|
171
|
+
'Content-Type': 'application/json',
|
|
172
|
+
Authorization: `Bearer ${OTEL_TOKEN}`,
|
|
173
|
+
},
|
|
174
|
+
body: JSON.stringify({ [type]: lines.map((line) => JSON.parse(line)) }),
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Delete file after successful transmission
|
|
178
|
+
await unlink(filePath);
|
|
179
|
+
console.log(`Processed and deleted ${file}`);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.error(`Failed to process ${file}:`, error);
|
|
182
|
+
// Don't delete the file on error, will retry next time
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
processFiles().catch(console.error);
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Add this to your crontab to run every 30 seconds:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
* * * * * /path/to/process-otel-data.ts
|
|
195
|
+
* * * * * sleep 30 && /path/to/process-otel-data.ts
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Exporters
|
|
199
|
+
|
|
200
|
+
### JSONLLogExporter
|
|
201
|
+
|
|
202
|
+
Implements `LogRecordExporter` interface.
|
|
203
|
+
|
|
204
|
+
### JSONLTraceExporter
|
|
205
|
+
|
|
206
|
+
Implements `SpanExporter` interface.
|
|
207
|
+
|
|
208
|
+
### JSONLMetricExporter
|
|
209
|
+
|
|
210
|
+
Implements `PushMetricExporter` interface.
|
|
211
|
+
|
|
212
|
+
## Notes
|
|
213
|
+
|
|
214
|
+
- Files are written synchronously using `appendFileSync` for simplicity and reliability
|
|
215
|
+
- File existence is checked before each write, creating a new file if necessary
|
|
216
|
+
- Timestamps in filenames use ISO format with special characters replaced by hyphens
|
|
217
|
+
- The exporters handle errors gracefully and report them via the callback mechanism
|