autotel-devtools 6.0.1 → 6.1.1
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 +19 -0
- package/dist/cli.cjs +72 -7
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +72 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +39 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +39 -7
- package/dist/index.js.map +1 -1
- package/dist/server/index.cjs +62 -7
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +17 -1
- package/dist/server/index.d.ts +17 -1
- package/dist/server/index.js +61 -8
- package/dist/server/index.js.map +1 -1
- package/dist/widget.global.js +11 -8
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -423,13 +423,16 @@ var DevtoolsServer = class {
|
|
|
423
423
|
addTraces(traces) {
|
|
424
424
|
for (const trace of traces) this.addTrace(trace);
|
|
425
425
|
}
|
|
426
|
+
// `errors` is full-state on every broadcast (the client replaces, not appends),
|
|
427
|
+
// so non-trace broadcasts must echo the current error groups rather than `[]` —
|
|
428
|
+
// otherwise a log/metric arriving after an error would wipe it from the UI.
|
|
426
429
|
addLog(log) {
|
|
427
430
|
this.logs = appendWithLimit(this.logs, log, this.limits.maxLogCount);
|
|
428
|
-
this.broadcast({ traces: [], metrics: [], logs: [log], errors:
|
|
431
|
+
this.broadcast({ traces: [], metrics: [], logs: [log], errors: this.errorAggregator.getErrorGroups() });
|
|
429
432
|
}
|
|
430
433
|
addLogs(logs) {
|
|
431
434
|
this.logs = appendManyWithLimit(this.logs, logs, this.limits.maxLogCount);
|
|
432
|
-
this.broadcast({ traces: [], metrics: [], logs, errors:
|
|
435
|
+
this.broadcast({ traces: [], metrics: [], logs, errors: this.errorAggregator.getErrorGroups() });
|
|
433
436
|
}
|
|
434
437
|
addMetric(metric) {
|
|
435
438
|
this.metrics = appendWithLimit(
|
|
@@ -437,7 +440,7 @@ var DevtoolsServer = class {
|
|
|
437
440
|
metric,
|
|
438
441
|
this.limits.maxMetricCount
|
|
439
442
|
);
|
|
440
|
-
this.broadcast({ traces: [], metrics: [metric], logs: [], errors:
|
|
443
|
+
this.broadcast({ traces: [], metrics: [metric], logs: [], errors: this.errorAggregator.getErrorGroups() });
|
|
441
444
|
}
|
|
442
445
|
getCurrentData() {
|
|
443
446
|
return {
|
|
@@ -916,7 +919,38 @@ function decodeOtlpMetricsRequest(body) {
|
|
|
916
919
|
return decodeRequest("opentelemetry.proto.metrics.v1.ExportMetricsServiceRequest", body);
|
|
917
920
|
}
|
|
918
921
|
|
|
922
|
+
// src/server/identity.ts
|
|
923
|
+
var DEVTOOLS_IDENTITY = "autotel-devtools";
|
|
924
|
+
async function probePortHolder(host, port, timeoutMs = 500) {
|
|
925
|
+
const authority = host.includes(":") ? `[${host}]` : host;
|
|
926
|
+
const controller = new AbortController();
|
|
927
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
928
|
+
try {
|
|
929
|
+
const res = await fetch(`http://${authority}:${port}/healthz`, {
|
|
930
|
+
signal: controller.signal
|
|
931
|
+
});
|
|
932
|
+
if (res.headers.get("x-autotel-devtools")) return "autotel-devtools";
|
|
933
|
+
try {
|
|
934
|
+
const body = await res.json();
|
|
935
|
+
if (body && body.service === DEVTOOLS_IDENTITY) return "autotel-devtools";
|
|
936
|
+
} catch {
|
|
937
|
+
}
|
|
938
|
+
return "foreign";
|
|
939
|
+
} catch {
|
|
940
|
+
return "none";
|
|
941
|
+
} finally {
|
|
942
|
+
clearTimeout(timer);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
919
946
|
// src/server/http.ts
|
|
947
|
+
function sendOtlpError(res, req, e) {
|
|
948
|
+
sendJson(res, 400, {
|
|
949
|
+
error: "Invalid OTLP payload",
|
|
950
|
+
message: e instanceof Error ? e.message : String(e),
|
|
951
|
+
contentType: req.headers["content-type"] ?? null
|
|
952
|
+
});
|
|
953
|
+
}
|
|
920
954
|
var PROTOBUF_DECODERS = {
|
|
921
955
|
traces: decodeOtlpTraceRequest,
|
|
922
956
|
logs: decodeOtlpLogsRequest,
|
|
@@ -937,6 +971,18 @@ function findPackageRoot() {
|
|
|
937
971
|
return dir;
|
|
938
972
|
}
|
|
939
973
|
var FULLPAGE_HTML = `<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>autotel-devtools</title><style>*{margin:0;padding:0;box-sizing:border-box}html,body{height:100%;width:100%;overflow:hidden}</style></head><body><script src="/widget.js?mode=fullpage"></script></body></html>`;
|
|
974
|
+
var cachedVersion = null;
|
|
975
|
+
function getVersion() {
|
|
976
|
+
if (cachedVersion !== null) return cachedVersion;
|
|
977
|
+
let version = "unknown";
|
|
978
|
+
try {
|
|
979
|
+
const pkg = JSON.parse(readFileSync(resolve(findPackageRoot(), "package.json"), "utf8"));
|
|
980
|
+
if (typeof pkg.version === "string") version = pkg.version;
|
|
981
|
+
} catch {
|
|
982
|
+
}
|
|
983
|
+
cachedVersion = version;
|
|
984
|
+
return version;
|
|
985
|
+
}
|
|
940
986
|
var cachedWidgetJs = null;
|
|
941
987
|
function getWidgetJs() {
|
|
942
988
|
if (!cachedWidgetJs) {
|
|
@@ -963,6 +1009,8 @@ function attachDevtoolsRoutes(httpServer, devtools) {
|
|
|
963
1009
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
964
1010
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
965
1011
|
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
1012
|
+
res.setHeader("x-autotel-devtools", getVersion());
|
|
1013
|
+
res.setHeader("Access-Control-Expose-Headers", "x-autotel-devtools");
|
|
966
1014
|
if (req.method === "OPTIONS") {
|
|
967
1015
|
res.writeHead(204);
|
|
968
1016
|
res.end();
|
|
@@ -981,7 +1029,12 @@ function attachDevtoolsRoutes(httpServer, devtools) {
|
|
|
981
1029
|
return;
|
|
982
1030
|
}
|
|
983
1031
|
if (req.method === "GET" && url === "/healthz") {
|
|
984
|
-
sendJson(res, 200, {
|
|
1032
|
+
sendJson(res, 200, {
|
|
1033
|
+
ok: true,
|
|
1034
|
+
service: DEVTOOLS_IDENTITY,
|
|
1035
|
+
version: getVersion(),
|
|
1036
|
+
clients: devtools.clientCount
|
|
1037
|
+
});
|
|
985
1038
|
return;
|
|
986
1039
|
}
|
|
987
1040
|
if (req.method === "GET" && url === "/v1/traces") {
|
|
@@ -1001,7 +1054,7 @@ function attachDevtoolsRoutes(httpServer, devtools) {
|
|
|
1001
1054
|
devtools.addTraces(traces);
|
|
1002
1055
|
sendJson(res, 200, { acceptedTraces: traces.length });
|
|
1003
1056
|
} catch (e) {
|
|
1004
|
-
|
|
1057
|
+
sendOtlpError(res, req, e);
|
|
1005
1058
|
}
|
|
1006
1059
|
return;
|
|
1007
1060
|
}
|
|
@@ -1012,7 +1065,7 @@ function attachDevtoolsRoutes(httpServer, devtools) {
|
|
|
1012
1065
|
devtools.addLogs(logs);
|
|
1013
1066
|
sendJson(res, 200, { acceptedLogs: logs.length });
|
|
1014
1067
|
} catch (e) {
|
|
1015
|
-
|
|
1068
|
+
sendOtlpError(res, req, e);
|
|
1016
1069
|
}
|
|
1017
1070
|
return;
|
|
1018
1071
|
}
|
|
@@ -1022,7 +1075,7 @@ function attachDevtoolsRoutes(httpServer, devtools) {
|
|
|
1022
1075
|
const count = countOtlpMetrics(payload);
|
|
1023
1076
|
sendJson(res, 200, { acceptedMetrics: count });
|
|
1024
1077
|
} catch (e) {
|
|
1025
|
-
|
|
1078
|
+
sendOtlpError(res, req, e);
|
|
1026
1079
|
}
|
|
1027
1080
|
return;
|
|
1028
1081
|
}
|
|
@@ -1239,6 +1292,18 @@ async function main() {
|
|
|
1239
1292
|
attachSecondary: (s) => attachDevtoolsRoutes(s, wsServer)
|
|
1240
1293
|
});
|
|
1241
1294
|
const { addresses, warnings, port: boundPort } = await listeners.ready;
|
|
1295
|
+
if (boundPort !== options.port) {
|
|
1296
|
+
const holder = await probePortHolder(options.host, options.port);
|
|
1297
|
+
if (holder === "autotel-devtools") {
|
|
1298
|
+
warnings.push(
|
|
1299
|
+
`another autotel-devtools is already running on port ${options.port}; this instance is on ${boundPort}. Use the existing one, or stop it and restart here.`
|
|
1300
|
+
);
|
|
1301
|
+
} else {
|
|
1302
|
+
warnings.push(
|
|
1303
|
+
`port ${options.port} is held by another process that is NOT autotel-devtools. Anything exporting OTLP to :${options.port} is reaching that process, not this devtools. Point your exporter at :${boundPort}, or free :${options.port} and restart.`
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1242
1307
|
const uiBase = `http://${options.host === "localhost" ? "127.0.0.1" : options.host}:${boundPort}`;
|
|
1243
1308
|
const title = options.title || "autotel-devtools";
|
|
1244
1309
|
process.stdout.write(`
|