drtrace 0.3.0 → 0.5.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/README.md +45 -4
- package/agents/CONTRIBUTING.md +0 -0
- package/agents/README.md +0 -0
- package/agents/daemon-method-selection.md +126 -227
- package/agents/integration-guides/cpp-best-practices.md +0 -0
- package/agents/integration-guides/cpp-ros-integration.md +0 -0
- package/agents/log-analysis.md +3 -89
- package/agents/log-help.md +6 -79
- package/agents/log-init.md +4 -0
- package/agents/log-it.md +4 -0
- package/bin/cli.js +98 -0
- package/bin/init.js +0 -0
- package/dist/bin/cli.js +98 -0
- package/dist/browser.d.ts +28 -0
- package/dist/browser.js +91 -0
- package/dist/cli/grep.d.ts +27 -0
- package/dist/cli/grep.js +251 -0
- package/dist/cli/status.d.ts +11 -0
- package/dist/cli/status.js +78 -0
- package/dist/client.d.ts +0 -0
- package/dist/client.js +0 -0
- package/dist/config-schema.d.ts +0 -0
- package/dist/config-schema.js +0 -0
- package/dist/config.d.ts +0 -0
- package/dist/config.js +0 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/init.d.ts +2 -0
- package/dist/init.js +23 -18
- package/dist/logger.d.ts +7 -0
- package/dist/logger.js +30 -4
- package/dist/node.d.ts +13 -0
- package/dist/node.js +67 -0
- package/dist/queue.d.ts +0 -0
- package/dist/queue.js +0 -0
- package/dist/resources/agents/CONTRIBUTING.md +0 -0
- package/dist/resources/agents/README.md +0 -0
- package/dist/resources/agents/daemon-method-selection.md +126 -227
- package/dist/resources/agents/integration-guides/cpp-best-practices.md +0 -0
- package/dist/resources/agents/integration-guides/cpp-ros-integration.md +0 -0
- package/dist/resources/agents/log-analysis.md +3 -89
- package/dist/resources/agents/log-help.md +6 -79
- package/dist/resources/agents/log-init.md +4 -0
- package/dist/resources/agents/log-it.md +4 -0
- package/dist/resources/cpp/drtrace_sink.hpp +1 -0
- package/dist/transport.d.ts +0 -0
- package/dist/transport.js +5 -1
- package/dist/types.d.ts +8 -2
- package/dist/types.js +0 -0
- package/package.json +22 -5
- package/dist/bin/init.js +0 -31
package/dist/cli/grep.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Grep command implementation for searching logs with POSIX regex.
|
|
4
|
+
* Matches Python implementation in packages/python/src/drtrace_service/cli/grep.py
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.grep = grep;
|
|
8
|
+
exports.runGrep = runGrep;
|
|
9
|
+
/**
|
|
10
|
+
* Check if daemon is available with timeout
|
|
11
|
+
*/
|
|
12
|
+
async function checkDaemonAlive(host, port, timeoutMs = 500) {
|
|
13
|
+
const controller = new AbortController();
|
|
14
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
15
|
+
try {
|
|
16
|
+
const response = await fetch(`http://${host}:${port}/status`, {
|
|
17
|
+
signal: controller.signal,
|
|
18
|
+
});
|
|
19
|
+
clearTimeout(timeout);
|
|
20
|
+
return response.ok;
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
clearTimeout(timeout);
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Format timestamp for display
|
|
29
|
+
*/
|
|
30
|
+
function formatTimestamp(ts) {
|
|
31
|
+
const date = new Date(ts * 1000);
|
|
32
|
+
return date.toISOString().replace('T', ' ').substring(0, 19);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Apply color to text based on log level
|
|
36
|
+
*/
|
|
37
|
+
function colorize(text, level, colorMode) {
|
|
38
|
+
const shouldColor = colorMode === 'always' || (colorMode === 'auto' && process.stdout.isTTY);
|
|
39
|
+
if (!shouldColor) {
|
|
40
|
+
return text;
|
|
41
|
+
}
|
|
42
|
+
const colors = {
|
|
43
|
+
ERROR: '\x1b[31m', // Red
|
|
44
|
+
CRITICAL: '\x1b[31m', // Red
|
|
45
|
+
WARNING: '\x1b[33m', // Yellow
|
|
46
|
+
WARN: '\x1b[33m', // Yellow
|
|
47
|
+
INFO: '\x1b[0m', // Default
|
|
48
|
+
DEBUG: '\x1b[90m', // Gray
|
|
49
|
+
};
|
|
50
|
+
const reset = '\x1b[0m';
|
|
51
|
+
const color = colors[level] || colors.INFO;
|
|
52
|
+
return `${color}${text}${reset}`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Main grep implementation
|
|
56
|
+
*/
|
|
57
|
+
async function grep(options) {
|
|
58
|
+
const { pattern, ignoreCase = false, count = false, invertMatch = false, lineNumber = false, extendedRegex = false, since = '5m', applicationId, daemonHost = process.env.DRTRACE_DAEMON_HOST || 'localhost', daemonPort = parseInt(process.env.DRTRACE_DAEMON_PORT || '8001'), json = false, color = 'auto', } = options;
|
|
59
|
+
// Check if daemon is available
|
|
60
|
+
const daemonAvailable = await checkDaemonAlive(daemonHost, daemonPort, 500);
|
|
61
|
+
if (!daemonAvailable) {
|
|
62
|
+
console.error('Error: DrTrace daemon is not running.');
|
|
63
|
+
console.error('');
|
|
64
|
+
console.error('Start the daemon with:');
|
|
65
|
+
console.error(' drtrace daemon start');
|
|
66
|
+
return 2;
|
|
67
|
+
}
|
|
68
|
+
// Build query parameters (Story 11-2: wire -E flag to message_regex)
|
|
69
|
+
const params = new URLSearchParams({
|
|
70
|
+
since: since,
|
|
71
|
+
});
|
|
72
|
+
// Use message_regex if -E flag provided, else message_contains (Epic 11.1, 11.2)
|
|
73
|
+
if (extendedRegex) {
|
|
74
|
+
params.append('message_regex', pattern);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
params.append('message_contains', pattern);
|
|
78
|
+
}
|
|
79
|
+
if (applicationId) {
|
|
80
|
+
params.append('application_id', applicationId);
|
|
81
|
+
}
|
|
82
|
+
// Query daemon using fetch
|
|
83
|
+
try {
|
|
84
|
+
const daemonUrl = `http://${daemonHost}:${daemonPort}/logs/query`;
|
|
85
|
+
const response = await fetch(`${daemonUrl}?${params.toString()}`);
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
const error = await response.json();
|
|
88
|
+
const errorMsg = typeof error.detail === 'object' ? error.detail?.message : error.detail;
|
|
89
|
+
console.error(`Error: ${errorMsg || 'Query failed'}`);
|
|
90
|
+
return 2;
|
|
91
|
+
}
|
|
92
|
+
const data = await response.json();
|
|
93
|
+
const results = data.results || [];
|
|
94
|
+
// Apply additional filters that weren't sent to API (invert_match)
|
|
95
|
+
let filteredResults = results;
|
|
96
|
+
if (invertMatch) {
|
|
97
|
+
if (extendedRegex) {
|
|
98
|
+
const flags = ignoreCase ? 'i' : '';
|
|
99
|
+
const regex = new RegExp(pattern, flags);
|
|
100
|
+
filteredResults = results.filter(record => !regex.test(record.message));
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
const searchPattern = ignoreCase ? pattern.toLowerCase() : pattern;
|
|
104
|
+
filteredResults = results.filter(record => {
|
|
105
|
+
const message = ignoreCase ? record.message.toLowerCase() : record.message;
|
|
106
|
+
return !message.includes(searchPattern);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Output results
|
|
111
|
+
if (count) {
|
|
112
|
+
console.log(filteredResults.length);
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
115
|
+
if (json) {
|
|
116
|
+
console.log(JSON.stringify(filteredResults, null, 2));
|
|
117
|
+
return 0;
|
|
118
|
+
}
|
|
119
|
+
if (filteredResults.length === 0) {
|
|
120
|
+
return 1;
|
|
121
|
+
}
|
|
122
|
+
// Format and print results
|
|
123
|
+
for (const record of filteredResults) {
|
|
124
|
+
const tsStr = formatTimestamp(record.ts);
|
|
125
|
+
const serviceStr = record.service_name ? `[${record.service_name}]` : '';
|
|
126
|
+
let msg = `[${tsStr}] ${serviceStr} [${record.level}] ${record.message}`;
|
|
127
|
+
msg = colorize(msg, record.level, color);
|
|
128
|
+
if (lineNumber) {
|
|
129
|
+
// Use timestamp as pseudo line number for daemon results
|
|
130
|
+
msg = `${Math.floor(record.ts)}:${msg}`;
|
|
131
|
+
}
|
|
132
|
+
console.log(msg);
|
|
133
|
+
}
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
138
|
+
console.error(`Error: Failed to query daemon: ${errorMessage}`);
|
|
139
|
+
return 2;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Parse CLI arguments and run grep command
|
|
144
|
+
*/
|
|
145
|
+
async function runGrep(args) {
|
|
146
|
+
// Simple argument parser matching Python argparse behavior
|
|
147
|
+
const options = {
|
|
148
|
+
pattern: '',
|
|
149
|
+
ignoreCase: false,
|
|
150
|
+
count: false,
|
|
151
|
+
invertMatch: false,
|
|
152
|
+
lineNumber: false,
|
|
153
|
+
extendedRegex: false,
|
|
154
|
+
since: '5m',
|
|
155
|
+
color: 'auto',
|
|
156
|
+
};
|
|
157
|
+
let i = 0;
|
|
158
|
+
while (i < args.length) {
|
|
159
|
+
const arg = args[i];
|
|
160
|
+
if (arg === '-i' || arg === '--ignore-case') {
|
|
161
|
+
options.ignoreCase = true;
|
|
162
|
+
}
|
|
163
|
+
else if (arg === '-c' || arg === '--count') {
|
|
164
|
+
options.count = true;
|
|
165
|
+
}
|
|
166
|
+
else if (arg === '-v' || arg === '--invert-match') {
|
|
167
|
+
options.invertMatch = true;
|
|
168
|
+
}
|
|
169
|
+
else if (arg === '-n' || arg === '--line-number') {
|
|
170
|
+
options.lineNumber = true;
|
|
171
|
+
}
|
|
172
|
+
else if (arg === '-E' || arg === '--extended-regex') {
|
|
173
|
+
options.extendedRegex = true;
|
|
174
|
+
}
|
|
175
|
+
else if (arg === '--since') {
|
|
176
|
+
options.since = args[++i];
|
|
177
|
+
}
|
|
178
|
+
else if (arg === '--application-id') {
|
|
179
|
+
options.applicationId = args[++i];
|
|
180
|
+
}
|
|
181
|
+
else if (arg === '--daemon-host') {
|
|
182
|
+
options.daemonHost = args[++i];
|
|
183
|
+
}
|
|
184
|
+
else if (arg === '--daemon-port') {
|
|
185
|
+
options.daemonPort = parseInt(args[++i]);
|
|
186
|
+
}
|
|
187
|
+
else if (arg === '--json') {
|
|
188
|
+
options.json = true;
|
|
189
|
+
}
|
|
190
|
+
else if (arg === '--color') {
|
|
191
|
+
const colorValue = args[++i];
|
|
192
|
+
if (colorValue !== 'auto' && colorValue !== 'always' && colorValue !== 'never') {
|
|
193
|
+
console.error(`Error: Invalid color value '${colorValue}'. Use: auto, always, never`);
|
|
194
|
+
return 2;
|
|
195
|
+
}
|
|
196
|
+
options.color = colorValue;
|
|
197
|
+
}
|
|
198
|
+
else if (arg === '-h' || arg === '--help') {
|
|
199
|
+
printHelp();
|
|
200
|
+
return 0;
|
|
201
|
+
}
|
|
202
|
+
else if (!arg.startsWith('-')) {
|
|
203
|
+
// Positional argument - pattern
|
|
204
|
+
options.pattern = arg;
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
console.error(`Error: Unknown option '${arg}'`);
|
|
208
|
+
return 2;
|
|
209
|
+
}
|
|
210
|
+
i++;
|
|
211
|
+
}
|
|
212
|
+
if (!options.pattern) {
|
|
213
|
+
console.error('Error: Pattern is required');
|
|
214
|
+
printHelp();
|
|
215
|
+
return 2;
|
|
216
|
+
}
|
|
217
|
+
return grep(options);
|
|
218
|
+
}
|
|
219
|
+
function printHelp() {
|
|
220
|
+
console.log(`Usage: drtrace grep [OPTIONS] PATTERN
|
|
221
|
+
|
|
222
|
+
Search log messages with pattern matching.
|
|
223
|
+
|
|
224
|
+
Options:
|
|
225
|
+
-i, --ignore-case Ignore case in pattern matching
|
|
226
|
+
-c, --count Output count of matches instead of matches
|
|
227
|
+
-v, --invert-match Invert match (show non-matching lines)
|
|
228
|
+
-n, --line-number Output line numbers with matches
|
|
229
|
+
-E, --extended-regex Use POSIX extended regex (sends message_regex to API)
|
|
230
|
+
--since <time> Time range: 30m/1h/2d/7d (default: 5m)
|
|
231
|
+
--application-id <id> Filter by application ID
|
|
232
|
+
--daemon-host <host> Daemon host (default: localhost)
|
|
233
|
+
--daemon-port <port> Daemon port (default: 8001)
|
|
234
|
+
--json Output in JSON format
|
|
235
|
+
--color <mode> Color control: auto, always, never (default: auto)
|
|
236
|
+
-h, --help Show this help message
|
|
237
|
+
|
|
238
|
+
Examples:
|
|
239
|
+
# Simple substring search
|
|
240
|
+
drtrace grep "timeout"
|
|
241
|
+
|
|
242
|
+
# Regex search with -E flag
|
|
243
|
+
drtrace grep -E "error|warning"
|
|
244
|
+
|
|
245
|
+
# Search with application filter
|
|
246
|
+
drtrace grep --application-id myapp "error"
|
|
247
|
+
|
|
248
|
+
# Count matches
|
|
249
|
+
drtrace grep -c "error"
|
|
250
|
+
`);
|
|
251
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Status command implementation - check daemon health
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Check daemon status and display information
|
|
6
|
+
*/
|
|
7
|
+
export declare function status(daemonHost?: string, daemonPort?: number): Promise<number>;
|
|
8
|
+
/**
|
|
9
|
+
* Parse CLI arguments and run status command
|
|
10
|
+
*/
|
|
11
|
+
export declare function runStatus(args: string[]): Promise<number>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Status command implementation - check daemon health
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.status = status;
|
|
7
|
+
exports.runStatus = runStatus;
|
|
8
|
+
/**
|
|
9
|
+
* Check daemon status and display information
|
|
10
|
+
*/
|
|
11
|
+
async function status(daemonHost = process.env.DRTRACE_DAEMON_HOST || 'localhost', daemonPort = parseInt(process.env.DRTRACE_DAEMON_PORT || '8001')) {
|
|
12
|
+
try {
|
|
13
|
+
const response = await fetch(`http://${daemonHost}:${daemonPort}/status`, {
|
|
14
|
+
signal: AbortSignal.timeout(5000),
|
|
15
|
+
});
|
|
16
|
+
if (!response.ok) {
|
|
17
|
+
console.error(`DrTrace daemon status: UNHEALTHY (HTTP ${response.status})`);
|
|
18
|
+
console.error(`URL: http://${daemonHost}:${daemonPort}/status`);
|
|
19
|
+
return 2;
|
|
20
|
+
}
|
|
21
|
+
const data = await response.json();
|
|
22
|
+
console.log('DrTrace daemon status: HEALTHY');
|
|
23
|
+
console.log(`Service: ${data.service_name} v${data.version}`);
|
|
24
|
+
console.log(`Listening on: ${data.host}:${data.port}`);
|
|
25
|
+
if (data.retention_days) {
|
|
26
|
+
console.log(`Retention: ${data.retention_days} days`);
|
|
27
|
+
}
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error(`DrTrace daemon status: UNREACHABLE`);
|
|
32
|
+
console.error(`URL: http://${daemonHost}:${daemonPort}/status`);
|
|
33
|
+
console.error('');
|
|
34
|
+
console.error('Make sure the daemon is running:');
|
|
35
|
+
console.error(' python -m drtrace_service daemon start');
|
|
36
|
+
return 2;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Parse CLI arguments and run status command
|
|
41
|
+
*/
|
|
42
|
+
async function runStatus(args) {
|
|
43
|
+
let daemonHost = process.env.DRTRACE_DAEMON_HOST || 'localhost';
|
|
44
|
+
let daemonPort = parseInt(process.env.DRTRACE_DAEMON_PORT || '8001');
|
|
45
|
+
for (let i = 0; i < args.length; i++) {
|
|
46
|
+
const arg = args[i];
|
|
47
|
+
if (arg === '--daemon-host') {
|
|
48
|
+
daemonHost = args[++i];
|
|
49
|
+
}
|
|
50
|
+
else if (arg === '--daemon-port') {
|
|
51
|
+
daemonPort = parseInt(args[++i]);
|
|
52
|
+
}
|
|
53
|
+
else if (arg === '-h' || arg === '--help') {
|
|
54
|
+
printHelp();
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
console.error(`Error: Unknown option '${arg}'`);
|
|
59
|
+
return 2;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return status(daemonHost, daemonPort);
|
|
63
|
+
}
|
|
64
|
+
function printHelp() {
|
|
65
|
+
console.log(`Usage: drtrace status [OPTIONS]
|
|
66
|
+
|
|
67
|
+
Check daemon health and configuration.
|
|
68
|
+
|
|
69
|
+
Options:
|
|
70
|
+
--daemon-host <host> Daemon host (default: localhost)
|
|
71
|
+
--daemon-port <port> Daemon port (default: 8001)
|
|
72
|
+
-h, --help Show this help message
|
|
73
|
+
|
|
74
|
+
Environment Variables:
|
|
75
|
+
DRTRACE_DAEMON_HOST Daemon host (default: localhost)
|
|
76
|
+
DRTRACE_DAEMON_PORT Daemon port (default: 8001)
|
|
77
|
+
`);
|
|
78
|
+
}
|
package/dist/client.d.ts
CHANGED
|
File without changes
|
package/dist/client.js
CHANGED
|
File without changes
|
package/dist/config-schema.d.ts
CHANGED
|
File without changes
|
package/dist/config-schema.js
CHANGED
|
File without changes
|
package/dist/config.d.ts
CHANGED
|
File without changes
|
package/dist/config.js
CHANGED
|
File without changes
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -15,8 +15,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.loadConfig = exports.DrTrace = void 0;
|
|
18
|
-
var
|
|
19
|
-
Object.defineProperty(exports, "DrTrace", { enumerable: true, get: function () { return
|
|
18
|
+
var node_1 = require("./node");
|
|
19
|
+
Object.defineProperty(exports, "DrTrace", { enumerable: true, get: function () { return node_1.DrTrace; } });
|
|
20
20
|
var config_1 = require("./config");
|
|
21
21
|
Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return config_1.loadConfig; } });
|
|
22
22
|
__exportStar(require("./types"), exports);
|
package/dist/init.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare class ProjectInitializer {
|
|
|
12
12
|
private projectRoot;
|
|
13
13
|
private drtraceDir;
|
|
14
14
|
private configPath;
|
|
15
|
+
private copiedAgentFiles;
|
|
15
16
|
constructor(projectRoot?: string);
|
|
16
17
|
/**
|
|
17
18
|
* Run the interactive initialization workflow
|
|
@@ -57,6 +58,7 @@ export declare class ProjectInitializer {
|
|
|
57
58
|
private copyAgentSpec;
|
|
58
59
|
/**
|
|
59
60
|
* Recursively copy all files from sourceDir to targetDir
|
|
61
|
+
* Returns list of relative file paths copied (for summary display)
|
|
60
62
|
*/
|
|
61
63
|
private copyAgentsRecursive;
|
|
62
64
|
/**
|
package/dist/init.js
CHANGED
|
@@ -49,6 +49,8 @@ const prompts_1 = __importDefault(require("prompts"));
|
|
|
49
49
|
const config_schema_1 = require("./config-schema");
|
|
50
50
|
class ProjectInitializer {
|
|
51
51
|
constructor(projectRoot = process.cwd()) {
|
|
52
|
+
// Track copied agent files for summary display
|
|
53
|
+
this.copiedAgentFiles = [];
|
|
52
54
|
this.projectRoot = projectRoot;
|
|
53
55
|
this.drtraceDir = path.join(projectRoot, "_drtrace");
|
|
54
56
|
this.configPath = path.join(this.drtraceDir, "config.json");
|
|
@@ -260,8 +262,9 @@ class ProjectInitializer {
|
|
|
260
262
|
console.warn("⚠️ Could not find agents directory");
|
|
261
263
|
return;
|
|
262
264
|
}
|
|
263
|
-
// Copy all files recursively
|
|
264
|
-
this.copyAgentsRecursive(agentsDir, path.join(this.drtraceDir, "agents"));
|
|
265
|
+
// Copy all files recursively and track copied files
|
|
266
|
+
const copiedFiles = this.copyAgentsRecursive(agentsDir, path.join(this.drtraceDir, "agents"));
|
|
267
|
+
this.copiedAgentFiles.push(...copiedFiles);
|
|
265
268
|
}
|
|
266
269
|
catch (error) {
|
|
267
270
|
console.warn(`⚠️ Could not copy agent files: ${error}`);
|
|
@@ -269,34 +272,35 @@ class ProjectInitializer {
|
|
|
269
272
|
}
|
|
270
273
|
/**
|
|
271
274
|
* Recursively copy all files from sourceDir to targetDir
|
|
275
|
+
* Returns list of relative file paths copied (for summary display)
|
|
272
276
|
*/
|
|
273
|
-
copyAgentsRecursive(sourceDir, targetDir) {
|
|
277
|
+
copyAgentsRecursive(sourceDir, targetDir, basePath = "") {
|
|
274
278
|
if (!fs.existsSync(targetDir)) {
|
|
275
279
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
276
280
|
}
|
|
277
281
|
const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
|
|
278
|
-
|
|
282
|
+
const copiedFiles = [];
|
|
279
283
|
for (const entry of entries) {
|
|
280
284
|
const srcPath = path.join(sourceDir, entry.name);
|
|
281
285
|
const destPath = path.join(targetDir, entry.name);
|
|
286
|
+
const relativePath = basePath ? path.join(basePath, entry.name) : entry.name;
|
|
282
287
|
if (entry.isDirectory()) {
|
|
283
288
|
// Recursively copy directories
|
|
284
|
-
this.copyAgentsRecursive(srcPath, destPath);
|
|
289
|
+
const subCopied = this.copyAgentsRecursive(srcPath, destPath, relativePath);
|
|
290
|
+
copiedFiles.push(...subCopied);
|
|
285
291
|
}
|
|
286
292
|
else {
|
|
287
293
|
// Copy file (no renaming needed - files are already named correctly)
|
|
288
294
|
fs.copyFileSync(srcPath, destPath);
|
|
289
|
-
|
|
290
|
-
console.log(`✓ Copied ${
|
|
295
|
+
copiedFiles.push(relativePath);
|
|
296
|
+
console.log(`✓ Copied ${relativePath}`);
|
|
291
297
|
}
|
|
292
298
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
if (!relativeSource.includes("..")) {
|
|
297
|
-
console.log(`✓ Successfully copied ${copiedCount} file(s) from agents/`);
|
|
298
|
-
}
|
|
299
|
+
// Print summary only at top level
|
|
300
|
+
if (!basePath && copiedFiles.length > 0) {
|
|
301
|
+
console.log(`✓ Successfully copied ${copiedFiles.length} file(s) from agents/`);
|
|
299
302
|
}
|
|
303
|
+
return copiedFiles;
|
|
300
304
|
}
|
|
301
305
|
/**
|
|
302
306
|
* Copy framework-specific integration guides to _drtrace/agents/integration-guides/
|
|
@@ -781,11 +785,12 @@ python -m drtrace_service status
|
|
|
781
785
|
}
|
|
782
786
|
console.log(` • ${path.join(this.drtraceDir, ".env.example")}`);
|
|
783
787
|
console.log(` • ${path.join(this.drtraceDir, "README.md")}`);
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
console.log(
|
|
787
|
-
|
|
788
|
-
|
|
788
|
+
// List all copied agent files (includes integration-guides/)
|
|
789
|
+
if (this.copiedAgentFiles.length > 0) {
|
|
790
|
+
console.log("\n📋 Agent Files:");
|
|
791
|
+
for (const agentFile of this.copiedAgentFiles.sort()) {
|
|
792
|
+
console.log(` • ${path.join(this.drtraceDir, "agents", agentFile)}`);
|
|
793
|
+
}
|
|
789
794
|
}
|
|
790
795
|
console.log("\n📖 Next Steps:");
|
|
791
796
|
console.log(` 1. Review ${this.configPath}`);
|
package/dist/logger.d.ts
CHANGED
|
@@ -3,13 +3,20 @@ import { LogQueue } from './queue';
|
|
|
3
3
|
export declare class DrTraceLogger {
|
|
4
4
|
private queue;
|
|
5
5
|
private applicationId;
|
|
6
|
+
private moduleName;
|
|
6
7
|
private logLevel;
|
|
7
8
|
private originalConsole?;
|
|
8
9
|
constructor(opts: {
|
|
9
10
|
queue: LogQueue;
|
|
10
11
|
applicationId: string;
|
|
12
|
+
moduleName?: string;
|
|
11
13
|
logLevel: LogLevel;
|
|
12
14
|
});
|
|
15
|
+
/**
|
|
16
|
+
* Serialize a value to a string suitable for logging.
|
|
17
|
+
* Handles objects, arrays, errors, and primitives correctly.
|
|
18
|
+
*/
|
|
19
|
+
private serialize;
|
|
13
20
|
attachToConsole(): void;
|
|
14
21
|
detachFromConsole(): void;
|
|
15
22
|
log(level: LogLevel, message: string, context?: Record<string, unknown>): void;
|
package/dist/logger.js
CHANGED
|
@@ -5,15 +5,40 @@ class DrTraceLogger {
|
|
|
5
5
|
constructor(opts) {
|
|
6
6
|
this.queue = opts.queue;
|
|
7
7
|
this.applicationId = opts.applicationId;
|
|
8
|
+
this.moduleName = opts.moduleName || 'default';
|
|
8
9
|
this.logLevel = opts.logLevel || 'info';
|
|
9
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Serialize a value to a string suitable for logging.
|
|
13
|
+
* Handles objects, arrays, errors, and primitives correctly.
|
|
14
|
+
*/
|
|
15
|
+
serialize(value) {
|
|
16
|
+
if (value === null)
|
|
17
|
+
return 'null';
|
|
18
|
+
if (value === undefined)
|
|
19
|
+
return 'undefined';
|
|
20
|
+
if (typeof value === 'string')
|
|
21
|
+
return value;
|
|
22
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
23
|
+
return String(value);
|
|
24
|
+
if (value instanceof Error) {
|
|
25
|
+
return value.stack || `${value.name}: ${value.message}`;
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
return JSON.stringify(value);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Handle circular references gracefully
|
|
32
|
+
return String(value);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
10
35
|
attachToConsole() {
|
|
11
36
|
if (this.originalConsole)
|
|
12
37
|
return;
|
|
13
38
|
this.originalConsole = { log: console.log, error: console.error };
|
|
14
39
|
console.log = (...args) => {
|
|
15
40
|
try {
|
|
16
|
-
this.log('info', args.map(
|
|
41
|
+
this.log('info', args.map((arg) => this.serialize(arg)).join(' '));
|
|
17
42
|
}
|
|
18
43
|
catch {
|
|
19
44
|
// Silently ignore logging errors
|
|
@@ -22,7 +47,7 @@ class DrTraceLogger {
|
|
|
22
47
|
};
|
|
23
48
|
console.error = (...args) => {
|
|
24
49
|
try {
|
|
25
|
-
this.log('error', args.map(
|
|
50
|
+
this.log('error', args.map((arg) => this.serialize(arg)).join(' '));
|
|
26
51
|
}
|
|
27
52
|
catch {
|
|
28
53
|
// Silently ignore logging errors
|
|
@@ -39,8 +64,9 @@ class DrTraceLogger {
|
|
|
39
64
|
}
|
|
40
65
|
log(level, message, context) {
|
|
41
66
|
const event = {
|
|
42
|
-
|
|
43
|
-
|
|
67
|
+
ts: Date.now() / 1000, // Unix timestamp as float (seconds.milliseconds)
|
|
68
|
+
application_id: this.applicationId,
|
|
69
|
+
module_name: this.moduleName,
|
|
44
70
|
level,
|
|
45
71
|
message,
|
|
46
72
|
context,
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ClientOptions, LogLevel } from './types';
|
|
2
|
+
export declare class DrTrace {
|
|
3
|
+
private queue;
|
|
4
|
+
private logger;
|
|
5
|
+
private enabled;
|
|
6
|
+
private constructor();
|
|
7
|
+
static init(opts?: Partial<ClientOptions>): DrTrace;
|
|
8
|
+
attachToConsole(): void;
|
|
9
|
+
detachFromConsole(): void;
|
|
10
|
+
log(message: string, level?: LogLevel, context?: Record<string, unknown>): void;
|
|
11
|
+
error(message: string, context?: Record<string, unknown>): void;
|
|
12
|
+
shutdown(): Promise<void>;
|
|
13
|
+
}
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DrTrace = void 0;
|
|
4
|
+
const config_1 = require("./config");
|
|
5
|
+
const transport_1 = require("./transport");
|
|
6
|
+
const queue_1 = require("./queue");
|
|
7
|
+
const logger_1 = require("./logger");
|
|
8
|
+
class DrTrace {
|
|
9
|
+
constructor(opts) {
|
|
10
|
+
const transport = new transport_1.Transport({
|
|
11
|
+
daemonUrl: opts.daemonUrl,
|
|
12
|
+
maxRetries: opts.maxRetries ?? 3,
|
|
13
|
+
timeoutMs: 5000,
|
|
14
|
+
});
|
|
15
|
+
this.queue = new queue_1.LogQueue({
|
|
16
|
+
transport,
|
|
17
|
+
batchSize: opts.batchSize ?? 50,
|
|
18
|
+
flushIntervalMs: opts.flushIntervalMs ?? 1000,
|
|
19
|
+
maxQueueSize: opts.maxQueueSize ?? 10000,
|
|
20
|
+
});
|
|
21
|
+
this.queue.start();
|
|
22
|
+
this.enabled = opts.enabled ?? true;
|
|
23
|
+
this.logger = new logger_1.DrTraceLogger({
|
|
24
|
+
queue: this.queue,
|
|
25
|
+
applicationId: opts.applicationId,
|
|
26
|
+
moduleName: opts.moduleName,
|
|
27
|
+
logLevel: (opts.logLevel ?? 'info'),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
static init(opts) {
|
|
31
|
+
const cfg = (0, config_1.loadConfig)({ projectRoot: '.', environment: process.env.NODE_ENV });
|
|
32
|
+
const merged = {
|
|
33
|
+
applicationId: cfg.drtrace.applicationId,
|
|
34
|
+
daemonUrl: cfg.drtrace.daemonUrl || 'http://localhost:8001',
|
|
35
|
+
enabled: cfg.drtrace.enabled ?? true,
|
|
36
|
+
logLevel: (cfg.drtrace.logLevel ?? 'info'),
|
|
37
|
+
batchSize: cfg.drtrace.batchSize ?? 50,
|
|
38
|
+
flushIntervalMs: cfg.drtrace.flushIntervalMs ?? 1000,
|
|
39
|
+
...opts,
|
|
40
|
+
};
|
|
41
|
+
return new DrTrace(merged);
|
|
42
|
+
}
|
|
43
|
+
attachToConsole() {
|
|
44
|
+
if (!this.enabled)
|
|
45
|
+
return;
|
|
46
|
+
this.logger.attachToConsole();
|
|
47
|
+
}
|
|
48
|
+
detachFromConsole() {
|
|
49
|
+
this.logger.detachFromConsole();
|
|
50
|
+
}
|
|
51
|
+
log(message, level = 'info', context) {
|
|
52
|
+
if (!this.enabled)
|
|
53
|
+
return;
|
|
54
|
+
this.logger.log(level, message, context);
|
|
55
|
+
}
|
|
56
|
+
error(message, context) {
|
|
57
|
+
if (!this.enabled)
|
|
58
|
+
return;
|
|
59
|
+
this.logger.log('error', message, context);
|
|
60
|
+
}
|
|
61
|
+
async shutdown() {
|
|
62
|
+
await this.queue.flush();
|
|
63
|
+
this.queue.stop();
|
|
64
|
+
this.detachFromConsole();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.DrTrace = DrTrace;
|
package/dist/queue.d.ts
CHANGED
|
File without changes
|
package/dist/queue.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|