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.
Files changed (51) hide show
  1. package/README.md +45 -4
  2. package/agents/CONTRIBUTING.md +0 -0
  3. package/agents/README.md +0 -0
  4. package/agents/daemon-method-selection.md +126 -227
  5. package/agents/integration-guides/cpp-best-practices.md +0 -0
  6. package/agents/integration-guides/cpp-ros-integration.md +0 -0
  7. package/agents/log-analysis.md +3 -89
  8. package/agents/log-help.md +6 -79
  9. package/agents/log-init.md +4 -0
  10. package/agents/log-it.md +4 -0
  11. package/bin/cli.js +98 -0
  12. package/bin/init.js +0 -0
  13. package/dist/bin/cli.js +98 -0
  14. package/dist/browser.d.ts +28 -0
  15. package/dist/browser.js +91 -0
  16. package/dist/cli/grep.d.ts +27 -0
  17. package/dist/cli/grep.js +251 -0
  18. package/dist/cli/status.d.ts +11 -0
  19. package/dist/cli/status.js +78 -0
  20. package/dist/client.d.ts +0 -0
  21. package/dist/client.js +0 -0
  22. package/dist/config-schema.d.ts +0 -0
  23. package/dist/config-schema.js +0 -0
  24. package/dist/config.d.ts +0 -0
  25. package/dist/config.js +0 -0
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.js +2 -2
  28. package/dist/init.d.ts +2 -0
  29. package/dist/init.js +23 -18
  30. package/dist/logger.d.ts +7 -0
  31. package/dist/logger.js +30 -4
  32. package/dist/node.d.ts +13 -0
  33. package/dist/node.js +67 -0
  34. package/dist/queue.d.ts +0 -0
  35. package/dist/queue.js +0 -0
  36. package/dist/resources/agents/CONTRIBUTING.md +0 -0
  37. package/dist/resources/agents/README.md +0 -0
  38. package/dist/resources/agents/daemon-method-selection.md +126 -227
  39. package/dist/resources/agents/integration-guides/cpp-best-practices.md +0 -0
  40. package/dist/resources/agents/integration-guides/cpp-ros-integration.md +0 -0
  41. package/dist/resources/agents/log-analysis.md +3 -89
  42. package/dist/resources/agents/log-help.md +6 -79
  43. package/dist/resources/agents/log-init.md +4 -0
  44. package/dist/resources/agents/log-it.md +4 -0
  45. package/dist/resources/cpp/drtrace_sink.hpp +1 -0
  46. package/dist/transport.d.ts +0 -0
  47. package/dist/transport.js +5 -1
  48. package/dist/types.d.ts +8 -2
  49. package/dist/types.js +0 -0
  50. package/package.json +22 -5
  51. package/dist/bin/init.js +0 -31
@@ -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
File without changes
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
@@ -1,3 +1,3 @@
1
- export { DrTrace } from './client';
1
+ export { DrTrace } from './node';
2
2
  export { loadConfig } from './config';
3
3
  export * from './types';
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 client_1 = require("./client");
19
- Object.defineProperty(exports, "DrTrace", { enumerable: true, get: function () { return client_1.DrTrace; } });
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
- let copiedCount = 0;
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
- copiedCount++;
290
- console.log(`✓ Copied ${entry.name}`);
295
+ copiedFiles.push(relativePath);
296
+ console.log(`✓ Copied ${relativePath}`);
291
297
  }
292
298
  }
293
- if (copiedCount > 0 && !fs.existsSync(path.join(targetDir, "..", ".."))) {
294
- // Only print summary if we're at the top level
295
- const relativeSource = path.relative(process.cwd(), sourceDir);
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
- if (config.agent?.enabled) {
785
- console.log(` • ${path.join(this.drtraceDir, "agents", "log-analysis.md")}`);
786
- console.log(` • ${path.join(this.drtraceDir, "agents", "log-it.md")}`);
787
- console.log(` • ${path.join(this.drtraceDir, "agents", "log-init.md")}`);
788
- console.log(` • ${path.join(this.drtraceDir, "agents", "log-help.md")}`);
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(String).join(' '));
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(String).join(' '));
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
- timestamp: new Date().toISOString(),
43
- applicationId: this.applicationId,
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