autotel 2.25.4 → 2.26.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 +47 -1
- package/dist/auto.cjs +2 -2
- package/dist/auto.js +1 -1
- package/dist/{chunk-OPTGXEVN.js → chunk-4PTCDOZY.js} +3 -3
- package/dist/{chunk-OPTGXEVN.js.map → chunk-4PTCDOZY.js.map} +1 -1
- package/dist/{chunk-MN6PZ4AN.cjs → chunk-CMADDTHY.cjs} +7 -7
- package/dist/{chunk-MN6PZ4AN.cjs.map → chunk-CMADDTHY.cjs.map} +1 -1
- package/dist/{chunk-CMUM4JQI.js → chunk-DGPUZ6TE.js} +3 -3
- package/dist/{chunk-CMUM4JQI.js.map → chunk-DGPUZ6TE.js.map} +1 -1
- package/dist/{chunk-MNBAXRVG.js → chunk-EXOXDI5A.js} +74 -4
- package/dist/chunk-EXOXDI5A.js.map +1 -0
- package/dist/{chunk-QDREXAD7.js → chunk-GTD3NXOS.js} +3 -3
- package/dist/{chunk-QDREXAD7.js.map → chunk-GTD3NXOS.js.map} +1 -1
- package/dist/{chunk-A5ZUL2RZ.cjs → chunk-II7GFVAF.cjs} +13 -13
- package/dist/{chunk-A5ZUL2RZ.cjs.map → chunk-II7GFVAF.cjs.map} +1 -1
- package/dist/{chunk-I6JPSD4R.cjs → chunk-N344PVE5.cjs} +5 -5
- package/dist/{chunk-I6JPSD4R.cjs.map → chunk-N344PVE5.cjs.map} +1 -1
- package/dist/{chunk-WYP6OOCT.js → chunk-RXFZKLRQ.js} +3 -3
- package/dist/{chunk-WYP6OOCT.js.map → chunk-RXFZKLRQ.js.map} +1 -1
- package/dist/{chunk-EEJGUBWV.cjs → chunk-TS7IHIRW.cjs} +5 -5
- package/dist/{chunk-EEJGUBWV.cjs.map → chunk-TS7IHIRW.cjs.map} +1 -1
- package/dist/{chunk-ITYASFHQ.cjs → chunk-UJJPTSEI.cjs} +74 -3
- package/dist/chunk-UJJPTSEI.cjs.map +1 -0
- package/dist/{chunk-XB2GITM5.js → chunk-WAB4CHBU.js} +3 -3
- package/dist/{chunk-XB2GITM5.js.map → chunk-WAB4CHBU.js.map} +1 -1
- package/dist/{chunk-QQLP4M6W.cjs → chunk-ZJ5GXCOT.cjs} +26 -26
- package/dist/{chunk-QQLP4M6W.cjs.map → chunk-ZJ5GXCOT.cjs.map} +1 -1
- package/dist/decorators.cjs +2 -2
- package/dist/decorators.js +2 -2
- package/dist/event.cjs +5 -5
- package/dist/event.js +2 -2
- package/dist/functional.cjs +9 -9
- package/dist/functional.js +2 -2
- package/dist/index.cjs +42 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +10 -9
- package/dist/index.js.map +1 -1
- package/dist/{init-C_PiC_Su.d.ts → init-FiR_glVc.d.ts} +23 -0
- package/dist/{init-CIzpC5kZ.d.cts → init-QSj7X6zU.d.cts} +23 -0
- package/dist/instrumentation.cjs +8 -8
- package/dist/instrumentation.js +1 -1
- package/dist/messaging.cjs +6 -6
- package/dist/messaging.js +3 -3
- package/dist/semantic-helpers.cjs +7 -7
- package/dist/semantic-helpers.js +3 -3
- package/dist/test-span-collector.cjs.map +1 -1
- package/dist/test-span-collector.d.cts +5 -2
- package/dist/test-span-collector.d.ts +5 -2
- package/dist/test-span-collector.js.map +1 -1
- package/dist/webhook.cjs +3 -3
- package/dist/webhook.js +2 -2
- package/dist/workflow-distributed.cjs +4 -4
- package/dist/workflow-distributed.js +2 -2
- package/dist/workflow.cjs +7 -7
- package/dist/workflow.js +3 -3
- package/dist/yaml-config.d.cts +1 -1
- package/dist/yaml-config.d.ts +1 -1
- package/package.json +41 -41
- package/src/devtools.ts +60 -0
- package/src/hook.mjs +2 -2
- package/src/init.customization.test.ts +81 -0
- package/src/init.ts +71 -1
- package/src/shutdown.test.ts +35 -1
- package/src/shutdown.ts +3 -1
- package/src/test-span-collector.ts +5 -2
- package/dist/chunk-ITYASFHQ.cjs.map +0 -1
- package/dist/chunk-MNBAXRVG.js.map +0 -1
package/dist/workflow.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export { getCurrentWorkflowContext, isInWorkflow, traceStep, traceWorkflow } from './chunk-
|
|
2
|
-
import './chunk-
|
|
1
|
+
export { getCurrentWorkflowContext, isInWorkflow, traceStep, traceWorkflow } from './chunk-RXFZKLRQ.js';
|
|
2
|
+
import './chunk-4PTCDOZY.js';
|
|
3
3
|
import './chunk-B3ZHLLMP.js';
|
|
4
4
|
import './chunk-WD4RP6IV.js';
|
|
5
5
|
import './chunk-S4OFEXLA.js';
|
|
6
6
|
import './chunk-BBBWDIYQ.js';
|
|
7
|
-
import './chunk-
|
|
7
|
+
import './chunk-EXOXDI5A.js';
|
|
8
8
|
import './chunk-RUD7KS4R.js';
|
|
9
9
|
import './chunk-XDKK53OL.js';
|
|
10
10
|
import './chunk-WGWSHJ2N.js';
|
package/dist/yaml-config.d.cts
CHANGED
package/dist/yaml-config.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autotel",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.26.0",
|
|
4
4
|
"description": "Write Once, Observe Anywhere",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -249,31 +249,31 @@
|
|
|
249
249
|
"author": "Jag Reehal<jag@jagreehal.com> (https://jagreehal.com)",
|
|
250
250
|
"license": "MIT",
|
|
251
251
|
"dependencies": {
|
|
252
|
-
"@opentelemetry/api": "^1.9.
|
|
253
|
-
"@opentelemetry/api-logs": "^0.
|
|
254
|
-
"@opentelemetry/exporter-logs-otlp-http": "^0.
|
|
255
|
-
"@opentelemetry/exporter-metrics-otlp-http": "^0.
|
|
256
|
-
"@opentelemetry/exporter-trace-otlp-http": "^0.
|
|
257
|
-
"@opentelemetry/instrumentation": "^0.
|
|
258
|
-
"@opentelemetry/resources": "^2.6.
|
|
259
|
-
"@opentelemetry/sdk-logs": "^0.
|
|
260
|
-
"@opentelemetry/sdk-metrics": "^2.6.
|
|
261
|
-
"@opentelemetry/sdk-node": "^0.
|
|
262
|
-
"@opentelemetry/sdk-trace-base": "^2.6.
|
|
252
|
+
"@opentelemetry/api": "^1.9.1",
|
|
253
|
+
"@opentelemetry/api-logs": "^0.214.0",
|
|
254
|
+
"@opentelemetry/exporter-logs-otlp-http": "^0.214.0",
|
|
255
|
+
"@opentelemetry/exporter-metrics-otlp-http": "^0.214.0",
|
|
256
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.214.0",
|
|
257
|
+
"@opentelemetry/instrumentation": "^0.214.0",
|
|
258
|
+
"@opentelemetry/resources": "^2.6.1",
|
|
259
|
+
"@opentelemetry/sdk-logs": "^0.214.0",
|
|
260
|
+
"@opentelemetry/sdk-metrics": "^2.6.1",
|
|
261
|
+
"@opentelemetry/sdk-node": "^0.214.0",
|
|
262
|
+
"@opentelemetry/sdk-trace-base": "^2.6.1",
|
|
263
263
|
"@opentelemetry/semantic-conventions": "^1.40.0",
|
|
264
|
-
"
|
|
265
|
-
"
|
|
264
|
+
"import-in-the-middle": "^3.0.1",
|
|
265
|
+
"@tanstack/intent": "^0.0.29"
|
|
266
266
|
},
|
|
267
267
|
"peerDependencies": {
|
|
268
|
-
"@opentelemetry/auto-instrumentations-node": "^0.
|
|
269
|
-
"@opentelemetry/exporter-logs-otlp-grpc": "^0.
|
|
270
|
-
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.
|
|
271
|
-
"@opentelemetry/exporter-trace-otlp-grpc": "^0.
|
|
272
|
-
"@opentelemetry/resource-detector-aws": "^2.
|
|
273
|
-
"@opentelemetry/resource-detector-container": "^0.8.
|
|
274
|
-
"@opentelemetry/resource-detector-gcp": "^0.
|
|
275
|
-
"@opentelemetry/sdk-trace-node": "^2.6.
|
|
276
|
-
"@traceloop/node-server-sdk": "^0.
|
|
268
|
+
"@opentelemetry/auto-instrumentations-node": "^0.72.0",
|
|
269
|
+
"@opentelemetry/exporter-logs-otlp-grpc": "^0.214.0",
|
|
270
|
+
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.214.0",
|
|
271
|
+
"@opentelemetry/exporter-trace-otlp-grpc": "^0.214.0",
|
|
272
|
+
"@opentelemetry/resource-detector-aws": "^2.14.0",
|
|
273
|
+
"@opentelemetry/resource-detector-container": "^0.8.5",
|
|
274
|
+
"@opentelemetry/resource-detector-gcp": "^0.49.0",
|
|
275
|
+
"@opentelemetry/sdk-trace-node": "^2.6.1",
|
|
276
|
+
"@traceloop/node-server-sdk": "^0.24.0",
|
|
277
277
|
"pino": "^10.3.1",
|
|
278
278
|
"pino-pretty": "^13.1.3",
|
|
279
279
|
"yaml": "^2.8.3"
|
|
@@ -319,35 +319,35 @@
|
|
|
319
319
|
"devDependencies": {
|
|
320
320
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
321
321
|
"@edge-runtime/vm": "^5.0.0",
|
|
322
|
-
"@opentelemetry/auto-instrumentations-node": "^0.
|
|
323
|
-
"@opentelemetry/context-async-hooks": "^2.6.
|
|
324
|
-
"@opentelemetry/exporter-logs-otlp-grpc": "^0.
|
|
325
|
-
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.
|
|
326
|
-
"@opentelemetry/exporter-trace-otlp-grpc": "^0.
|
|
327
|
-
"@opentelemetry/resource-detector-aws": "^2.
|
|
328
|
-
"@opentelemetry/resource-detector-container": "^0.8.
|
|
329
|
-
"@opentelemetry/resource-detector-gcp": "^0.
|
|
330
|
-
"@opentelemetry/sdk-trace-node": "^2.6.
|
|
331
|
-
"@swc/core": "^1.15.
|
|
322
|
+
"@opentelemetry/auto-instrumentations-node": "^0.72.0",
|
|
323
|
+
"@opentelemetry/context-async-hooks": "^2.6.1",
|
|
324
|
+
"@opentelemetry/exporter-logs-otlp-grpc": "^0.214.0",
|
|
325
|
+
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.214.0",
|
|
326
|
+
"@opentelemetry/exporter-trace-otlp-grpc": "^0.214.0",
|
|
327
|
+
"@opentelemetry/resource-detector-aws": "^2.14.0",
|
|
328
|
+
"@opentelemetry/resource-detector-container": "^0.8.5",
|
|
329
|
+
"@opentelemetry/resource-detector-gcp": "^0.49.0",
|
|
330
|
+
"@opentelemetry/sdk-trace-node": "^2.6.1",
|
|
331
|
+
"@swc/core": "^1.15.24",
|
|
332
332
|
"@total-typescript/ts-reset": "^0.6.1",
|
|
333
333
|
"@total-typescript/tsconfig": "^1.0.4",
|
|
334
334
|
"@types/eslint-config-prettier": "^6.11.3",
|
|
335
|
-
"@types/node": "^25.5.
|
|
336
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
337
|
-
"@typescript-eslint/parser": "^8.
|
|
335
|
+
"@types/node": "^25.5.2",
|
|
336
|
+
"@typescript-eslint/eslint-plugin": "^8.58.1",
|
|
337
|
+
"@typescript-eslint/parser": "^8.58.1",
|
|
338
338
|
"eslint-config-prettier": "^10.1.8",
|
|
339
|
-
"eslint-plugin-unicorn": "^
|
|
339
|
+
"eslint-plugin-unicorn": "^64.0.0",
|
|
340
340
|
"pino": "^10.3.1",
|
|
341
341
|
"prettier": "^3.8.1",
|
|
342
342
|
"rimraf": "^6.1.3",
|
|
343
343
|
"tsup": "^8.5.1",
|
|
344
344
|
"tsx": "^4.21.0",
|
|
345
|
-
"typescript": "^
|
|
346
|
-
"typescript-eslint": "^8.
|
|
345
|
+
"typescript": "^6.0.2",
|
|
346
|
+
"typescript-eslint": "^8.58.1",
|
|
347
347
|
"unplugin-swc": "^1.5.9",
|
|
348
348
|
"vite-tsconfig-paths": "^6.1.1",
|
|
349
|
-
"vitest": "^4.1.
|
|
350
|
-
"vitest-mock-extended": "^
|
|
349
|
+
"vitest": "^4.1.3",
|
|
350
|
+
"vitest-mock-extended": "^4.0.0",
|
|
351
351
|
"winston": "^3.19.0",
|
|
352
352
|
"yaml": "^2.8.3"
|
|
353
353
|
},
|
package/src/devtools.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export interface AutotelDevtoolsConfig {
|
|
2
|
+
enabled?: boolean;
|
|
3
|
+
endpoint?: string;
|
|
4
|
+
embedded?: boolean;
|
|
5
|
+
host?: string;
|
|
6
|
+
port?: number;
|
|
7
|
+
verbose?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ResolvedAutotelDevtoolsConfig {
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
endpoint?: string;
|
|
13
|
+
embedded: boolean;
|
|
14
|
+
host: string;
|
|
15
|
+
port: number;
|
|
16
|
+
verbose: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const defaultHost = '127.0.0.1';
|
|
20
|
+
const defaultPort = 4318;
|
|
21
|
+
|
|
22
|
+
export function resolveDevtoolsConfig(
|
|
23
|
+
config: boolean | AutotelDevtoolsConfig | undefined,
|
|
24
|
+
): ResolvedAutotelDevtoolsConfig {
|
|
25
|
+
if (!config) {
|
|
26
|
+
return {
|
|
27
|
+
enabled: false,
|
|
28
|
+
endpoint: undefined,
|
|
29
|
+
embedded: false,
|
|
30
|
+
host: defaultHost,
|
|
31
|
+
port: defaultPort,
|
|
32
|
+
verbose: false,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (config === true) {
|
|
37
|
+
return {
|
|
38
|
+
enabled: true,
|
|
39
|
+
endpoint: `http://${defaultHost}:${defaultPort}`,
|
|
40
|
+
embedded: false,
|
|
41
|
+
host: defaultHost,
|
|
42
|
+
port: defaultPort,
|
|
43
|
+
verbose: false,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const enabled = config.enabled ?? true;
|
|
48
|
+
const host = config.host ?? defaultHost;
|
|
49
|
+
const port = config.port ?? defaultPort;
|
|
50
|
+
const endpoint = config.endpoint ?? `http://${host}:${port}`;
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
enabled,
|
|
54
|
+
endpoint: enabled ? endpoint : undefined,
|
|
55
|
+
embedded: enabled && (config.embedded ?? false),
|
|
56
|
+
host,
|
|
57
|
+
port,
|
|
58
|
+
verbose: config.verbose ?? false,
|
|
59
|
+
};
|
|
60
|
+
}
|
package/src/hook.mjs
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* This file re-exports the OpenTelemetry ESM loader hook so users don't need
|
|
5
5
|
* to install @opentelemetry/instrumentation as a direct dependency.
|
|
6
6
|
*
|
|
7
|
-
* Usage (Node
|
|
7
|
+
* Usage (Node 22+):
|
|
8
8
|
* NODE_OPTIONS="--experimental-loader=autotel/hook.mjs --import ./instrumentation.ts" tsx src/index.ts
|
|
9
9
|
*
|
|
10
|
-
* For Node
|
|
10
|
+
* For supported Node versions, prefer using autotel/register instead which uses the newer
|
|
11
11
|
* module.register() API and doesn't require NODE_OPTIONS.
|
|
12
12
|
*
|
|
13
13
|
* @see https://github.com/open-telemetry/opentelemetry-js/blob/main/doc/esm-support.md
|
|
@@ -115,6 +115,9 @@ async function loadInitWithMocks() {
|
|
|
115
115
|
getConfig: mod.getConfig,
|
|
116
116
|
getDefaultSampler: mod.getDefaultSampler,
|
|
117
117
|
resolveLogsFlag: mod.resolveLogsFlag,
|
|
118
|
+
setOptionalRequireForTesting: mod._setOptionalRequireForTesting,
|
|
119
|
+
resetOptionalRequireForTesting: mod._resetOptionalRequireForTesting,
|
|
120
|
+
getEmbeddedDevtoolsCloseForTesting: mod._getEmbeddedDevtoolsCloseForTesting,
|
|
118
121
|
sdkInstances,
|
|
119
122
|
traceExporterOptions,
|
|
120
123
|
metricExporterOptions,
|
|
@@ -135,6 +138,84 @@ describe('init() customization', () => {
|
|
|
135
138
|
delete process.env.NODE_ENV;
|
|
136
139
|
});
|
|
137
140
|
|
|
141
|
+
it('auto-configures local devtools endpoint and logs when devtools is enabled', async () => {
|
|
142
|
+
const {
|
|
143
|
+
init,
|
|
144
|
+
sdkInstances,
|
|
145
|
+
traceExporterOptions,
|
|
146
|
+
metricExporterOptions,
|
|
147
|
+
logExporterOptions,
|
|
148
|
+
} = await loadInitWithMocks();
|
|
149
|
+
|
|
150
|
+
init({ service: 'devtools-app', devtools: true });
|
|
151
|
+
|
|
152
|
+
expect(traceExporterOptions[0]).toMatchObject({
|
|
153
|
+
url: 'http://127.0.0.1:4318/v1/traces',
|
|
154
|
+
});
|
|
155
|
+
expect(metricExporterOptions[0]).toMatchObject({
|
|
156
|
+
url: 'http://127.0.0.1:4318/v1/metrics',
|
|
157
|
+
});
|
|
158
|
+
expect(logExporterOptions[0]).toMatchObject({
|
|
159
|
+
url: 'http://127.0.0.1:4318/v1/logs',
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const options = sdkInstances.at(-1)?.options as Record<string, unknown>;
|
|
163
|
+
expect(options.logRecordProcessors).toBeDefined();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('starts embedded autotel-devtools when requested and installed', async () => {
|
|
167
|
+
const {
|
|
168
|
+
init,
|
|
169
|
+
setOptionalRequireForTesting,
|
|
170
|
+
getEmbeddedDevtoolsCloseForTesting,
|
|
171
|
+
traceExporterOptions,
|
|
172
|
+
logExporterOptions,
|
|
173
|
+
} = await loadInitWithMocks();
|
|
174
|
+
|
|
175
|
+
const close = vi.fn();
|
|
176
|
+
|
|
177
|
+
setOptionalRequireForTesting((id: string) => {
|
|
178
|
+
if (id === 'autotel-devtools') {
|
|
179
|
+
return {
|
|
180
|
+
createDevtools: () => ({
|
|
181
|
+
port: 9876,
|
|
182
|
+
close,
|
|
183
|
+
}),
|
|
184
|
+
} as any;
|
|
185
|
+
}
|
|
186
|
+
return undefined;
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
init({
|
|
190
|
+
service: 'embedded-devtools-app',
|
|
191
|
+
devtools: { embedded: true, host: '127.0.0.1', port: 0 },
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
expect(traceExporterOptions[0]).toMatchObject({
|
|
195
|
+
url: 'http://127.0.0.1:9876/v1/traces',
|
|
196
|
+
});
|
|
197
|
+
expect(logExporterOptions[0]).toMatchObject({
|
|
198
|
+
url: 'http://127.0.0.1:9876/v1/logs',
|
|
199
|
+
});
|
|
200
|
+
expect(getEmbeddedDevtoolsCloseForTesting()).toBe(close);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('falls back cleanly when embedded devtools is requested but unavailable', async () => {
|
|
204
|
+
const { init, setOptionalRequireForTesting, traceExporterOptions } =
|
|
205
|
+
await loadInitWithMocks();
|
|
206
|
+
|
|
207
|
+
setOptionalRequireForTesting(() => undefined);
|
|
208
|
+
|
|
209
|
+
init({
|
|
210
|
+
service: 'embedded-devtools-fallback-app',
|
|
211
|
+
devtools: { embedded: true },
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
expect(traceExporterOptions[0]).toMatchObject({
|
|
215
|
+
url: 'http://127.0.0.1:4318/v1/traces',
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
138
219
|
it(
|
|
139
220
|
'passes custom instrumentations to the NodeSDK',
|
|
140
221
|
{ timeout: 10_000 },
|
package/src/init.ts
CHANGED
|
@@ -73,6 +73,7 @@ import {
|
|
|
73
73
|
type CanonicalLogLineOptions,
|
|
74
74
|
} from './processors/canonical-log-line-processor';
|
|
75
75
|
import type { EventsConfig } from './events-config';
|
|
76
|
+
import { resolveDevtoolsConfig, type AutotelDevtoolsConfig } from './devtools';
|
|
76
77
|
|
|
77
78
|
/**
|
|
78
79
|
* Silent logger (no-op) - used as default when user doesn't provide one.
|
|
@@ -297,6 +298,21 @@ export interface AutotelConfig {
|
|
|
297
298
|
/** Service name (required) */
|
|
298
299
|
service: string;
|
|
299
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Local developer UX for autotel-devtools.
|
|
303
|
+
*
|
|
304
|
+
* - `true`: send traces, metrics, and logs to `http://127.0.0.1:4318`
|
|
305
|
+
* - `{ embedded: true }`: attempt to start `autotel-devtools` automatically
|
|
306
|
+
*
|
|
307
|
+
* When enabled:
|
|
308
|
+
* - `endpoint` defaults to the local devtools URL
|
|
309
|
+
* - `logs` default to `true` unless explicitly set
|
|
310
|
+
*
|
|
311
|
+
* This keeps production config unchanged while making local debugging
|
|
312
|
+
* effectively zero-config.
|
|
313
|
+
*/
|
|
314
|
+
devtools?: boolean | AutotelDevtoolsConfig;
|
|
315
|
+
|
|
300
316
|
/** Event subscribers - bring your own (PostHog, Mixpanel, etc.) */
|
|
301
317
|
subscribers?: EventSubscriber[];
|
|
302
318
|
|
|
@@ -1138,6 +1154,7 @@ let validationConfig: Partial<ValidationConfig> | null = null;
|
|
|
1138
1154
|
let eventsConfig: EventsConfig | null = null;
|
|
1139
1155
|
let _stringRedactor: StringRedactor | null = null;
|
|
1140
1156
|
let _optionalRequire: typeof safeRequire = safeRequire;
|
|
1157
|
+
let _devtoolsClose: (() => Promise<void> | void) | null = null;
|
|
1141
1158
|
|
|
1142
1159
|
/**
|
|
1143
1160
|
* Resolve metrics flag with env var override support
|
|
@@ -1297,6 +1314,11 @@ export function init(cfg: AutotelConfig): void {
|
|
|
1297
1314
|
headers: cfg.headers ?? yamlConfig.headers ?? envConfig.headers,
|
|
1298
1315
|
} as AutotelConfig;
|
|
1299
1316
|
|
|
1317
|
+
const devtoolsConfig = resolveDevtoolsConfig(mergedConfig.devtools);
|
|
1318
|
+
if (devtoolsConfig.enabled && mergedConfig.logs === undefined) {
|
|
1319
|
+
mergedConfig.logs = true;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1300
1322
|
// Set logger (use provided or default to silent - no spam)
|
|
1301
1323
|
logger = mergedConfig.logger || silentLogger;
|
|
1302
1324
|
|
|
@@ -1314,7 +1336,7 @@ export function init(cfg: AutotelConfig): void {
|
|
|
1314
1336
|
|
|
1315
1337
|
// Initialize OpenTelemetry
|
|
1316
1338
|
// Only use endpoint if explicitly configured (no default fallback)
|
|
1317
|
-
|
|
1339
|
+
let endpoint = mergedConfig.endpoint ?? devtoolsConfig.endpoint;
|
|
1318
1340
|
const otlpHeaders = normalizeOtlpHeaders(mergedConfig.headers);
|
|
1319
1341
|
const version = mergedConfig.version || detectVersion();
|
|
1320
1342
|
const environment =
|
|
@@ -1322,6 +1344,35 @@ export function init(cfg: AutotelConfig): void {
|
|
|
1322
1344
|
const metricsEnabled = resolveMetricsFlag(mergedConfig.metrics);
|
|
1323
1345
|
const logsEnabled = resolveLogsFlag(mergedConfig.logs);
|
|
1324
1346
|
|
|
1347
|
+
if (devtoolsConfig.enabled && devtoolsConfig.embedded) {
|
|
1348
|
+
const devtoolsModule = _optionalRequire<{
|
|
1349
|
+
createDevtools?: (options?: {
|
|
1350
|
+
port?: number;
|
|
1351
|
+
host?: string;
|
|
1352
|
+
verbose?: boolean;
|
|
1353
|
+
}) => { port: number; close: () => Promise<void> | void };
|
|
1354
|
+
}>('autotel-devtools');
|
|
1355
|
+
|
|
1356
|
+
if (devtoolsModule?.createDevtools) {
|
|
1357
|
+
const devtoolsInstance = devtoolsModule.createDevtools({
|
|
1358
|
+
port: devtoolsConfig.port,
|
|
1359
|
+
host: devtoolsConfig.host,
|
|
1360
|
+
verbose: devtoolsConfig.verbose,
|
|
1361
|
+
});
|
|
1362
|
+
_devtoolsClose = devtoolsInstance.close;
|
|
1363
|
+
endpoint = `http://${devtoolsConfig.host}:${devtoolsInstance.port}`;
|
|
1364
|
+
logger.info(
|
|
1365
|
+
{},
|
|
1366
|
+
`[autotel] autotel-devtools embedded server started at ${endpoint}`,
|
|
1367
|
+
);
|
|
1368
|
+
} else {
|
|
1369
|
+
logger.warn(
|
|
1370
|
+
{},
|
|
1371
|
+
'[autotel] devtools.embedded requested but autotel-devtools is not installed. Falling back to endpoint-only mode.',
|
|
1372
|
+
);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1325
1376
|
// Detect hostname for proper Datadog correlation and Service Catalog discovery
|
|
1326
1377
|
const hostname = detectHostname();
|
|
1327
1378
|
|
|
@@ -2013,6 +2064,25 @@ export function _resetOptionalRequireForTesting(): void {
|
|
|
2013
2064
|
_optionalRequire = safeRequire;
|
|
2014
2065
|
}
|
|
2015
2066
|
|
|
2067
|
+
/**
|
|
2068
|
+
* @internal Close embedded devtools if running.
|
|
2069
|
+
*/
|
|
2070
|
+
export async function _closeEmbeddedDevtools(): Promise<void> {
|
|
2071
|
+
if (_devtoolsClose) {
|
|
2072
|
+
await _devtoolsClose();
|
|
2073
|
+
_devtoolsClose = null;
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
/**
|
|
2078
|
+
* @internal Get embedded devtools close handle.
|
|
2079
|
+
*/
|
|
2080
|
+
export function _getEmbeddedDevtoolsCloseForTesting():
|
|
2081
|
+
| (() => Promise<void> | void)
|
|
2082
|
+
| null {
|
|
2083
|
+
return _devtoolsClose;
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2016
2086
|
/**
|
|
2017
2087
|
* Get SDK instance (for shutdown)
|
|
2018
2088
|
*/
|
package/src/shutdown.test.ts
CHANGED
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
6
6
|
import { flush, shutdown } from './shutdown';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
init,
|
|
9
|
+
_getEmbeddedDevtoolsCloseForTesting,
|
|
10
|
+
_resetOptionalRequireForTesting,
|
|
11
|
+
_setOptionalRequireForTesting,
|
|
12
|
+
} from './init';
|
|
8
13
|
import { track, getEventQueue } from './track';
|
|
9
14
|
import { EventSubscriber } from './event-subscriber';
|
|
10
15
|
|
|
@@ -37,6 +42,7 @@ describe('shutdown module', () => {
|
|
|
37
42
|
beforeEach(() => {
|
|
38
43
|
vi.clearAllMocks();
|
|
39
44
|
mockAdapter = new MockAdapter();
|
|
45
|
+
_resetOptionalRequireForTesting();
|
|
40
46
|
});
|
|
41
47
|
|
|
42
48
|
afterEach(async () => {
|
|
@@ -45,6 +51,7 @@ describe('shutdown module', () => {
|
|
|
45
51
|
if (queue) {
|
|
46
52
|
await queue.flush();
|
|
47
53
|
}
|
|
54
|
+
_resetOptionalRequireForTesting();
|
|
48
55
|
});
|
|
49
56
|
|
|
50
57
|
describe('flush()', () => {
|
|
@@ -288,6 +295,33 @@ describe('shutdown module', () => {
|
|
|
288
295
|
// Should not throw even if adapter shutdown fails
|
|
289
296
|
await expect(shutdown()).resolves.toBeUndefined();
|
|
290
297
|
});
|
|
298
|
+
|
|
299
|
+
it('should close embedded devtools during shutdown', async () => {
|
|
300
|
+
const close = vi.fn().mockResolvedValue(undefined);
|
|
301
|
+
_setOptionalRequireForTesting((id: string) => {
|
|
302
|
+
if (id === 'autotel-devtools') {
|
|
303
|
+
return {
|
|
304
|
+
createDevtools: () => ({
|
|
305
|
+
port: 4318,
|
|
306
|
+
close,
|
|
307
|
+
}),
|
|
308
|
+
} as any;
|
|
309
|
+
}
|
|
310
|
+
return undefined;
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
init({
|
|
314
|
+
service: 'test-service',
|
|
315
|
+
devtools: { embedded: true },
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
expect(_getEmbeddedDevtoolsCloseForTesting()).not.toBeNull();
|
|
319
|
+
|
|
320
|
+
await shutdown();
|
|
321
|
+
|
|
322
|
+
expect(close).toHaveBeenCalledOnce();
|
|
323
|
+
expect(_getEmbeddedDevtoolsCloseForTesting()).toBeNull();
|
|
324
|
+
});
|
|
291
325
|
});
|
|
292
326
|
|
|
293
327
|
describe('Integration', () => {
|
package/src/shutdown.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Graceful shutdown with flush and cleanup
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { getSdk, getLogger } from './init';
|
|
5
|
+
import { getSdk, getLogger, _closeEmbeddedDevtools } from './init';
|
|
6
6
|
import { getEventQueue, resetEventQueue } from './track';
|
|
7
7
|
import { resetEvents } from './event';
|
|
8
8
|
import { resetMetrics } from './metric';
|
|
@@ -179,6 +179,8 @@ export async function shutdown(): Promise<void> {
|
|
|
179
179
|
logger.error({ err }, '[autotel] SDK shutdown failed');
|
|
180
180
|
}
|
|
181
181
|
} finally {
|
|
182
|
+
await _closeEmbeddedDevtools();
|
|
183
|
+
|
|
182
184
|
// Clean up singleton Maps and queues to prevent memory leaks
|
|
183
185
|
// This runs even if SDK shutdown fails
|
|
184
186
|
const eventsQueue = getEventQueue();
|
|
@@ -36,8 +36,11 @@ type SerializableValue =
|
|
|
36
36
|
/**
|
|
37
37
|
* Portable serialized span for embedding in test metadata.
|
|
38
38
|
* `startTimeMs` is derived from OTel HrTime — epoch-based wall-clock ms in the current SDK.
|
|
39
|
+
*
|
|
40
|
+
* Defined as a `type` (not `interface`) so it is assignable to
|
|
41
|
+
* `Record<string, unknown>` in TypeScript 6+ strict mode.
|
|
39
42
|
*/
|
|
40
|
-
export
|
|
43
|
+
export type SerializedSpan = {
|
|
41
44
|
spanId: string;
|
|
42
45
|
parentSpanId?: string;
|
|
43
46
|
name: string;
|
|
@@ -46,7 +49,7 @@ export interface SerializedSpan {
|
|
|
46
49
|
status: 'ok' | 'error' | 'unset';
|
|
47
50
|
statusMessage?: string;
|
|
48
51
|
attributes?: Record<string, SerializableValue>;
|
|
49
|
-
}
|
|
52
|
+
};
|
|
50
53
|
|
|
51
54
|
export class TestSpanCollector implements SpanExporter {
|
|
52
55
|
private traces = new Map<string, ReadableSpan[]>();
|