drtrace 0.2.0 → 0.4.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 +74 -4
- package/agents/CONTRIBUTING.md +296 -0
- package/agents/README.md +174 -0
- package/agents/daemon-method-selection.md +370 -0
- package/agents/integration-guides/cpp-best-practices.md +218 -0
- package/agents/integration-guides/cpp-ros-integration.md +88 -0
- package/agents/log-analysis.md +218 -0
- package/agents/log-help.md +226 -0
- package/agents/log-init.md +933 -0
- package/agents/log-it.md +1126 -0
- package/bin/init.js +4 -4
- package/dist/bin/init.js +31 -0
- package/dist/browser.d.ts +28 -0
- package/dist/browser.js +91 -0
- package/dist/config-schema.d.ts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/init.d.ts +44 -2
- package/dist/init.js +460 -30
- 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/resources/agents/CONTRIBUTING.md +296 -0
- package/dist/resources/agents/README.md +174 -0
- package/dist/resources/agents/daemon-method-selection.md +370 -0
- package/dist/resources/agents/integration-guides/cpp-best-practices.md +218 -0
- package/dist/resources/agents/integration-guides/cpp-ros-integration.md +88 -0
- package/dist/resources/agents/log-analysis.md +218 -0
- package/dist/resources/agents/log-help.md +226 -0
- package/dist/resources/agents/log-init.md +933 -0
- package/dist/resources/agents/log-it.md +1126 -0
- package/dist/resources/cpp/drtrace_sink.hpp +1249 -0
- package/dist/transport.js +5 -1
- package/dist/types.d.ts +8 -2
- package/package.json +28 -4
- package/.eslintrc.js +0 -20
- package/jest.config.js +0 -11
- package/src/client.ts +0 -68
- package/src/config-schema.ts +0 -115
- package/src/config.ts +0 -326
- package/src/index.ts +0 -3
- package/src/init.ts +0 -451
- package/src/logger.ts +0 -56
- package/src/queue.ts +0 -105
- package/src/transport.ts +0 -60
- package/src/types.ts +0 -20
- package/tests/client.test.ts +0 -66
- package/tests/config-schema.test.ts +0 -198
- package/tests/config.test.ts +0 -456
- package/tests/queue.test.ts +0 -72
- package/tests/transport.test.ts +0 -52
- package/tsconfig.json +0 -18
package/dist/transport.js
CHANGED
|
@@ -15,7 +15,11 @@ class Transport {
|
|
|
15
15
|
if (events.length === 0)
|
|
16
16
|
return;
|
|
17
17
|
const url = `${this.daemonUrl}/logs/ingest`;
|
|
18
|
-
|
|
18
|
+
// Wrap batch with application_id at top level as required by daemon API
|
|
19
|
+
const payload = {
|
|
20
|
+
application_id: events[0].application_id,
|
|
21
|
+
logs: events,
|
|
22
|
+
};
|
|
19
23
|
const fetchImpl = globalThis.fetch;
|
|
20
24
|
if (!fetchImpl)
|
|
21
25
|
return; // Non-blocking skip
|
package/dist/types.d.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
2
2
|
export interface LogEvent {
|
|
3
|
-
|
|
4
|
-
applicationId: string;
|
|
3
|
+
ts: number;
|
|
5
4
|
level: LogLevel;
|
|
6
5
|
message: string;
|
|
6
|
+
application_id: string;
|
|
7
|
+
module_name: string;
|
|
7
8
|
context?: Record<string, unknown>;
|
|
8
9
|
}
|
|
10
|
+
export interface LogBatch {
|
|
11
|
+
application_id: string;
|
|
12
|
+
logs: LogEvent[];
|
|
13
|
+
}
|
|
9
14
|
export interface ClientOptions {
|
|
10
15
|
applicationId: string;
|
|
11
16
|
daemonUrl: string;
|
|
17
|
+
moduleName?: string;
|
|
12
18
|
enabled?: boolean;
|
|
13
19
|
logLevel?: LogLevel;
|
|
14
20
|
batchSize?: number;
|
package/package.json
CHANGED
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drtrace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "DrTrace JavaScript/TypeScript client",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
|
+
"browser": "dist/browser.js",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
7
8
|
"exports": {
|
|
8
9
|
".": {
|
|
9
10
|
"types": "./dist/index.d.ts",
|
|
11
|
+
"browser": "./dist/browser.js",
|
|
12
|
+
"node": "./dist/index.js",
|
|
10
13
|
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./browser": {
|
|
16
|
+
"types": "./dist/browser.d.ts",
|
|
17
|
+
"default": "./dist/browser.js"
|
|
11
18
|
}
|
|
12
19
|
},
|
|
13
20
|
"bin": {
|
|
14
21
|
"drtrace": "dist/bin/init.js"
|
|
15
22
|
},
|
|
16
23
|
"scripts": {
|
|
17
|
-
"
|
|
24
|
+
"prebuild": "node scripts/copy-agents.js",
|
|
25
|
+
"build": "tsc && mkdir -p dist/bin && cp bin/init.js dist/bin/init.js && chmod +x dist/bin/init.js && mkdir -p dist/resources/agents && (cp -r agents/* dist/resources/agents/ 2>/dev/null || true) && (cp -r ../../../agents/integration-guides/* dist/resources/agents/integration-guides/ 2>/dev/null || true) && mkdir -p dist/resources/cpp && cp ../../../packages/cpp/drtrace-client/src/drtrace_sink.hpp dist/resources/cpp/ 2>/dev/null || true",
|
|
18
26
|
"test": "jest",
|
|
19
27
|
"test:watch": "jest --watch",
|
|
20
28
|
"lint": "eslint src --ext .ts",
|
|
@@ -44,11 +52,27 @@
|
|
|
44
52
|
"error-tracking",
|
|
45
53
|
"debugging"
|
|
46
54
|
],
|
|
47
|
-
"author":
|
|
55
|
+
"author": {
|
|
56
|
+
"name": "Thanh Cao",
|
|
57
|
+
"email": "caoduythanhcantho@gmail.com"
|
|
58
|
+
},
|
|
48
59
|
"license": "MIT",
|
|
60
|
+
"files": [
|
|
61
|
+
"dist/",
|
|
62
|
+
"agents/",
|
|
63
|
+
"bin/",
|
|
64
|
+
"resources/"
|
|
65
|
+
],
|
|
49
66
|
"repository": {
|
|
50
67
|
"type": "git",
|
|
51
|
-
"url": "https://github.com/
|
|
68
|
+
"url": "git+https://github.com/CaoDuyThanh/drtrace.git",
|
|
52
69
|
"directory": "packages/javascript/drtrace-client"
|
|
70
|
+
},
|
|
71
|
+
"homepage": "https://github.com/CaoDuyThanh/drtrace#readme",
|
|
72
|
+
"bugs": {
|
|
73
|
+
"url": "https://github.com/CaoDuyThanh/drtrace/issues"
|
|
74
|
+
},
|
|
75
|
+
"engines": {
|
|
76
|
+
"node": ">=16.0.0"
|
|
53
77
|
}
|
|
54
78
|
}
|
package/.eslintrc.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
parser: '@typescript-eslint/parser',
|
|
3
|
-
parserOptions: {
|
|
4
|
-
ecmaVersion: 2020,
|
|
5
|
-
sourceType: 'module',
|
|
6
|
-
},
|
|
7
|
-
extends: [
|
|
8
|
-
'eslint:recommended',
|
|
9
|
-
'plugin:@typescript-eslint/recommended',
|
|
10
|
-
],
|
|
11
|
-
plugins: ['@typescript-eslint'],
|
|
12
|
-
env: {
|
|
13
|
-
node: true,
|
|
14
|
-
es6: true,
|
|
15
|
-
},
|
|
16
|
-
rules: {
|
|
17
|
-
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
|
18
|
-
'@typescript-eslint/no-explicit-any': 'warn',
|
|
19
|
-
},
|
|
20
|
-
};
|
package/jest.config.js
DELETED
package/src/client.ts
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { loadConfig } from './config';
|
|
2
|
-
import { Transport } from './transport';
|
|
3
|
-
import { LogQueue } from './queue';
|
|
4
|
-
import { DrTraceLogger } from './logger';
|
|
5
|
-
import type { ClientOptions, LogLevel } from './types';
|
|
6
|
-
|
|
7
|
-
export class DrTrace {
|
|
8
|
-
private queue: LogQueue;
|
|
9
|
-
private logger: DrTraceLogger;
|
|
10
|
-
private enabled: boolean;
|
|
11
|
-
|
|
12
|
-
private constructor(opts: ClientOptions) {
|
|
13
|
-
const transport = new Transport({
|
|
14
|
-
daemonUrl: opts.daemonUrl,
|
|
15
|
-
maxRetries: opts.maxRetries ?? 3,
|
|
16
|
-
timeoutMs: 5000,
|
|
17
|
-
});
|
|
18
|
-
this.queue = new LogQueue({
|
|
19
|
-
transport,
|
|
20
|
-
batchSize: opts.batchSize ?? 50,
|
|
21
|
-
flushIntervalMs: opts.flushIntervalMs ?? 1000,
|
|
22
|
-
maxQueueSize: opts.maxQueueSize ?? 10000,
|
|
23
|
-
});
|
|
24
|
-
this.queue.start();
|
|
25
|
-
|
|
26
|
-
this.enabled = opts.enabled ?? true;
|
|
27
|
-
this.logger = new DrTraceLogger({ queue: this.queue, applicationId: opts.applicationId, logLevel: (opts.logLevel ?? 'info') as LogLevel });
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
static init(opts?: Partial<ClientOptions>): DrTrace {
|
|
31
|
-
const cfg = loadConfig({ projectRoot: '.', environment: process.env.NODE_ENV });
|
|
32
|
-
const merged: ClientOptions = {
|
|
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') as LogLevel,
|
|
37
|
-
batchSize: cfg.drtrace.batchSize ?? 50,
|
|
38
|
-
flushIntervalMs: cfg.drtrace.flushIntervalMs ?? 1000,
|
|
39
|
-
...opts,
|
|
40
|
-
};
|
|
41
|
-
return new DrTrace(merged);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
attachToConsole(): void {
|
|
45
|
-
if (!this.enabled) return;
|
|
46
|
-
this.logger.attachToConsole();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
detachFromConsole(): void {
|
|
50
|
-
this.logger.detachFromConsole();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
log(message: string, level: LogLevel = 'info', context?: Record<string, unknown>): void {
|
|
54
|
-
if (!this.enabled) return;
|
|
55
|
-
this.logger.log(level, message, context);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
error(message: string, context?: Record<string, unknown>): void {
|
|
59
|
-
if (!this.enabled) return;
|
|
60
|
-
this.logger.log('error', message, context);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async shutdown(): Promise<void> {
|
|
64
|
-
await this.queue.flush();
|
|
65
|
-
this.queue.stop();
|
|
66
|
-
this.detachFromConsole();
|
|
67
|
-
}
|
|
68
|
-
}
|
package/src/config-schema.ts
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration schema and validation for DrTrace projects.
|
|
3
|
-
* Mirrors the Python config_schema.py implementation.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import * as fs from "fs";
|
|
7
|
-
import * as path from "path";
|
|
8
|
-
|
|
9
|
-
export interface DrTraceConfig {
|
|
10
|
-
project_name: string;
|
|
11
|
-
application_id: string;
|
|
12
|
-
language?: "python" | "javascript" | "both";
|
|
13
|
-
daemon_url?: string;
|
|
14
|
-
enabled?: boolean;
|
|
15
|
-
environments?: string[];
|
|
16
|
-
agent?: {
|
|
17
|
-
enabled?: boolean;
|
|
18
|
-
framework?: "bmad" | "langchain" | "other";
|
|
19
|
-
};
|
|
20
|
-
created_at?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export class ConfigSchema {
|
|
24
|
-
private static VALID_ENVIRONMENTS = [
|
|
25
|
-
"development",
|
|
26
|
-
"staging",
|
|
27
|
-
"production",
|
|
28
|
-
"ci",
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Get default configuration with provided values
|
|
33
|
-
*/
|
|
34
|
-
static getDefaultConfig(options: {
|
|
35
|
-
project_name: string;
|
|
36
|
-
application_id: string;
|
|
37
|
-
language?: "python" | "javascript" | "both";
|
|
38
|
-
daemon_url?: string;
|
|
39
|
-
enabled?: boolean;
|
|
40
|
-
environments?: string[];
|
|
41
|
-
agent_enabled?: boolean;
|
|
42
|
-
agent_framework?: "bmad" | "langchain" | "other";
|
|
43
|
-
}): DrTraceConfig {
|
|
44
|
-
return {
|
|
45
|
-
project_name: options.project_name,
|
|
46
|
-
application_id: options.application_id,
|
|
47
|
-
language: options.language || "javascript",
|
|
48
|
-
daemon_url: options.daemon_url || "http://localhost:8001",
|
|
49
|
-
enabled: options.enabled !== false,
|
|
50
|
-
environments: options.environments || ["development"],
|
|
51
|
-
agent: {
|
|
52
|
-
enabled: options.agent_enabled || false,
|
|
53
|
-
framework: options.agent_framework || "bmad",
|
|
54
|
-
},
|
|
55
|
-
created_at: new Date().toISOString(),
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Validate configuration against schema
|
|
61
|
-
*/
|
|
62
|
-
static validate(config: DrTraceConfig): boolean {
|
|
63
|
-
// Required fields
|
|
64
|
-
if (!config.project_name || typeof config.project_name !== "string") {
|
|
65
|
-
throw new Error("Missing required field: project_name");
|
|
66
|
-
}
|
|
67
|
-
if (!config.application_id || typeof config.application_id !== "string") {
|
|
68
|
-
throw new Error("Missing required field: application_id");
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Type validation
|
|
72
|
-
if (config.enabled !== undefined && typeof config.enabled !== "boolean") {
|
|
73
|
-
throw new Error("Field 'enabled' must be boolean");
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Environment validation
|
|
77
|
-
if (config.environments) {
|
|
78
|
-
if (!Array.isArray(config.environments)) {
|
|
79
|
-
throw new Error("Field 'environments' must be an array");
|
|
80
|
-
}
|
|
81
|
-
for (const env of config.environments) {
|
|
82
|
-
if (!this.VALID_ENVIRONMENTS.includes(env)) {
|
|
83
|
-
throw new Error(`Invalid environment: ${env}`);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Save configuration to file
|
|
93
|
-
*/
|
|
94
|
-
static save(config: DrTraceConfig, filePath: string): void {
|
|
95
|
-
this.validate(config);
|
|
96
|
-
const dir = path.dirname(filePath);
|
|
97
|
-
if (!fs.existsSync(dir)) {
|
|
98
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
99
|
-
}
|
|
100
|
-
fs.writeFileSync(filePath, JSON.stringify(config, null, 2));
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Load configuration from file
|
|
105
|
-
*/
|
|
106
|
-
static load(filePath: string): DrTraceConfig {
|
|
107
|
-
if (!fs.existsSync(filePath)) {
|
|
108
|
-
throw new Error(`Config file not found: ${filePath}`);
|
|
109
|
-
}
|
|
110
|
-
const content = fs.readFileSync(filePath, "utf-8");
|
|
111
|
-
const config = JSON.parse(content);
|
|
112
|
-
this.validate(config);
|
|
113
|
-
return config;
|
|
114
|
-
}
|
|
115
|
-
}
|
package/src/config.ts
DELETED
|
@@ -1,326 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration loading and management for DrTrace JavaScript client.
|
|
3
|
-
*
|
|
4
|
-
* Implements hierarchical config loading with the following priority:
|
|
5
|
-
* 1. Environment variables (highest)
|
|
6
|
-
* 2. _drtrace/config.json (per-project)
|
|
7
|
-
* 3. _drtrace/config.{NODE_ENV}.json (environment-specific overrides)
|
|
8
|
-
* 4. Default values (lowest)
|
|
9
|
-
*
|
|
10
|
-
* Usage:
|
|
11
|
-
* ```typescript
|
|
12
|
-
* import { ConfigLoader } from './config';
|
|
13
|
-
* const config = ConfigLoader.load({ projectRoot: '.', environment: 'production' });
|
|
14
|
-
* console.log(config.drtrace.applicationId);
|
|
15
|
-
* ```
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import * as fs from 'fs';
|
|
19
|
-
import * as path from 'path';
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* DrTrace Configuration interface
|
|
23
|
-
*/
|
|
24
|
-
export interface DrTraceConfig {
|
|
25
|
-
project: {
|
|
26
|
-
name: string;
|
|
27
|
-
language?: string;
|
|
28
|
-
description?: string;
|
|
29
|
-
};
|
|
30
|
-
drtrace: {
|
|
31
|
-
applicationId: string;
|
|
32
|
-
daemonUrl?: string;
|
|
33
|
-
enabled?: boolean;
|
|
34
|
-
logLevel?: 'debug' | 'info' | 'warn' | 'error';
|
|
35
|
-
batchSize?: number;
|
|
36
|
-
flushIntervalMs?: number;
|
|
37
|
-
retentionDays?: number;
|
|
38
|
-
};
|
|
39
|
-
agent?: {
|
|
40
|
-
enabled?: boolean;
|
|
41
|
-
agentFile?: string | null;
|
|
42
|
-
framework?: 'bmad' | 'langchain' | 'other' | null;
|
|
43
|
-
};
|
|
44
|
-
environment?: {
|
|
45
|
-
[key: string]: Partial<DrTraceConfig>;
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Schema validation and defaults for DrTrace configuration.
|
|
51
|
-
*/
|
|
52
|
-
export class ConfigSchema {
|
|
53
|
-
/**
|
|
54
|
-
* Default configuration values
|
|
55
|
-
*/
|
|
56
|
-
static readonly DEFAULTS: DrTraceConfig = {
|
|
57
|
-
project: {
|
|
58
|
-
name: 'my-app',
|
|
59
|
-
language: 'javascript',
|
|
60
|
-
description: 'My application',
|
|
61
|
-
},
|
|
62
|
-
drtrace: {
|
|
63
|
-
applicationId: 'my-app',
|
|
64
|
-
daemonUrl: 'http://localhost:8001',
|
|
65
|
-
enabled: true,
|
|
66
|
-
logLevel: 'info',
|
|
67
|
-
batchSize: 50,
|
|
68
|
-
flushIntervalMs: 1000,
|
|
69
|
-
retentionDays: 7,
|
|
70
|
-
},
|
|
71
|
-
agent: {
|
|
72
|
-
enabled: false,
|
|
73
|
-
agentFile: null,
|
|
74
|
-
framework: null,
|
|
75
|
-
},
|
|
76
|
-
environment: {},
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Validate configuration against schema.
|
|
81
|
-
*
|
|
82
|
-
* @param config - Configuration to validate
|
|
83
|
-
* @returns Validated configuration
|
|
84
|
-
* @throws Error if validation fails
|
|
85
|
-
*/
|
|
86
|
-
static validate(config: any): DrTraceConfig {
|
|
87
|
-
const errors: string[] = [];
|
|
88
|
-
|
|
89
|
-
// Check for required sections
|
|
90
|
-
if (!config.project) {
|
|
91
|
-
errors.push('Missing required section: project');
|
|
92
|
-
} else {
|
|
93
|
-
if (!config.project.name) {
|
|
94
|
-
errors.push('Missing required field: project.name');
|
|
95
|
-
}
|
|
96
|
-
if (typeof config.project.name !== 'string' && config.project.name !== undefined) {
|
|
97
|
-
errors.push(`Invalid type for project.name: expected string, got ${typeof config.project.name}`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (!config.drtrace) {
|
|
102
|
-
errors.push('Missing required section: drtrace');
|
|
103
|
-
} else {
|
|
104
|
-
if (!config.drtrace.applicationId) {
|
|
105
|
-
errors.push('Missing required field: drtrace.applicationId');
|
|
106
|
-
}
|
|
107
|
-
if (typeof config.drtrace.applicationId !== 'string' && config.drtrace.applicationId !== undefined) {
|
|
108
|
-
errors.push(
|
|
109
|
-
`Invalid type for drtrace.applicationId: expected string, got ${typeof config.drtrace.applicationId}`
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Validate enums
|
|
115
|
-
if (config.drtrace?.logLevel) {
|
|
116
|
-
const validLevels = ['debug', 'info', 'warn', 'error'];
|
|
117
|
-
if (!validLevels.includes(config.drtrace.logLevel)) {
|
|
118
|
-
errors.push(
|
|
119
|
-
`Invalid logLevel: ${config.drtrace.logLevel}. Must be one of: ${validLevels.join(', ')}`
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (config.agent?.framework) {
|
|
125
|
-
const validFrameworks = ['bmad', 'langchain', 'other'];
|
|
126
|
-
if (!validFrameworks.includes(config.agent.framework)) {
|
|
127
|
-
errors.push(
|
|
128
|
-
`Invalid agent framework: ${config.agent.framework}. Must be one of: ${validFrameworks.join(', ')}`
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Validate types
|
|
134
|
-
if (config.drtrace?.enabled !== undefined && typeof config.drtrace.enabled !== 'boolean') {
|
|
135
|
-
errors.push(`Invalid type for drtrace.enabled: expected boolean, got ${typeof config.drtrace.enabled}`);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (config.drtrace?.batchSize !== undefined && typeof config.drtrace.batchSize !== 'number') {
|
|
139
|
-
errors.push(`Invalid type for drtrace.batchSize: expected number, got ${typeof config.drtrace.batchSize}`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (config.drtrace?.flushIntervalMs !== undefined && typeof config.drtrace.flushIntervalMs !== 'number') {
|
|
143
|
-
errors.push(
|
|
144
|
-
`Invalid type for drtrace.flushIntervalMs: expected number, got ${typeof config.drtrace.flushIntervalMs}`
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (config.drtrace?.retentionDays !== undefined && typeof config.drtrace.retentionDays !== 'number') {
|
|
149
|
-
errors.push(`Invalid type for drtrace.retentionDays: expected number, got ${typeof config.drtrace.retentionDays}`);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (errors.length > 0) {
|
|
153
|
-
throw new Error(`Configuration validation failed:\n${errors.join('\n')}`);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return config as DrTraceConfig;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Get default configuration
|
|
161
|
-
*/
|
|
162
|
-
static getDefault(): DrTraceConfig {
|
|
163
|
-
return JSON.parse(JSON.stringify(ConfigSchema.DEFAULTS));
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Configuration loading and merging utility
|
|
169
|
-
*/
|
|
170
|
-
export class ConfigLoader {
|
|
171
|
-
/**
|
|
172
|
-
* Load configuration with hierarchical merging.
|
|
173
|
-
*
|
|
174
|
-
* Priority (highest to lowest):
|
|
175
|
-
* 1. Environment variables (process.env with DRTRACE_* prefix)
|
|
176
|
-
* 2. _drtrace/config.json
|
|
177
|
-
* 3. _drtrace/config.{environment}.json
|
|
178
|
-
* 4. Default values
|
|
179
|
-
*
|
|
180
|
-
* @param options - Loading options
|
|
181
|
-
* @returns Loaded and validated configuration
|
|
182
|
-
*/
|
|
183
|
-
static load(options: { projectRoot?: string; environment?: string } = {}): DrTraceConfig {
|
|
184
|
-
const projectRoot = options.projectRoot || '.';
|
|
185
|
-
const configDir = path.join(projectRoot, '_drtrace');
|
|
186
|
-
|
|
187
|
-
// Start with defaults
|
|
188
|
-
let config = ConfigSchema.getDefault();
|
|
189
|
-
|
|
190
|
-
// Determine environment
|
|
191
|
-
let environment = options.environment;
|
|
192
|
-
if (!environment) {
|
|
193
|
-
environment = process.env.NODE_ENV || process.env.PYTHON_ENV || undefined;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Load base config from _drtrace/config.json
|
|
197
|
-
const baseConfigPath = path.join(configDir, 'config.json');
|
|
198
|
-
if (fs.existsSync(baseConfigPath)) {
|
|
199
|
-
const baseConfig = ConfigLoader.loadJsonFile(baseConfigPath);
|
|
200
|
-
config = ConfigLoader.mergeConfigs(config, baseConfig);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Load environment-specific overrides
|
|
204
|
-
if (environment) {
|
|
205
|
-
// First, check for environment-specific subsection in base config
|
|
206
|
-
if (config.environment && environment in config.environment) {
|
|
207
|
-
config = ConfigLoader.mergeConfigs(config, {
|
|
208
|
-
drtrace: config.environment[environment],
|
|
209
|
-
} as any);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Then, load environment-specific config file (takes precedence)
|
|
213
|
-
const envConfigPath = path.join(configDir, `config.${environment}.json`);
|
|
214
|
-
if (fs.existsSync(envConfigPath)) {
|
|
215
|
-
const envConfig = ConfigLoader.loadJsonFile(envConfigPath);
|
|
216
|
-
config = ConfigLoader.mergeConfigs(config, envConfig);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Apply environment variable overrides (highest priority)
|
|
221
|
-
config = ConfigLoader.applyEnvVarOverrides(config);
|
|
222
|
-
|
|
223
|
-
// Validate final configuration
|
|
224
|
-
config = ConfigSchema.validate(config);
|
|
225
|
-
|
|
226
|
-
return config;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Load and parse JSON file
|
|
231
|
-
*/
|
|
232
|
-
private static loadJsonFile(filepath: string): any {
|
|
233
|
-
try {
|
|
234
|
-
const content = fs.readFileSync(filepath, 'utf-8');
|
|
235
|
-
return JSON.parse(content);
|
|
236
|
-
} catch (error: any) {
|
|
237
|
-
if (error instanceof SyntaxError) {
|
|
238
|
-
throw new Error(`Invalid JSON in ${filepath}: ${error.message}`);
|
|
239
|
-
}
|
|
240
|
-
throw new Error(`Error reading ${filepath}: ${error.message}`);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Deep merge override config into base config
|
|
246
|
-
*/
|
|
247
|
-
private static mergeConfigs(base: any, overrides: any): any {
|
|
248
|
-
const result = JSON.parse(JSON.stringify(base)); // Deep copy
|
|
249
|
-
|
|
250
|
-
for (const key in overrides) {
|
|
251
|
-
if (Object.prototype.hasOwnProperty.call(overrides, key)) {
|
|
252
|
-
const value = overrides[key];
|
|
253
|
-
if (
|
|
254
|
-
result[key] &&
|
|
255
|
-
typeof result[key] === 'object' &&
|
|
256
|
-
!Array.isArray(result[key]) &&
|
|
257
|
-
typeof value === 'object' &&
|
|
258
|
-
!Array.isArray(value)
|
|
259
|
-
) {
|
|
260
|
-
result[key] = ConfigLoader.mergeConfigs(result[key], value);
|
|
261
|
-
} else {
|
|
262
|
-
result[key] = value;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
return result;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Apply environment variable overrides to config.
|
|
272
|
-
*
|
|
273
|
-
* Environment variables use DRTRACE_* prefix with UPPER_SNAKE_CASE.
|
|
274
|
-
* Examples:
|
|
275
|
-
* - DRTRACE_APPLICATION_ID -> config.drtrace.applicationId
|
|
276
|
-
* - DRTRACE_DAEMON_URL -> config.drtrace.daemonUrl
|
|
277
|
-
* - DRTRACE_ENABLED -> config.drtrace.enabled
|
|
278
|
-
*/
|
|
279
|
-
private static applyEnvVarOverrides(config: any): any {
|
|
280
|
-
const result = JSON.parse(JSON.stringify(config)); // Deep copy
|
|
281
|
-
|
|
282
|
-
const envVarMappings: Record<string, [string, string]> = {
|
|
283
|
-
DRTRACE_APPLICATION_ID: ['drtrace', 'applicationId'],
|
|
284
|
-
DRTRACE_DAEMON_URL: ['drtrace', 'daemonUrl'],
|
|
285
|
-
DRTRACE_ENABLED: ['drtrace', 'enabled'],
|
|
286
|
-
DRTRACE_LOG_LEVEL: ['drtrace', 'logLevel'],
|
|
287
|
-
DRTRACE_BATCH_SIZE: ['drtrace', 'batchSize'],
|
|
288
|
-
DRTRACE_FLUSH_INTERVAL_MS: ['drtrace', 'flushIntervalMs'],
|
|
289
|
-
DRTRACE_RETENTION_DAYS: ['drtrace', 'retentionDays'],
|
|
290
|
-
DRTRACE_AGENT_ENABLED: ['agent', 'enabled'],
|
|
291
|
-
DRTRACE_AGENT_FILE: ['agent', 'agentFile'],
|
|
292
|
-
DRTRACE_AGENT_FRAMEWORK: ['agent', 'framework'],
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
for (const [envVar, [section, field]] of Object.entries(envVarMappings)) {
|
|
296
|
-
if (process.env[envVar]) {
|
|
297
|
-
let value: any = process.env[envVar];
|
|
298
|
-
|
|
299
|
-
// Type conversions
|
|
300
|
-
if (field === 'enabled') {
|
|
301
|
-
value = ['true', '1', 'yes'].includes(value.toLowerCase());
|
|
302
|
-
} else if (['batchSize', 'flushIntervalMs', 'retentionDays'].includes(field)) {
|
|
303
|
-
const parsed = parseInt(value, 10);
|
|
304
|
-
if (isNaN(parsed)) {
|
|
305
|
-
throw new Error(`Invalid value for ${envVar}: must be an integer`);
|
|
306
|
-
}
|
|
307
|
-
value = parsed;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (!result[section]) {
|
|
311
|
-
result[section] = {};
|
|
312
|
-
}
|
|
313
|
-
result[section][field] = value;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
return result;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Convenience function to load configuration
|
|
323
|
-
*/
|
|
324
|
-
export function loadConfig(options?: { projectRoot?: string; environment?: string }): DrTraceConfig {
|
|
325
|
-
return ConfigLoader.load(options);
|
|
326
|
-
}
|
package/src/index.ts
DELETED