@autter/runtime-node 1.3.2 → 1.3.3
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 +6 -0
- package/dist/index.cjs +90 -43
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +48 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @autter/runtime-node
|
|
2
2
|
|
|
3
|
+
The server tracker exports per-instance RSS, heap, memory limit, and GC
|
|
4
|
+
metrics by default. See [memory pressure incidents](../../docs/MEMORY-PRESSURE.md)
|
|
5
|
+
for ECS/Kubernetes OOM event forwarding and optional heap profiles. Set
|
|
6
|
+
`memoryMetrics: false` only if another exporter already emits equivalent
|
|
7
|
+
process metrics.
|
|
8
|
+
|
|
3
9
|
Autter Runtime for Node.js — two halves in one package:
|
|
4
10
|
|
|
5
11
|
1. **Same-origin browser relay** for `@autter/runtime-browser`
|
package/dist/index.cjs
CHANGED
|
@@ -255,7 +255,7 @@ function createBrowserRelayHandler(opts) {
|
|
|
255
255
|
// src/server.ts
|
|
256
256
|
var import_node_http = require("http");
|
|
257
257
|
var import_node_crypto = require("crypto");
|
|
258
|
-
var
|
|
258
|
+
var import_api2 = require("@opentelemetry/api");
|
|
259
259
|
var import_core2 = require("@opentelemetry/core");
|
|
260
260
|
var import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
|
|
261
261
|
var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
|
|
@@ -707,6 +707,51 @@ function makeRedactor(options) {
|
|
|
707
707
|
return (attributes) => redactWith(attributes, r);
|
|
708
708
|
}
|
|
709
709
|
|
|
710
|
+
// src/memory.ts
|
|
711
|
+
var import_node_perf_hooks = require("perf_hooks");
|
|
712
|
+
var import_node_v8 = require("v8");
|
|
713
|
+
var import_api = require("@opentelemetry/api");
|
|
714
|
+
function startMemoryMetrics() {
|
|
715
|
+
const meter = import_api.metrics.getMeter("autter-process-memory");
|
|
716
|
+
const rss = meter.createObservableGauge("autter.process.memory.rss", { unit: "By" });
|
|
717
|
+
const heapUsed = meter.createObservableGauge("autter.process.memory.heap.used", { unit: "By" });
|
|
718
|
+
const heapLimit = meter.createObservableGauge("autter.process.memory.heap.limit", { unit: "By" });
|
|
719
|
+
const containerLimit = meter.createObservableGauge("autter.process.memory.limit", { unit: "By" });
|
|
720
|
+
const afterGc = meter.createObservableGauge("autter.process.memory.heap.after_gc", { unit: "By" });
|
|
721
|
+
const gcCount = meter.createObservableCounter("autter.process.gc.count", { unit: "{collection}" });
|
|
722
|
+
const gcDuration = meter.createObservableCounter("autter.process.gc.duration", { unit: "ms" });
|
|
723
|
+
let collections = 0;
|
|
724
|
+
let durationMs = 0;
|
|
725
|
+
let lastAfterGc = null;
|
|
726
|
+
const callbacks = [
|
|
727
|
+
[rss, (result) => result.observe(process.memoryUsage().rss)],
|
|
728
|
+
[heapUsed, (result) => result.observe(process.memoryUsage().heapUsed)],
|
|
729
|
+
[heapLimit, (result) => result.observe((0, import_node_v8.getHeapStatistics)().heap_size_limit)],
|
|
730
|
+
[containerLimit, (result) => {
|
|
731
|
+
const constrained = process.constrainedMemory?.() ?? 0;
|
|
732
|
+
if (constrained > 0) result.observe(constrained);
|
|
733
|
+
}],
|
|
734
|
+
[afterGc, (result) => {
|
|
735
|
+
if (lastAfterGc !== null) result.observe(lastAfterGc);
|
|
736
|
+
}],
|
|
737
|
+
[gcCount, (result) => result.observe(collections)],
|
|
738
|
+
[gcDuration, (result) => result.observe(durationMs)]
|
|
739
|
+
];
|
|
740
|
+
for (const [instrument, callback] of callbacks) instrument.addCallback(callback);
|
|
741
|
+
const observer = new import_node_perf_hooks.PerformanceObserver((list) => {
|
|
742
|
+
for (const entry of list.getEntries()) {
|
|
743
|
+
collections++;
|
|
744
|
+
durationMs += entry.duration;
|
|
745
|
+
lastAfterGc = process.memoryUsage().heapUsed;
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
observer.observe({ entryTypes: ["gc"] });
|
|
749
|
+
return () => {
|
|
750
|
+
observer.disconnect();
|
|
751
|
+
for (const [instrument, callback] of callbacks) instrument.removeCallback(callback);
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
|
|
710
755
|
// src/server.ts
|
|
711
756
|
var LLM_MARKER_ATTRIBUTES = [
|
|
712
757
|
"gen_ai.operation.name",
|
|
@@ -769,14 +814,14 @@ var RETENTION_MAX_BUFFERED_SPANS = 5e3;
|
|
|
769
814
|
var RETENTION_SWEEP_INTERVAL_MS = 1e4;
|
|
770
815
|
var RETENTION_TRACE_TTL_MS = 3e4;
|
|
771
816
|
function spanIndicatesError(span) {
|
|
772
|
-
if (span.status.code ===
|
|
817
|
+
if (span.status.code === import_api2.SpanStatusCode.ERROR) return true;
|
|
773
818
|
return span.events.some((event) => event.name === "exception");
|
|
774
819
|
}
|
|
775
820
|
var TAIL_RETAINED_ATTR = "autter.tail_retained";
|
|
776
821
|
function withSampledFlag(span) {
|
|
777
822
|
const spanContext = {
|
|
778
823
|
...span.spanContext(),
|
|
779
|
-
traceFlags: span.spanContext().traceFlags |
|
|
824
|
+
traceFlags: span.spanContext().traceFlags | import_api2.TraceFlags.SAMPLED
|
|
780
825
|
};
|
|
781
826
|
return {
|
|
782
827
|
name: span.name,
|
|
@@ -816,11 +861,11 @@ var ErrorTraceRetentionProcessor = class {
|
|
|
816
861
|
}
|
|
817
862
|
onEnd(span) {
|
|
818
863
|
const ctx = span.spanContext();
|
|
819
|
-
if ((ctx.traceFlags &
|
|
864
|
+
if ((ctx.traceFlags & import_api2.TraceFlags.SAMPLED) !== 0) return;
|
|
820
865
|
const entry = this.entryFor(ctx.traceId);
|
|
821
866
|
if (entry.retained) {
|
|
822
867
|
this.forward(span);
|
|
823
|
-
} else if (this.retainErrors && spanIndicatesError(span) || this.slowThresholdMs > 0 && span.kind ===
|
|
868
|
+
} else if (this.retainErrors && spanIndicatesError(span) || this.slowThresholdMs > 0 && span.kind === import_api2.SpanKind.SERVER && span.duration[0] * 1e3 + span.duration[1] / 1e6 >= this.slowThresholdMs) {
|
|
824
869
|
entry.retained = true;
|
|
825
870
|
this.flush(entry);
|
|
826
871
|
this.forward(span);
|
|
@@ -835,8 +880,8 @@ var ErrorTraceRetentionProcessor = class {
|
|
|
835
880
|
}
|
|
836
881
|
/** Promote the active trace, if any — called on captured errors. */
|
|
837
882
|
retainActiveTrace() {
|
|
838
|
-
const active2 =
|
|
839
|
-
if (!active2 || (active2.traceFlags &
|
|
883
|
+
const active2 = import_api2.trace.getActiveSpan()?.spanContext();
|
|
884
|
+
if (!active2 || (active2.traceFlags & import_api2.TraceFlags.SAMPLED) !== 0) return;
|
|
840
885
|
const entry = this.entryFor(active2.traceId);
|
|
841
886
|
if (entry.retained) return;
|
|
842
887
|
entry.retained = true;
|
|
@@ -925,7 +970,7 @@ function usageAttributes(usage) {
|
|
|
925
970
|
}
|
|
926
971
|
async function runLlmSpan(tracer, info, fn) {
|
|
927
972
|
const span = tracer.startSpan(`${info.operation ?? "chat"} ${info.model}`, {
|
|
928
|
-
kind:
|
|
973
|
+
kind: import_api2.SpanKind.CLIENT,
|
|
929
974
|
attributes: llmBaseAttributes(info)
|
|
930
975
|
});
|
|
931
976
|
const setResponseModel = (model) => span.setAttribute("gen_ai.response.model", model);
|
|
@@ -937,11 +982,11 @@ async function runLlmSpan(tracer, info, fn) {
|
|
|
937
982
|
setAttributes: (attributes) => span.setAttributes(attributes)
|
|
938
983
|
};
|
|
939
984
|
try {
|
|
940
|
-
const result = await
|
|
941
|
-
|
|
985
|
+
const result = await import_api2.context.with(
|
|
986
|
+
import_api2.trace.setSpan(import_api2.context.active(), span),
|
|
942
987
|
() => fn(handle)
|
|
943
988
|
);
|
|
944
|
-
span.setStatus({ code:
|
|
989
|
+
span.setStatus({ code: import_api2.SpanStatusCode.OK });
|
|
945
990
|
return result;
|
|
946
991
|
} catch (err) {
|
|
947
992
|
if (err instanceof Error) {
|
|
@@ -949,7 +994,7 @@ async function runLlmSpan(tracer, info, fn) {
|
|
|
949
994
|
span.setAttribute("error.type", err.name);
|
|
950
995
|
}
|
|
951
996
|
span.setStatus({
|
|
952
|
-
code:
|
|
997
|
+
code: import_api2.SpanStatusCode.ERROR,
|
|
953
998
|
message: err instanceof Error ? err.message : String(err)
|
|
954
999
|
});
|
|
955
1000
|
throw err;
|
|
@@ -964,7 +1009,7 @@ function recordLlmCall(tracer, call) {
|
|
|
964
1009
|
const span = tracer.startSpan(
|
|
965
1010
|
`${call.operation ?? "chat"} ${call.model}`,
|
|
966
1011
|
{
|
|
967
|
-
kind:
|
|
1012
|
+
kind: import_api2.SpanKind.CLIENT,
|
|
968
1013
|
startTime,
|
|
969
1014
|
attributes: { ...llmBaseAttributes(call), ...usageAttributes(call) }
|
|
970
1015
|
}
|
|
@@ -975,27 +1020,27 @@ function recordLlmCall(tracer, call) {
|
|
|
975
1020
|
span.setAttribute("error.type", call.error.name);
|
|
976
1021
|
}
|
|
977
1022
|
span.setStatus({
|
|
978
|
-
code:
|
|
1023
|
+
code: import_api2.SpanStatusCode.ERROR,
|
|
979
1024
|
message: call.error instanceof Error ? call.error.message : String(call.error)
|
|
980
1025
|
});
|
|
981
1026
|
} else {
|
|
982
|
-
span.setStatus({ code:
|
|
1027
|
+
span.setStatus({ code: import_api2.SpanStatusCode.OK });
|
|
983
1028
|
}
|
|
984
1029
|
span.end(endTime);
|
|
985
1030
|
}
|
|
986
1031
|
async function runWithSpan(tracer, name, fn, attributes) {
|
|
987
1032
|
const span = tracer.startSpan(name, { attributes });
|
|
988
1033
|
try {
|
|
989
|
-
const result = await
|
|
990
|
-
|
|
1034
|
+
const result = await import_api2.context.with(
|
|
1035
|
+
import_api2.trace.setSpan(import_api2.context.active(), span),
|
|
991
1036
|
fn
|
|
992
1037
|
);
|
|
993
|
-
span.setStatus({ code:
|
|
1038
|
+
span.setStatus({ code: import_api2.SpanStatusCode.OK });
|
|
994
1039
|
return result;
|
|
995
1040
|
} catch (err) {
|
|
996
1041
|
if (err instanceof Error) span.recordException(err);
|
|
997
1042
|
span.setStatus({
|
|
998
|
-
code:
|
|
1043
|
+
code: import_api2.SpanStatusCode.ERROR,
|
|
999
1044
|
message: err instanceof Error ? err.message : String(err)
|
|
1000
1045
|
});
|
|
1001
1046
|
throw err;
|
|
@@ -1013,7 +1058,7 @@ function expressRouteOf(req) {
|
|
|
1013
1058
|
}
|
|
1014
1059
|
function captureExpressRoute(_span, response) {
|
|
1015
1060
|
if (!(response instanceof import_node_http.ServerResponse)) return;
|
|
1016
|
-
const rpcMetadata = (0, import_core2.getRPCMetadata)(
|
|
1061
|
+
const rpcMetadata = (0, import_core2.getRPCMetadata)(import_api2.context.active());
|
|
1017
1062
|
if (rpcMetadata?.type !== import_core2.RPCType.HTTP) return;
|
|
1018
1063
|
const setRoute = () => {
|
|
1019
1064
|
if (rpcMetadata.route) return;
|
|
@@ -1039,7 +1084,7 @@ function initAutterServer(options) {
|
|
|
1039
1084
|
activeRedactor = makeRedactor(options.redactAttributes ?? true);
|
|
1040
1085
|
const resource = new import_resources.Resource({
|
|
1041
1086
|
[import_semantic_conventions.ATTR_SERVICE_NAME]: options.service,
|
|
1042
|
-
"service.instance.id": (0, import_node_crypto.randomUUID)(),
|
|
1087
|
+
"service.instance.id": (options.instanceId || process.env.AUTTER_RUNTIME_INSTANCE_ID || (0, import_node_crypto.randomUUID)()).slice(0, 128),
|
|
1043
1088
|
...options.release ? { [import_semantic_conventions.ATTR_SERVICE_VERSION]: options.release } : {},
|
|
1044
1089
|
"deployment.environment": environment,
|
|
1045
1090
|
// Tells the ingester request metrics arrive on the metrics pipe, so
|
|
@@ -1104,6 +1149,7 @@ function initAutterServer(options) {
|
|
|
1104
1149
|
]
|
|
1105
1150
|
});
|
|
1106
1151
|
sdk.start();
|
|
1152
|
+
const stopMemoryMetrics = options.memoryMetrics === false ? null : startMemoryMetrics();
|
|
1107
1153
|
const alwaysOnProvider = new import_sdk_trace_base.BasicTracerProvider({
|
|
1108
1154
|
resource,
|
|
1109
1155
|
sampler: new import_sdk_trace_base.AlwaysOnSampler(),
|
|
@@ -1125,7 +1171,7 @@ function initAutterServer(options) {
|
|
|
1125
1171
|
const isError = error instanceof Error;
|
|
1126
1172
|
const message = isError ? error.message : String(error);
|
|
1127
1173
|
const redacted = activeRedactor(attributes);
|
|
1128
|
-
const activeSpan =
|
|
1174
|
+
const activeSpan = import_api2.trace.getActiveSpan();
|
|
1129
1175
|
const reuseActive = activeSpan?.isRecording() === true;
|
|
1130
1176
|
const span = reuseActive ? activeSpan : errorTracer.startSpan(isError ? error.name : "Error");
|
|
1131
1177
|
span.setAttributes({ "autter.severity": "error", ...redacted });
|
|
@@ -1150,7 +1196,7 @@ function initAutterServer(options) {
|
|
|
1150
1196
|
...redacted["autter.handled"] === true ? { "autter.handled": true, "autter.sampled": redacted["autter.sampled"] === true } : {}
|
|
1151
1197
|
});
|
|
1152
1198
|
}
|
|
1153
|
-
span.setStatus({ code:
|
|
1199
|
+
span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message });
|
|
1154
1200
|
if (!reuseActive) span.end();
|
|
1155
1201
|
}
|
|
1156
1202
|
function captureMessage2(message, severity = "warning", attributes) {
|
|
@@ -1168,13 +1214,13 @@ function initAutterServer(options) {
|
|
|
1168
1214
|
...stack ? { "exception.stacktrace": stack } : {},
|
|
1169
1215
|
"autter.severity": severity
|
|
1170
1216
|
});
|
|
1171
|
-
span.setStatus({ code:
|
|
1217
|
+
span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message });
|
|
1172
1218
|
span.end();
|
|
1173
1219
|
}
|
|
1174
1220
|
function reportOutcome2(name, message, attributes) {
|
|
1175
1221
|
errorTraceBuffer?.retainActiveTrace();
|
|
1176
1222
|
telemetryStats.markCaptured();
|
|
1177
|
-
const activeSpan =
|
|
1223
|
+
const activeSpan = import_api2.trace.getActiveSpan();
|
|
1178
1224
|
const reuseActive = activeSpan?.isRecording() === true;
|
|
1179
1225
|
const span = reuseActive ? activeSpan : errorTracer.startSpan("OutcomeFailure");
|
|
1180
1226
|
const safeOutcome = activeRedactor({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
|
|
@@ -1186,7 +1232,7 @@ function initAutterServer(options) {
|
|
|
1186
1232
|
"autter.outcome.name": safeName,
|
|
1187
1233
|
"autter.outcome.message": safeMessage
|
|
1188
1234
|
});
|
|
1189
|
-
span.setStatus({ code:
|
|
1235
|
+
span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message: safeMessage });
|
|
1190
1236
|
if (!reuseActive) span.end();
|
|
1191
1237
|
}
|
|
1192
1238
|
if (options.captureGlobalErrors !== false) {
|
|
@@ -1225,6 +1271,7 @@ function initAutterServer(options) {
|
|
|
1225
1271
|
withLlmCall: (info, fn) => runLlmSpan(llmTracer, info, fn),
|
|
1226
1272
|
trackLlmCall: (call) => recordLlmCall(llmTracer, call),
|
|
1227
1273
|
shutdown: async () => {
|
|
1274
|
+
stopMemoryMetrics?.();
|
|
1228
1275
|
active = null;
|
|
1229
1276
|
activeAlwaysOnProvider = null;
|
|
1230
1277
|
autoFlushHandle?.dispose();
|
|
@@ -1243,35 +1290,35 @@ function captureException(error, attributes) {
|
|
|
1243
1290
|
active.captureException(error, attributes);
|
|
1244
1291
|
return;
|
|
1245
1292
|
}
|
|
1246
|
-
const span =
|
|
1293
|
+
const span = import_api2.trace.getTracer("autter-errors").startSpan(error instanceof Error ? error.name : "Error", { attributes });
|
|
1247
1294
|
if (error instanceof Error) span.recordException(error);
|
|
1248
1295
|
span.setStatus({
|
|
1249
|
-
code:
|
|
1296
|
+
code: import_api2.SpanStatusCode.ERROR,
|
|
1250
1297
|
message: error instanceof Error ? error.message : String(error)
|
|
1251
1298
|
});
|
|
1252
1299
|
span.end();
|
|
1253
1300
|
}
|
|
1254
1301
|
function withProcessSpan(name, fn, attributes) {
|
|
1255
1302
|
if (active) return active.withProcessSpan(name, fn, attributes);
|
|
1256
|
-
return runWithSpan(
|
|
1303
|
+
return runWithSpan(import_api2.trace.getTracer("autter-processes"), name, fn, attributes);
|
|
1257
1304
|
}
|
|
1258
1305
|
function withLlmCall(info, fn) {
|
|
1259
1306
|
if (active) return active.withLlmCall(info, fn);
|
|
1260
|
-
return runLlmSpan(
|
|
1307
|
+
return runLlmSpan(import_api2.trace.getTracer("autter-llm"), info, fn);
|
|
1261
1308
|
}
|
|
1262
1309
|
function trackLlmCall(call) {
|
|
1263
1310
|
if (active) {
|
|
1264
1311
|
active.trackLlmCall(call);
|
|
1265
1312
|
return;
|
|
1266
1313
|
}
|
|
1267
|
-
recordLlmCall(
|
|
1314
|
+
recordLlmCall(import_api2.trace.getTracer("autter-llm"), call);
|
|
1268
1315
|
}
|
|
1269
1316
|
function captureMessage(message, severity = "warning", attributes) {
|
|
1270
1317
|
if (active) {
|
|
1271
1318
|
active.captureMessage(message, severity, attributes);
|
|
1272
1319
|
return;
|
|
1273
1320
|
}
|
|
1274
|
-
const span =
|
|
1321
|
+
const span = import_api2.trace.getTracer("autter-errors").startSpan("Message", {
|
|
1275
1322
|
attributes: { "autter.severity": severity, ...attributes }
|
|
1276
1323
|
});
|
|
1277
1324
|
span.addEvent("exception", {
|
|
@@ -1279,12 +1326,12 @@ function captureMessage(message, severity = "warning", attributes) {
|
|
|
1279
1326
|
"exception.message": message,
|
|
1280
1327
|
"autter.severity": severity
|
|
1281
1328
|
});
|
|
1282
|
-
span.setStatus({ code:
|
|
1329
|
+
span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message });
|
|
1283
1330
|
span.end();
|
|
1284
1331
|
}
|
|
1285
1332
|
function reportOutcome(name, message, attributes) {
|
|
1286
1333
|
if (active) return active.reportOutcome(name, message, attributes);
|
|
1287
|
-
const span =
|
|
1334
|
+
const span = import_api2.trace.getTracer("autter-outcomes").startSpan("OutcomeFailure");
|
|
1288
1335
|
const safeOutcome = redactAttributes({ "autter.outcome.name": name.slice(0, 200), "autter.outcome.message": message.slice(0, 1e3) });
|
|
1289
1336
|
const safeName = String(safeOutcome["autter.outcome.name"] ?? "operation");
|
|
1290
1337
|
const safeMessage = String(safeOutcome["autter.outcome.message"] ?? "failed");
|
|
@@ -1294,7 +1341,7 @@ function reportOutcome(name, message, attributes) {
|
|
|
1294
1341
|
"autter.outcome.name": safeName,
|
|
1295
1342
|
"autter.outcome.message": safeMessage
|
|
1296
1343
|
});
|
|
1297
|
-
span.setStatus({ code:
|
|
1344
|
+
span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message: safeMessage });
|
|
1298
1345
|
span.end();
|
|
1299
1346
|
}
|
|
1300
1347
|
function makeSafeCapture(options) {
|
|
@@ -1305,7 +1352,7 @@ function makeSafeCapture(options) {
|
|
|
1305
1352
|
};
|
|
1306
1353
|
}
|
|
1307
1354
|
function alwaysOnTracer(name) {
|
|
1308
|
-
return activeAlwaysOnProvider ? activeAlwaysOnProvider.getTracer(name) :
|
|
1355
|
+
return activeAlwaysOnProvider ? activeAlwaysOnProvider.getTracer(name) : import_api2.trace.getTracer(name);
|
|
1309
1356
|
}
|
|
1310
1357
|
function autterLlmTracer() {
|
|
1311
1358
|
return alwaysOnTracer("autter-llm");
|
|
@@ -1325,7 +1372,7 @@ async function emitLlmSelftestTrace() {
|
|
|
1325
1372
|
attributes: { "autter.selftest": true }
|
|
1326
1373
|
},
|
|
1327
1374
|
(llm) => {
|
|
1328
|
-
traceId =
|
|
1375
|
+
traceId = import_api2.trace.getActiveSpan()?.spanContext().traceId ?? "";
|
|
1329
1376
|
llm.setUsage({ inputTokens: 1, outputTokens: 1 });
|
|
1330
1377
|
llm.setCost(0);
|
|
1331
1378
|
}
|
|
@@ -1335,7 +1382,7 @@ async function emitLlmSelftestTrace() {
|
|
|
1335
1382
|
}
|
|
1336
1383
|
|
|
1337
1384
|
// src/llm-instrument.ts
|
|
1338
|
-
var
|
|
1385
|
+
var import_api3 = require("@opentelemetry/api");
|
|
1339
1386
|
var CALL_METHODS = /* @__PURE__ */ new Set([
|
|
1340
1387
|
"create",
|
|
1341
1388
|
// openai chat/completions/embeddings/responses, anthropic messages
|
|
@@ -1393,7 +1440,7 @@ function applyUsage(span, usage) {
|
|
|
1393
1440
|
function endWithError(span, err) {
|
|
1394
1441
|
if (err instanceof Error) span.recordException(err);
|
|
1395
1442
|
span.setStatus({
|
|
1396
|
-
code:
|
|
1443
|
+
code: import_api3.SpanStatusCode.ERROR,
|
|
1397
1444
|
message: err instanceof Error ? err.message : String(err)
|
|
1398
1445
|
});
|
|
1399
1446
|
span.end();
|
|
@@ -1413,7 +1460,7 @@ function wrapStream(stream, span) {
|
|
|
1413
1460
|
if (err !== void 0) {
|
|
1414
1461
|
endWithError(span, err);
|
|
1415
1462
|
} else {
|
|
1416
|
-
span.setStatus({ code:
|
|
1463
|
+
span.setStatus({ code: import_api3.SpanStatusCode.OK });
|
|
1417
1464
|
span.end();
|
|
1418
1465
|
}
|
|
1419
1466
|
};
|
|
@@ -1468,7 +1515,7 @@ function instrumentLlmClient(client, options) {
|
|
|
1468
1515
|
const operation = /embed/i.test(callPath) ? "embeddings" : "chat";
|
|
1469
1516
|
const userId = resolveUserId();
|
|
1470
1517
|
const span = autterLlmTracer().startSpan(`${operation} ${request.model}`, {
|
|
1471
|
-
kind:
|
|
1518
|
+
kind: import_api3.SpanKind.CLIENT,
|
|
1472
1519
|
attributes: {
|
|
1473
1520
|
"gen_ai.operation.name": operation,
|
|
1474
1521
|
"gen_ai.provider.name": provider,
|
|
@@ -1490,7 +1537,7 @@ function instrumentLlmClient(client, options) {
|
|
|
1490
1537
|
return wrapStream(result, span);
|
|
1491
1538
|
}
|
|
1492
1539
|
applyUsage(span, extractUsage(result));
|
|
1493
|
-
span.setStatus({ code:
|
|
1540
|
+
span.setStatus({ code: import_api3.SpanStatusCode.OK });
|
|
1494
1541
|
span.end();
|
|
1495
1542
|
return result;
|
|
1496
1543
|
}
|
|
@@ -1500,7 +1547,7 @@ function instrumentLlmClient(client, options) {
|
|
|
1500
1547
|
return wrapStream(resolved, span);
|
|
1501
1548
|
}
|
|
1502
1549
|
applyUsage(span, extractUsage(resolved));
|
|
1503
|
-
span.setStatus({ code:
|
|
1550
|
+
span.setStatus({ code: import_api3.SpanStatusCode.OK });
|
|
1504
1551
|
span.end();
|
|
1505
1552
|
return resolved;
|
|
1506
1553
|
},
|
package/dist/index.d.cts
CHANGED
|
@@ -100,6 +100,8 @@ interface AutterServerOptions {
|
|
|
100
100
|
/** Ingester base URL. Default: https://otlp.autter.dev */
|
|
101
101
|
endpoint?: string;
|
|
102
102
|
service: string;
|
|
103
|
+
/** Stable for this process lifetime; use an ECS task/container ID or pod UID plus restart count to correlate platform events. */
|
|
104
|
+
instanceId?: string;
|
|
103
105
|
environment?: string;
|
|
104
106
|
/** e.g. a git SHA. Maps to service.version / release. */
|
|
105
107
|
release?: string;
|
|
@@ -119,6 +121,8 @@ interface AutterServerOptions {
|
|
|
119
121
|
retainTracesAboveMs?: number;
|
|
120
122
|
/** Metric export interval. Default 60_000 ms. */
|
|
121
123
|
metricIntervalMs?: number;
|
|
124
|
+
/** Collect RSS, heap, limit, and GC signals per process instance. Default true. */
|
|
125
|
+
memoryMetrics?: boolean;
|
|
122
126
|
/** Capture crashing exceptions via process.uncaughtExceptionMonitor (default true). */
|
|
123
127
|
captureGlobalErrors?: boolean;
|
|
124
128
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -100,6 +100,8 @@ interface AutterServerOptions {
|
|
|
100
100
|
/** Ingester base URL. Default: https://otlp.autter.dev */
|
|
101
101
|
endpoint?: string;
|
|
102
102
|
service: string;
|
|
103
|
+
/** Stable for this process lifetime; use an ECS task/container ID or pod UID plus restart count to correlate platform events. */
|
|
104
|
+
instanceId?: string;
|
|
103
105
|
environment?: string;
|
|
104
106
|
/** e.g. a git SHA. Maps to service.version / release. */
|
|
105
107
|
release?: string;
|
|
@@ -119,6 +121,8 @@ interface AutterServerOptions {
|
|
|
119
121
|
retainTracesAboveMs?: number;
|
|
120
122
|
/** Metric export interval. Default 60_000 ms. */
|
|
121
123
|
metricIntervalMs?: number;
|
|
124
|
+
/** Collect RSS, heap, limit, and GC signals per process instance. Default true. */
|
|
125
|
+
memoryMetrics?: boolean;
|
|
122
126
|
/** Capture crashing exceptions via process.uncaughtExceptionMonitor (default true). */
|
|
123
127
|
captureGlobalErrors?: boolean;
|
|
124
128
|
/**
|
package/dist/index.js
CHANGED
|
@@ -675,6 +675,51 @@ function makeRedactor(options) {
|
|
|
675
675
|
return (attributes) => redactWith(attributes, r);
|
|
676
676
|
}
|
|
677
677
|
|
|
678
|
+
// src/memory.ts
|
|
679
|
+
import { PerformanceObserver } from "perf_hooks";
|
|
680
|
+
import { getHeapStatistics } from "v8";
|
|
681
|
+
import { metrics } from "@opentelemetry/api";
|
|
682
|
+
function startMemoryMetrics() {
|
|
683
|
+
const meter = metrics.getMeter("autter-process-memory");
|
|
684
|
+
const rss = meter.createObservableGauge("autter.process.memory.rss", { unit: "By" });
|
|
685
|
+
const heapUsed = meter.createObservableGauge("autter.process.memory.heap.used", { unit: "By" });
|
|
686
|
+
const heapLimit = meter.createObservableGauge("autter.process.memory.heap.limit", { unit: "By" });
|
|
687
|
+
const containerLimit = meter.createObservableGauge("autter.process.memory.limit", { unit: "By" });
|
|
688
|
+
const afterGc = meter.createObservableGauge("autter.process.memory.heap.after_gc", { unit: "By" });
|
|
689
|
+
const gcCount = meter.createObservableCounter("autter.process.gc.count", { unit: "{collection}" });
|
|
690
|
+
const gcDuration = meter.createObservableCounter("autter.process.gc.duration", { unit: "ms" });
|
|
691
|
+
let collections = 0;
|
|
692
|
+
let durationMs = 0;
|
|
693
|
+
let lastAfterGc = null;
|
|
694
|
+
const callbacks = [
|
|
695
|
+
[rss, (result) => result.observe(process.memoryUsage().rss)],
|
|
696
|
+
[heapUsed, (result) => result.observe(process.memoryUsage().heapUsed)],
|
|
697
|
+
[heapLimit, (result) => result.observe(getHeapStatistics().heap_size_limit)],
|
|
698
|
+
[containerLimit, (result) => {
|
|
699
|
+
const constrained = process.constrainedMemory?.() ?? 0;
|
|
700
|
+
if (constrained > 0) result.observe(constrained);
|
|
701
|
+
}],
|
|
702
|
+
[afterGc, (result) => {
|
|
703
|
+
if (lastAfterGc !== null) result.observe(lastAfterGc);
|
|
704
|
+
}],
|
|
705
|
+
[gcCount, (result) => result.observe(collections)],
|
|
706
|
+
[gcDuration, (result) => result.observe(durationMs)]
|
|
707
|
+
];
|
|
708
|
+
for (const [instrument, callback] of callbacks) instrument.addCallback(callback);
|
|
709
|
+
const observer = new PerformanceObserver((list) => {
|
|
710
|
+
for (const entry of list.getEntries()) {
|
|
711
|
+
collections++;
|
|
712
|
+
durationMs += entry.duration;
|
|
713
|
+
lastAfterGc = process.memoryUsage().heapUsed;
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
observer.observe({ entryTypes: ["gc"] });
|
|
717
|
+
return () => {
|
|
718
|
+
observer.disconnect();
|
|
719
|
+
for (const [instrument, callback] of callbacks) instrument.removeCallback(callback);
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
|
|
678
723
|
// src/server.ts
|
|
679
724
|
var LLM_MARKER_ATTRIBUTES = [
|
|
680
725
|
"gen_ai.operation.name",
|
|
@@ -1007,7 +1052,7 @@ function initAutterServer(options) {
|
|
|
1007
1052
|
activeRedactor = makeRedactor(options.redactAttributes ?? true);
|
|
1008
1053
|
const resource = new Resource({
|
|
1009
1054
|
[ATTR_SERVICE_NAME]: options.service,
|
|
1010
|
-
"service.instance.id": randomUUID(),
|
|
1055
|
+
"service.instance.id": (options.instanceId || process.env.AUTTER_RUNTIME_INSTANCE_ID || randomUUID()).slice(0, 128),
|
|
1011
1056
|
...options.release ? { [ATTR_SERVICE_VERSION]: options.release } : {},
|
|
1012
1057
|
"deployment.environment": environment,
|
|
1013
1058
|
// Tells the ingester request metrics arrive on the metrics pipe, so
|
|
@@ -1072,6 +1117,7 @@ function initAutterServer(options) {
|
|
|
1072
1117
|
]
|
|
1073
1118
|
});
|
|
1074
1119
|
sdk.start();
|
|
1120
|
+
const stopMemoryMetrics = options.memoryMetrics === false ? null : startMemoryMetrics();
|
|
1075
1121
|
const alwaysOnProvider = new BasicTracerProvider({
|
|
1076
1122
|
resource,
|
|
1077
1123
|
sampler: new AlwaysOnSampler(),
|
|
@@ -1193,6 +1239,7 @@ function initAutterServer(options) {
|
|
|
1193
1239
|
withLlmCall: (info, fn) => runLlmSpan(llmTracer, info, fn),
|
|
1194
1240
|
trackLlmCall: (call) => recordLlmCall(llmTracer, call),
|
|
1195
1241
|
shutdown: async () => {
|
|
1242
|
+
stopMemoryMetrics?.();
|
|
1196
1243
|
active = null;
|
|
1197
1244
|
activeAlwaysOnProvider = null;
|
|
1198
1245
|
autoFlushHandle?.dispose();
|
package/package.json
CHANGED