@probelabs/probe 0.6.0-rc147 → 0.6.0-rc149
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/bin/probe +12 -3
- package/build/agent/ProbeAgent.js +1 -3
- package/build/agent/index.js +58 -477
- package/build/extract.js +1 -1
- package/build/index.js +1 -7
- package/build/mcp/index.js +3 -3
- package/build/mcp/index.ts +3 -3
- package/build/tools/common.js +58 -49
- package/cjs/agent/ProbeAgent.cjs +699 -770
- package/cjs/index.cjs +650 -1347
- package/package.json +1 -12
- package/src/agent/ProbeAgent.js +1 -3
- package/src/extract.js +1 -1
- package/src/index.js +1 -7
- package/src/mcp/index.ts +3 -3
- package/src/tools/common.js +58 -49
- package/bin/binaries/probe-v0.6.0-rc147-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc147-aarch64-unknown-linux-gnu.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc147-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc147-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc147-x86_64-unknown-linux-gnu.tar.gz +0 -0
- package/build/agent/appTracer.js +0 -360
- package/build/agent/fileSpanExporter.js +0 -167
- package/build/agent/telemetry.js +0 -219
- package/cjs/agent/telemetry.cjs +0 -358
- package/src/agent/appTracer.js +0 -360
- package/src/agent/fileSpanExporter.js +0 -167
- package/src/agent/telemetry.js +0 -219
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import { createWriteStream } from 'fs';
|
|
2
|
-
import { ExportResultCode } from '@opentelemetry/core';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* File exporter for OpenTelemetry spans
|
|
6
|
-
* Exports spans to a file in JSON Lines format (one JSON object per line)
|
|
7
|
-
* Following the OTLP JSON format specification
|
|
8
|
-
*/
|
|
9
|
-
export class FileSpanExporter {
|
|
10
|
-
constructor(filePath = './traces.jsonl') {
|
|
11
|
-
this.filePath = filePath;
|
|
12
|
-
this.stream = createWriteStream(filePath, { flags: 'a' });
|
|
13
|
-
this.stream.on('error', (error) => {
|
|
14
|
-
console.error(`[FileSpanExporter] Stream error: ${error.message}`);
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Export spans to file
|
|
20
|
-
* @param {ReadableSpan[]} spans - Array of spans to export
|
|
21
|
-
* @param {function} resultCallback - Callback to call with the export result
|
|
22
|
-
*/
|
|
23
|
-
export(spans, resultCallback) {
|
|
24
|
-
if (!spans || spans.length === 0) {
|
|
25
|
-
resultCallback({ code: ExportResultCode.SUCCESS });
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
const timestamp = Date.now();
|
|
31
|
-
|
|
32
|
-
spans.forEach((span, index) => {
|
|
33
|
-
// Extract parent span ID - check various possible properties
|
|
34
|
-
let parentSpanId = undefined;
|
|
35
|
-
|
|
36
|
-
if (span.parentSpanContext) {
|
|
37
|
-
parentSpanId = span.parentSpanContext.spanId;
|
|
38
|
-
} else if (span._parentSpanContext) {
|
|
39
|
-
parentSpanId = span._parentSpanContext.spanId;
|
|
40
|
-
} else if (span.parent) {
|
|
41
|
-
parentSpanId = span.parent.spanId;
|
|
42
|
-
} else if (span._parent) {
|
|
43
|
-
parentSpanId = span._parent.spanId;
|
|
44
|
-
} else if (span._parentId) {
|
|
45
|
-
parentSpanId = span._parentId;
|
|
46
|
-
} else if (span.parentSpanId) {
|
|
47
|
-
parentSpanId = span.parentSpanId;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Convert span to OTLP JSON format
|
|
51
|
-
const spanData = {
|
|
52
|
-
traceId: span.spanContext().traceId,
|
|
53
|
-
spanId: span.spanContext().spanId,
|
|
54
|
-
parentSpanId: parentSpanId,
|
|
55
|
-
name: span.name,
|
|
56
|
-
kind: span.kind,
|
|
57
|
-
startTimeUnixNano: span.startTime[0] * 1_000_000_000 + span.startTime[1],
|
|
58
|
-
endTimeUnixNano: span.endTime[0] * 1_000_000_000 + span.endTime[1],
|
|
59
|
-
attributes: this.convertAttributes(span.attributes),
|
|
60
|
-
status: span.status,
|
|
61
|
-
events: span.events?.map(event => ({
|
|
62
|
-
timeUnixNano: event.time[0] * 1_000_000_000 + event.time[1],
|
|
63
|
-
name: event.name,
|
|
64
|
-
attributes: this.convertAttributes(event.attributes),
|
|
65
|
-
})) || [],
|
|
66
|
-
links: span.links?.map(link => ({
|
|
67
|
-
traceId: link.context.traceId,
|
|
68
|
-
spanId: link.context.spanId,
|
|
69
|
-
attributes: this.convertAttributes(link.attributes),
|
|
70
|
-
})) || [],
|
|
71
|
-
resource: {
|
|
72
|
-
attributes: this.convertAttributes(span.resource?.attributes || {}),
|
|
73
|
-
},
|
|
74
|
-
instrumentationLibrary: {
|
|
75
|
-
name: span.instrumentationLibrary?.name || 'unknown',
|
|
76
|
-
version: span.instrumentationLibrary?.version || 'unknown',
|
|
77
|
-
},
|
|
78
|
-
timestamp,
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
// Write as JSON Lines format (one JSON object per line)
|
|
82
|
-
this.stream.write(JSON.stringify(spanData) + '\n');
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
resultCallback({ code: ExportResultCode.SUCCESS });
|
|
86
|
-
} catch (error) {
|
|
87
|
-
console.error(`[FileSpanExporter] Export error: ${error.message}`);
|
|
88
|
-
resultCallback({
|
|
89
|
-
code: ExportResultCode.FAILED,
|
|
90
|
-
error: error
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Convert OpenTelemetry attributes to plain object
|
|
97
|
-
* @param {Object} attributes - OpenTelemetry attributes
|
|
98
|
-
* @returns {Object} Plain object with string values
|
|
99
|
-
*/
|
|
100
|
-
convertAttributes(attributes) {
|
|
101
|
-
if (!attributes) return {};
|
|
102
|
-
|
|
103
|
-
const result = {};
|
|
104
|
-
for (const [key, value] of Object.entries(attributes)) {
|
|
105
|
-
// Convert all values to strings for JSON compatibility
|
|
106
|
-
if (typeof value === 'object' && value !== null) {
|
|
107
|
-
result[key] = JSON.stringify(value);
|
|
108
|
-
} else {
|
|
109
|
-
result[key] = String(value);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return result;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Shutdown the exporter
|
|
117
|
-
* @returns {Promise<void>}
|
|
118
|
-
*/
|
|
119
|
-
async shutdown() {
|
|
120
|
-
return new Promise((resolve) => {
|
|
121
|
-
if (this.stream) {
|
|
122
|
-
this.stream.end(() => {
|
|
123
|
-
console.log(`[FileSpanExporter] File stream closed: ${this.filePath}`);
|
|
124
|
-
resolve();
|
|
125
|
-
});
|
|
126
|
-
} else {
|
|
127
|
-
resolve();
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Force flush any pending spans
|
|
134
|
-
* @returns {Promise<void>}
|
|
135
|
-
*/
|
|
136
|
-
async forceFlush() {
|
|
137
|
-
return new Promise((resolve, reject) => {
|
|
138
|
-
if (this.stream) {
|
|
139
|
-
const flushTimeout = setTimeout(() => {
|
|
140
|
-
console.warn('[FileSpanExporter] Flush timeout after 5 seconds');
|
|
141
|
-
resolve();
|
|
142
|
-
}, 5000);
|
|
143
|
-
|
|
144
|
-
// Uncork the stream to force buffered writes
|
|
145
|
-
if (this.stream.writableCorked) {
|
|
146
|
-
this.stream.uncork();
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// If there's buffered data, wait for drain event
|
|
150
|
-
if (this.stream.writableNeedDrain) {
|
|
151
|
-
this.stream.once('drain', () => {
|
|
152
|
-
clearTimeout(flushTimeout);
|
|
153
|
-
resolve();
|
|
154
|
-
});
|
|
155
|
-
} else {
|
|
156
|
-
// No buffered data, but still give it a moment to ensure writes complete
|
|
157
|
-
setImmediate(() => {
|
|
158
|
-
clearTimeout(flushTimeout);
|
|
159
|
-
resolve();
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
} else {
|
|
163
|
-
resolve();
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
}
|
package/build/agent/telemetry.js
DELETED
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
import { NodeSDK } from '@opentelemetry/sdk-node';
|
|
2
|
-
import { resourceFromAttributes } from '@opentelemetry/resources';
|
|
3
|
-
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
|
|
4
|
-
import { trace, context, SpanStatusCode } from '@opentelemetry/api';
|
|
5
|
-
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
|
6
|
-
import { BatchSpanProcessor, ConsoleSpanExporter } from '@opentelemetry/sdk-trace-base';
|
|
7
|
-
|
|
8
|
-
import { existsSync, mkdirSync } from 'fs';
|
|
9
|
-
import { dirname } from 'path';
|
|
10
|
-
import { FileSpanExporter } from './fileSpanExporter.js';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Custom OpenTelemetry configuration for probe-agent
|
|
14
|
-
*/
|
|
15
|
-
export class TelemetryConfig {
|
|
16
|
-
constructor(options = {}) {
|
|
17
|
-
this.serviceName = options.serviceName || 'probe-agent';
|
|
18
|
-
this.serviceVersion = options.serviceVersion || '1.0.0';
|
|
19
|
-
this.enableFile = options.enableFile || false;
|
|
20
|
-
this.enableRemote = options.enableRemote || false;
|
|
21
|
-
this.enableConsole = options.enableConsole || false;
|
|
22
|
-
this.filePath = options.filePath || './traces.jsonl';
|
|
23
|
-
this.remoteEndpoint = options.remoteEndpoint || 'http://localhost:4318/v1/traces';
|
|
24
|
-
this.sdk = null;
|
|
25
|
-
this.tracer = null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Initialize OpenTelemetry SDK
|
|
30
|
-
*/
|
|
31
|
-
initialize() {
|
|
32
|
-
if (this.sdk) {
|
|
33
|
-
console.warn('Telemetry already initialized');
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const resource = resourceFromAttributes({
|
|
38
|
-
[ATTR_SERVICE_NAME]: this.serviceName,
|
|
39
|
-
[ATTR_SERVICE_VERSION]: this.serviceVersion,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const spanProcessors = [];
|
|
43
|
-
|
|
44
|
-
// Add file exporter if enabled
|
|
45
|
-
if (this.enableFile) {
|
|
46
|
-
try {
|
|
47
|
-
// Ensure the directory exists
|
|
48
|
-
const dir = dirname(this.filePath);
|
|
49
|
-
if (!existsSync(dir)) {
|
|
50
|
-
mkdirSync(dir, { recursive: true });
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const fileExporter = new FileSpanExporter(this.filePath);
|
|
54
|
-
spanProcessors.push(new BatchSpanProcessor(fileExporter, {
|
|
55
|
-
maxQueueSize: 2048,
|
|
56
|
-
maxExportBatchSize: 512,
|
|
57
|
-
scheduledDelayMillis: 500,
|
|
58
|
-
exportTimeoutMillis: 30000,
|
|
59
|
-
}));
|
|
60
|
-
console.log(`[Telemetry] File exporter enabled, writing to: ${this.filePath}`);
|
|
61
|
-
} catch (error) {
|
|
62
|
-
console.error(`[Telemetry] Failed to initialize file exporter: ${error.message}`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Add remote exporter if enabled
|
|
67
|
-
if (this.enableRemote) {
|
|
68
|
-
try {
|
|
69
|
-
const remoteExporter = new OTLPTraceExporter({
|
|
70
|
-
url: this.remoteEndpoint,
|
|
71
|
-
});
|
|
72
|
-
spanProcessors.push(new BatchSpanProcessor(remoteExporter, {
|
|
73
|
-
maxQueueSize: 2048,
|
|
74
|
-
maxExportBatchSize: 512,
|
|
75
|
-
scheduledDelayMillis: 500,
|
|
76
|
-
exportTimeoutMillis: 30000,
|
|
77
|
-
}));
|
|
78
|
-
console.log(`[Telemetry] Remote exporter enabled, endpoint: ${this.remoteEndpoint}`);
|
|
79
|
-
} catch (error) {
|
|
80
|
-
console.error(`[Telemetry] Failed to initialize remote exporter: ${error.message}`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Add console exporter if enabled (useful for debugging)
|
|
85
|
-
if (this.enableConsole) {
|
|
86
|
-
const consoleExporter = new ConsoleSpanExporter();
|
|
87
|
-
spanProcessors.push(new BatchSpanProcessor(consoleExporter, {
|
|
88
|
-
maxQueueSize: 2048,
|
|
89
|
-
maxExportBatchSize: 512,
|
|
90
|
-
scheduledDelayMillis: 500,
|
|
91
|
-
exportTimeoutMillis: 30000,
|
|
92
|
-
}));
|
|
93
|
-
console.log(`[Telemetry] Console exporter enabled`);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (spanProcessors.length === 0) {
|
|
97
|
-
console.log('[Telemetry] No exporters configured, telemetry will not be collected');
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
this.sdk = new NodeSDK({
|
|
102
|
-
resource,
|
|
103
|
-
spanProcessors,
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
try {
|
|
107
|
-
this.sdk.start();
|
|
108
|
-
this.tracer = trace.getTracer(this.serviceName, this.serviceVersion);
|
|
109
|
-
console.log(`[Telemetry] OpenTelemetry SDK initialized successfully`);
|
|
110
|
-
} catch (error) {
|
|
111
|
-
console.error(`[Telemetry] Failed to start OpenTelemetry SDK: ${error.message}`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Get the tracer instance
|
|
117
|
-
*/
|
|
118
|
-
getTracer() {
|
|
119
|
-
return this.tracer;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Create a span with the given name and attributes
|
|
124
|
-
*/
|
|
125
|
-
createSpan(name, attributes = {}) {
|
|
126
|
-
if (!this.tracer) {
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return this.tracer.startSpan(name, {
|
|
131
|
-
attributes,
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Wrap a function to automatically create spans
|
|
137
|
-
*/
|
|
138
|
-
wrapFunction(name, fn, attributes = {}) {
|
|
139
|
-
if (!this.tracer) {
|
|
140
|
-
return fn;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return async (...args) => {
|
|
144
|
-
const span = this.createSpan(name, attributes);
|
|
145
|
-
if (!span) {
|
|
146
|
-
return fn(...args);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
try {
|
|
150
|
-
const result = await context.with(trace.setSpan(context.active(), span), () => fn(...args));
|
|
151
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
152
|
-
return result;
|
|
153
|
-
} catch (error) {
|
|
154
|
-
span.setStatus({
|
|
155
|
-
code: SpanStatusCode.ERROR,
|
|
156
|
-
message: error.message,
|
|
157
|
-
});
|
|
158
|
-
span.recordException(error);
|
|
159
|
-
throw error;
|
|
160
|
-
} finally {
|
|
161
|
-
span.end();
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Force flush all pending spans
|
|
168
|
-
*/
|
|
169
|
-
async forceFlush() {
|
|
170
|
-
if (this.sdk) {
|
|
171
|
-
try {
|
|
172
|
-
const tracerProvider = trace.getTracerProvider();
|
|
173
|
-
|
|
174
|
-
if (tracerProvider && typeof tracerProvider.forceFlush === 'function') {
|
|
175
|
-
await tracerProvider.forceFlush();
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Add a small delay to ensure file writes complete
|
|
179
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
180
|
-
|
|
181
|
-
console.log('[Telemetry] OpenTelemetry spans flushed successfully');
|
|
182
|
-
} catch (error) {
|
|
183
|
-
console.error(`[Telemetry] Failed to flush OpenTelemetry spans: ${error.message}`);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Shutdown telemetry
|
|
190
|
-
*/
|
|
191
|
-
async shutdown() {
|
|
192
|
-
if (this.sdk) {
|
|
193
|
-
try {
|
|
194
|
-
await this.sdk.shutdown();
|
|
195
|
-
console.log('[Telemetry] OpenTelemetry SDK shutdown successfully');
|
|
196
|
-
} catch (error) {
|
|
197
|
-
console.error(`[Telemetry] Failed to shutdown OpenTelemetry SDK: ${error.message}`);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Initialize telemetry from CLI options
|
|
205
|
-
*/
|
|
206
|
-
export function initializeTelemetryFromOptions(options) {
|
|
207
|
-
const config = new TelemetryConfig({
|
|
208
|
-
serviceName: 'probe-agent',
|
|
209
|
-
serviceVersion: '1.0.0',
|
|
210
|
-
enableFile: options.traceFile !== undefined,
|
|
211
|
-
enableRemote: options.traceRemote !== undefined,
|
|
212
|
-
enableConsole: options.traceConsole,
|
|
213
|
-
filePath: options.traceFile || './traces.jsonl',
|
|
214
|
-
remoteEndpoint: options.traceRemote || 'http://localhost:4318/v1/traces',
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
config.initialize();
|
|
218
|
-
return config;
|
|
219
|
-
}
|