@frontmcp/observability 1.9.1 → 1.9.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/esm/index.mjs +396 -219
- package/esm/package.json +3 -3
- package/index.js +466 -290
- package/otel/otel.types.d.ts +1 -1
- package/package.json +3 -3
- package/plugin/observability.hooks.d.ts +28 -0
- package/plugin/observability.plugin.d.ts +10 -2
- package/process-stats/process-stats.collector.d.ts +1 -1
- package/request-log/request-log.collector.d.ts +4 -0
- package/request-log/request-log.hooks.d.ts +35 -0
- package/request-log/request-log.types.d.ts +1 -1
- package/telemetry/telemetry.context-extension.d.ts +3 -3
package/esm/index.mjs
CHANGED
|
@@ -332,7 +332,7 @@ import {
|
|
|
332
332
|
AgentCallHook,
|
|
333
333
|
DynamicPlugin,
|
|
334
334
|
FlowHooksOf,
|
|
335
|
-
FRONTMCP_CONTEXT,
|
|
335
|
+
FRONTMCP_CONTEXT as FRONTMCP_CONTEXT2,
|
|
336
336
|
HttpHook,
|
|
337
337
|
ListResourcesHook,
|
|
338
338
|
ListResourceTemplatesHook,
|
|
@@ -704,6 +704,13 @@ var RequestLogCollector = class {
|
|
|
704
704
|
this.status = status;
|
|
705
705
|
this.statusCode = statusCode;
|
|
706
706
|
}
|
|
707
|
+
/**
|
|
708
|
+
* Set the HTTP status the request was answered with; one of 400 or more marks it failed.
|
|
709
|
+
*/
|
|
710
|
+
setStatusCode(statusCode) {
|
|
711
|
+
this.statusCode = statusCode;
|
|
712
|
+
if (statusCode >= 400) this.status = "error";
|
|
713
|
+
}
|
|
707
714
|
/**
|
|
708
715
|
* Set error details.
|
|
709
716
|
*/
|
|
@@ -772,15 +779,18 @@ var RequestLogCollector = class {
|
|
|
772
779
|
}
|
|
773
780
|
};
|
|
774
781
|
|
|
775
|
-
// libs/observability/src/request-log/request-log.
|
|
776
|
-
|
|
782
|
+
// libs/observability/src/request-log/request-log.hooks.ts
|
|
783
|
+
import { isAnonymousSubject } from "@frontmcp/sdk";
|
|
777
784
|
|
|
778
|
-
// libs/observability/src/
|
|
785
|
+
// libs/observability/src/plugin/observability.hooks.ts
|
|
779
786
|
import {
|
|
787
|
+
context as otelContext3,
|
|
780
788
|
SpanKind as SpanKind12,
|
|
781
789
|
SpanStatusCode as SpanStatusCode2,
|
|
782
|
-
trace as
|
|
790
|
+
trace as trace3
|
|
783
791
|
} from "@opentelemetry/api";
|
|
792
|
+
import { FlowControl, InternalMcpError, toMcpError } from "@frontmcp/sdk";
|
|
793
|
+
import { sha256Hex } from "@frontmcp/utils";
|
|
784
794
|
|
|
785
795
|
// libs/observability/src/otel/otel.types.ts
|
|
786
796
|
var McpAttributes = {
|
|
@@ -895,43 +905,6 @@ var EnduserAttributes = {
|
|
|
895
905
|
SCOPE: "enduser.scope"
|
|
896
906
|
};
|
|
897
907
|
|
|
898
|
-
// libs/observability/src/otel/trace-context-bridge.ts
|
|
899
|
-
import {
|
|
900
|
-
TraceFlags,
|
|
901
|
-
trace,
|
|
902
|
-
context as otelContext
|
|
903
|
-
} from "@opentelemetry/api";
|
|
904
|
-
function frontmcpToOTelSpanContext(tc) {
|
|
905
|
-
return {
|
|
906
|
-
traceId: tc.traceId,
|
|
907
|
-
spanId: tc.parentId,
|
|
908
|
-
traceFlags: tc.traceFlags & TraceFlags.SAMPLED ? TraceFlags.SAMPLED : TraceFlags.NONE,
|
|
909
|
-
isRemote: true
|
|
910
|
-
};
|
|
911
|
-
}
|
|
912
|
-
function otelToFrontmcpContext(sc) {
|
|
913
|
-
const flags = sc.traceFlags.toString(16).padStart(2, "0");
|
|
914
|
-
return {
|
|
915
|
-
traceId: sc.traceId,
|
|
916
|
-
parentId: sc.spanId,
|
|
917
|
-
traceFlags: sc.traceFlags,
|
|
918
|
-
raw: `00-${sc.traceId}-${sc.spanId}-${flags}`
|
|
919
|
-
};
|
|
920
|
-
}
|
|
921
|
-
function createOTelContextFromTrace(tc) {
|
|
922
|
-
const spanContext = frontmcpToOTelSpanContext(tc);
|
|
923
|
-
const span = trace.wrapSpanContext(spanContext);
|
|
924
|
-
return trace.setSpan(otelContext.active(), span);
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
// libs/observability/src/plugin/observability.hooks.ts
|
|
928
|
-
import {
|
|
929
|
-
context as otelContext3,
|
|
930
|
-
SpanKind as SpanKind11,
|
|
931
|
-
trace as trace3
|
|
932
|
-
} from "@opentelemetry/api";
|
|
933
|
-
import { sha256Hex } from "@frontmcp/utils";
|
|
934
|
-
|
|
935
908
|
// libs/observability/src/otel/spans/auth.span.ts
|
|
936
909
|
import { SpanKind as SpanKind2 } from "@opentelemetry/api";
|
|
937
910
|
|
|
@@ -939,11 +912,11 @@ import { SpanKind as SpanKind2 } from "@opentelemetry/api";
|
|
|
939
912
|
import {
|
|
940
913
|
SpanKind,
|
|
941
914
|
SpanStatusCode,
|
|
942
|
-
context as
|
|
943
|
-
trace
|
|
915
|
+
context as otelContext,
|
|
916
|
+
trace
|
|
944
917
|
} from "@opentelemetry/api";
|
|
945
918
|
function startSpan(tracer, options) {
|
|
946
|
-
const parentCtx = options.parentContext ??
|
|
919
|
+
const parentCtx = options.parentContext ?? otelContext.active();
|
|
947
920
|
const span = tracer.startSpan(
|
|
948
921
|
options.name,
|
|
949
922
|
{
|
|
@@ -952,7 +925,7 @@ function startSpan(tracer, options) {
|
|
|
952
925
|
},
|
|
953
926
|
parentCtx
|
|
954
927
|
);
|
|
955
|
-
const spanContext =
|
|
928
|
+
const spanContext = trace.setSpan(parentCtx, span);
|
|
956
929
|
return { span, context: spanContext };
|
|
957
930
|
}
|
|
958
931
|
function endSpanOk(span) {
|
|
@@ -969,7 +942,7 @@ function endSpanError(span, error) {
|
|
|
969
942
|
}
|
|
970
943
|
async function withSpan(tracer, options, fn) {
|
|
971
944
|
const { span, context: spanContext } = startSpan(tracer, options);
|
|
972
|
-
return
|
|
945
|
+
return otelContext.with(spanContext, async () => {
|
|
973
946
|
try {
|
|
974
947
|
const result = await fn(span);
|
|
975
948
|
endSpanOk(span);
|
|
@@ -1016,8 +989,35 @@ function setFetchResponseStatus(span, statusCode) {
|
|
|
1016
989
|
span.setAttribute(HttpAttributes.STATUS_CODE, statusCode);
|
|
1017
990
|
}
|
|
1018
991
|
|
|
1019
|
-
// libs/observability/src/otel/spans/
|
|
992
|
+
// libs/observability/src/otel/spans/hook.span.ts
|
|
1020
993
|
import { SpanKind as SpanKind4 } from "@opentelemetry/api";
|
|
994
|
+
function recordHookEvent(parentSpan, stage, owner) {
|
|
995
|
+
const attributes = {};
|
|
996
|
+
if (owner) {
|
|
997
|
+
attributes[FrontMcpAttributes.HOOK_OWNER] = owner;
|
|
998
|
+
}
|
|
999
|
+
parentSpan.addEvent(`hook.${stage}`, attributes);
|
|
1000
|
+
}
|
|
1001
|
+
function startHookSpan(tracer, options) {
|
|
1002
|
+
const attributes = {
|
|
1003
|
+
[FrontMcpAttributes.HOOK_STAGE]: options.stage
|
|
1004
|
+
};
|
|
1005
|
+
if (options.owner) {
|
|
1006
|
+
attributes[FrontMcpAttributes.HOOK_OWNER] = options.owner;
|
|
1007
|
+
}
|
|
1008
|
+
if (options.flowName) {
|
|
1009
|
+
attributes[FrontMcpAttributes.FLOW_NAME] = options.flowName;
|
|
1010
|
+
}
|
|
1011
|
+
return startSpan(tracer, {
|
|
1012
|
+
name: `hook ${options.stage}`,
|
|
1013
|
+
kind: SpanKind4.INTERNAL,
|
|
1014
|
+
attributes,
|
|
1015
|
+
parentContext: options.parentContext
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// libs/observability/src/otel/spans/http-server.span.ts
|
|
1020
|
+
import { SpanKind as SpanKind5 } from "@opentelemetry/api";
|
|
1021
1021
|
function startHttpServerSpan(tracer, options) {
|
|
1022
1022
|
const attributes = {
|
|
1023
1023
|
[HttpAttributes.METHOD]: options.method.toUpperCase(),
|
|
@@ -1040,7 +1040,7 @@ function startHttpServerSpan(tracer, options) {
|
|
|
1040
1040
|
}
|
|
1041
1041
|
return startSpan(tracer, {
|
|
1042
1042
|
name: `${options.method.toUpperCase()} ${options.path}`,
|
|
1043
|
-
kind:
|
|
1043
|
+
kind: SpanKind5.SERVER,
|
|
1044
1044
|
attributes,
|
|
1045
1045
|
parentContext: options.parentContext
|
|
1046
1046
|
});
|
|
@@ -1050,11 +1050,11 @@ function setHttpResponseStatus(span, statusCode) {
|
|
|
1050
1050
|
}
|
|
1051
1051
|
|
|
1052
1052
|
// libs/observability/src/otel/spans/prompt.span.ts
|
|
1053
|
-
import { SpanKind as
|
|
1053
|
+
import { SpanKind as SpanKind6 } from "@opentelemetry/api";
|
|
1054
1054
|
function startPromptSpan(tracer, options) {
|
|
1055
1055
|
return startSpan(tracer, {
|
|
1056
1056
|
name: `prompt ${options.name}`,
|
|
1057
|
-
kind:
|
|
1057
|
+
kind: SpanKind6.INTERNAL,
|
|
1058
1058
|
attributes: {
|
|
1059
1059
|
[FrontMcpAttributes.PROMPT_NAME]: options.name,
|
|
1060
1060
|
[McpAttributes.COMPONENT_TYPE]: "prompt",
|
|
@@ -1065,7 +1065,7 @@ function startPromptSpan(tracer, options) {
|
|
|
1065
1065
|
}
|
|
1066
1066
|
|
|
1067
1067
|
// libs/observability/src/otel/spans/resource.span.ts
|
|
1068
|
-
import { SpanKind as
|
|
1068
|
+
import { SpanKind as SpanKind7 } from "@opentelemetry/api";
|
|
1069
1069
|
function startResourceSpan(tracer, options) {
|
|
1070
1070
|
const attributes = {
|
|
1071
1071
|
[FrontMcpAttributes.RESOURCE_URI]: options.uri,
|
|
@@ -1078,14 +1078,14 @@ function startResourceSpan(tracer, options) {
|
|
|
1078
1078
|
}
|
|
1079
1079
|
return startSpan(tracer, {
|
|
1080
1080
|
name: `resource ${options.uri}`,
|
|
1081
|
-
kind:
|
|
1081
|
+
kind: SpanKind7.INTERNAL,
|
|
1082
1082
|
attributes,
|
|
1083
1083
|
parentContext: options.parentContext
|
|
1084
1084
|
});
|
|
1085
1085
|
}
|
|
1086
1086
|
|
|
1087
1087
|
// libs/observability/src/otel/spans/rpc.span.ts
|
|
1088
|
-
import { SpanKind as
|
|
1088
|
+
import { SpanKind as SpanKind8 } from "@opentelemetry/api";
|
|
1089
1089
|
function startRpcSpan(tracer, options) {
|
|
1090
1090
|
const attributes = {
|
|
1091
1091
|
[RpcAttributes.SYSTEM]: "mcp",
|
|
@@ -1109,14 +1109,14 @@ function startRpcSpan(tracer, options) {
|
|
|
1109
1109
|
}
|
|
1110
1110
|
return startSpan(tracer, {
|
|
1111
1111
|
name: options.method,
|
|
1112
|
-
kind:
|
|
1112
|
+
kind: SpanKind8.SERVER,
|
|
1113
1113
|
attributes,
|
|
1114
1114
|
parentContext: options.parentContext
|
|
1115
1115
|
});
|
|
1116
1116
|
}
|
|
1117
1117
|
|
|
1118
1118
|
// libs/observability/src/otel/spans/startup.span.ts
|
|
1119
|
-
import { SpanKind as
|
|
1119
|
+
import { SpanKind as SpanKind9 } from "@opentelemetry/api";
|
|
1120
1120
|
function emitStartupReport(tracer, data) {
|
|
1121
1121
|
const attributes = {
|
|
1122
1122
|
[FrontMcpAttributes.STARTUP_TOOLS_COUNT]: data.toolsCount,
|
|
@@ -1130,14 +1130,14 @@ function emitStartupReport(tracer, data) {
|
|
|
1130
1130
|
}
|
|
1131
1131
|
const { span } = startSpan(tracer, {
|
|
1132
1132
|
name: "frontmcp.startup",
|
|
1133
|
-
kind:
|
|
1133
|
+
kind: SpanKind9.INTERNAL,
|
|
1134
1134
|
attributes
|
|
1135
1135
|
});
|
|
1136
1136
|
endSpanOk(span);
|
|
1137
1137
|
}
|
|
1138
1138
|
|
|
1139
1139
|
// libs/observability/src/otel/spans/tool.span.ts
|
|
1140
|
-
import { SpanKind as
|
|
1140
|
+
import { SpanKind as SpanKind10 } from "@opentelemetry/api";
|
|
1141
1141
|
function startToolSpan(tracer, options) {
|
|
1142
1142
|
const attributes = {
|
|
1143
1143
|
[FrontMcpAttributes.TOOL_NAME]: options.name,
|
|
@@ -1155,14 +1155,14 @@ function startToolSpan(tracer, options) {
|
|
|
1155
1155
|
}
|
|
1156
1156
|
return startSpan(tracer, {
|
|
1157
1157
|
name: `tool ${options.name}`,
|
|
1158
|
-
kind:
|
|
1158
|
+
kind: SpanKind10.INTERNAL,
|
|
1159
1159
|
attributes,
|
|
1160
1160
|
parentContext: options.parentContext
|
|
1161
1161
|
});
|
|
1162
1162
|
}
|
|
1163
1163
|
|
|
1164
1164
|
// libs/observability/src/otel/spans/transport.span.ts
|
|
1165
|
-
import { SpanKind as
|
|
1165
|
+
import { SpanKind as SpanKind11 } from "@opentelemetry/api";
|
|
1166
1166
|
function startTransportSpan(tracer, options) {
|
|
1167
1167
|
const attributes = {
|
|
1168
1168
|
[FrontMcpAttributes.TRANSPORT_TYPE]: options.type
|
|
@@ -1175,7 +1175,7 @@ function startTransportSpan(tracer, options) {
|
|
|
1175
1175
|
}
|
|
1176
1176
|
return startSpan(tracer, {
|
|
1177
1177
|
name: `transport ${options.type}`,
|
|
1178
|
-
kind:
|
|
1178
|
+
kind: SpanKind11.INTERNAL,
|
|
1179
1179
|
attributes,
|
|
1180
1180
|
parentContext: options.parentContext
|
|
1181
1181
|
});
|
|
@@ -1184,6 +1184,35 @@ function setTransportRequestType(span, requestType) {
|
|
|
1184
1184
|
span.setAttribute(FrontMcpAttributes.TRANSPORT_REQUEST_TYPE, requestType);
|
|
1185
1185
|
}
|
|
1186
1186
|
|
|
1187
|
+
// libs/observability/src/otel/trace-context-bridge.ts
|
|
1188
|
+
import {
|
|
1189
|
+
TraceFlags,
|
|
1190
|
+
trace as trace2,
|
|
1191
|
+
context as otelContext2
|
|
1192
|
+
} from "@opentelemetry/api";
|
|
1193
|
+
function frontmcpToOTelSpanContext(tc) {
|
|
1194
|
+
return {
|
|
1195
|
+
traceId: tc.traceId,
|
|
1196
|
+
spanId: tc.parentId,
|
|
1197
|
+
traceFlags: tc.traceFlags & TraceFlags.SAMPLED ? TraceFlags.SAMPLED : TraceFlags.NONE,
|
|
1198
|
+
isRemote: true
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
function otelToFrontmcpContext(sc) {
|
|
1202
|
+
const flags = sc.traceFlags.toString(16).padStart(2, "0");
|
|
1203
|
+
return {
|
|
1204
|
+
traceId: sc.traceId,
|
|
1205
|
+
parentId: sc.spanId,
|
|
1206
|
+
traceFlags: sc.traceFlags,
|
|
1207
|
+
raw: `00-${sc.traceId}-${sc.spanId}-${flags}`
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
function createOTelContextFromTrace(tc) {
|
|
1211
|
+
const spanContext = frontmcpToOTelSpanContext(tc);
|
|
1212
|
+
const span = trace2.wrapSpanContext(spanContext);
|
|
1213
|
+
return trace2.setSpan(otelContext2.active(), span);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1187
1216
|
// libs/observability/src/plugin/observability.hooks.ts
|
|
1188
1217
|
var SPAN_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:span");
|
|
1189
1218
|
var SPAN_CTX_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:span-ctx");
|
|
@@ -1191,6 +1220,8 @@ var EXEC_SPAN_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:exec-span");
|
|
|
1191
1220
|
var ACTIVE_SPAN_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:active-span");
|
|
1192
1221
|
var ACTIVE_OTEL_CTX_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:active-otel-ctx");
|
|
1193
1222
|
var STAGE_TS_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:stage-ts");
|
|
1223
|
+
var STAGE_EVENTS_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:stage-events");
|
|
1224
|
+
var HTTP_SPAN_CTX_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:http-span-ctx");
|
|
1194
1225
|
function sessionTracingId(sessionId) {
|
|
1195
1226
|
return sha256Hex(sessionId).slice(0, 16);
|
|
1196
1227
|
}
|
|
@@ -1225,7 +1256,44 @@ function clearActiveSpan(flowCtx) {
|
|
|
1225
1256
|
} catch {
|
|
1226
1257
|
}
|
|
1227
1258
|
}
|
|
1259
|
+
function beginFlowSpan(flowCtx, options, span, spanCtx) {
|
|
1260
|
+
flowCtx.state[SPAN_KEY] = span;
|
|
1261
|
+
flowCtx.state[SPAN_CTX_KEY] = spanCtx;
|
|
1262
|
+
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1263
|
+
flowCtx.state[STAGE_EVENTS_KEY] = options.flowStageEvents !== false;
|
|
1264
|
+
}
|
|
1265
|
+
function parentContextOf(ctx) {
|
|
1266
|
+
return ctx.get?.(HTTP_SPAN_CTX_KEY) ?? createOTelContextFromTrace(ctx.traceContext);
|
|
1267
|
+
}
|
|
1268
|
+
function flowFailureOf(flowError) {
|
|
1269
|
+
const failed = flowError?.originalError;
|
|
1270
|
+
const error = flowError instanceof FlowControl ? failed instanceof Error ? failed : new InternalMcpError(`Flow ended with: ${flowError.type}`) : flowError instanceof Error ? flowError : new Error(String(flowError));
|
|
1271
|
+
const reported = toMcpError(error);
|
|
1272
|
+
return {
|
|
1273
|
+
error,
|
|
1274
|
+
type: reported.name,
|
|
1275
|
+
message: reported.getPublicMessage(),
|
|
1276
|
+
code: reported.code,
|
|
1277
|
+
errorId: reported.errorId
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
function endFailedSpan(span, flowError) {
|
|
1281
|
+
const failure = flowFailureOf(flowError);
|
|
1282
|
+
span.setStatus({ code: SpanStatusCode2.ERROR, message: failure.message });
|
|
1283
|
+
span.recordException({
|
|
1284
|
+
name: failure.type,
|
|
1285
|
+
message: failure.message,
|
|
1286
|
+
code: failure.code,
|
|
1287
|
+
stack: failure.error.stack
|
|
1288
|
+
});
|
|
1289
|
+
span.end();
|
|
1290
|
+
}
|
|
1291
|
+
function traceparentOf(span) {
|
|
1292
|
+
const { traceId, spanId, traceFlags } = span.spanContext();
|
|
1293
|
+
return `00-${traceId}-${spanId}-${traceFlags.toString(16).padStart(2, "0")}`;
|
|
1294
|
+
}
|
|
1228
1295
|
function recordStageEvent(span, stageName, state) {
|
|
1296
|
+
if (state[STAGE_EVENTS_KEY] === false) return;
|
|
1229
1297
|
const now = Date.now();
|
|
1230
1298
|
const prev = state[STAGE_TS_KEY];
|
|
1231
1299
|
const attributes = {};
|
|
@@ -1251,9 +1319,8 @@ function onHttpWillTrace(options, flowCtx) {
|
|
|
1251
1319
|
});
|
|
1252
1320
|
span.setAttribute(McpAttributes.SESSION_ID, sessionHash);
|
|
1253
1321
|
span.setAttribute(FrontMcpAttributes.SESSION_ID_HASH, sessionHash);
|
|
1254
|
-
|
|
1255
|
-
flowCtx
|
|
1256
|
-
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1322
|
+
ctx.set?.(HTTP_SPAN_CTX_KEY, spanCtx);
|
|
1323
|
+
beginFlowSpan(flowCtx, options, span, spanCtx);
|
|
1257
1324
|
recordStageEvent(span, "traceRequest", flowCtx.state);
|
|
1258
1325
|
}
|
|
1259
1326
|
function onHttpWillAcquireQuota(flowCtx) {
|
|
@@ -1297,7 +1364,7 @@ function onToolWillParse(options, flowCtx) {
|
|
|
1297
1364
|
const ctx = extractContext(flowCtx);
|
|
1298
1365
|
if (!ctx) return;
|
|
1299
1366
|
const tracer = getTracer();
|
|
1300
|
-
const parentOTelCtx =
|
|
1367
|
+
const parentOTelCtx = parentContextOf(ctx);
|
|
1301
1368
|
const sessionHash = sessionTracingId(ctx.sessionId);
|
|
1302
1369
|
const { span, context: spanCtx } = startRpcSpan(tracer, {
|
|
1303
1370
|
method: "tools/call",
|
|
@@ -1305,9 +1372,7 @@ function onToolWillParse(options, flowCtx) {
|
|
|
1305
1372
|
sessionIdHash: sessionHash,
|
|
1306
1373
|
parentContext: parentOTelCtx
|
|
1307
1374
|
});
|
|
1308
|
-
flowCtx
|
|
1309
|
-
flowCtx.state[SPAN_CTX_KEY] = spanCtx;
|
|
1310
|
-
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1375
|
+
beginFlowSpan(flowCtx, options, span, spanCtx);
|
|
1311
1376
|
recordStageEvent(span, "parseInput", flowCtx.state);
|
|
1312
1377
|
}
|
|
1313
1378
|
function onToolWillFindTool(flowCtx) {
|
|
@@ -1375,10 +1440,10 @@ function onToolWillApplyUI(flowCtx) {
|
|
|
1375
1440
|
function onToolDidFinalize(flowCtx) {
|
|
1376
1441
|
const rpcSpan = flowCtx.state?.[SPAN_KEY];
|
|
1377
1442
|
const toolSpan = flowCtx.state?.[EXEC_SPAN_KEY];
|
|
1378
|
-
const error = flowCtx.state?.
|
|
1443
|
+
const error = flowCtx.state?.flowError;
|
|
1379
1444
|
if (error) {
|
|
1380
|
-
if (toolSpan)
|
|
1381
|
-
if (rpcSpan)
|
|
1445
|
+
if (toolSpan) endFailedSpan(toolSpan, error);
|
|
1446
|
+
if (rpcSpan) endFailedSpan(rpcSpan, error);
|
|
1382
1447
|
} else if (rpcSpan) {
|
|
1383
1448
|
recordStageEvent(rpcSpan, "finalize", flowCtx.state);
|
|
1384
1449
|
endSpanOk(rpcSpan);
|
|
@@ -1389,7 +1454,7 @@ function onResourceWillParse(options, flowCtx) {
|
|
|
1389
1454
|
const ctx = extractContext(flowCtx);
|
|
1390
1455
|
if (!ctx) return;
|
|
1391
1456
|
const tracer = getTracer();
|
|
1392
|
-
const parentOTelCtx =
|
|
1457
|
+
const parentOTelCtx = parentContextOf(ctx);
|
|
1393
1458
|
const sessionHash = sessionTracingId(ctx.sessionId);
|
|
1394
1459
|
const { span, context: spanCtx } = startRpcSpan(tracer, {
|
|
1395
1460
|
method: "resources/read",
|
|
@@ -1397,9 +1462,7 @@ function onResourceWillParse(options, flowCtx) {
|
|
|
1397
1462
|
sessionIdHash: sessionHash,
|
|
1398
1463
|
parentContext: parentOTelCtx
|
|
1399
1464
|
});
|
|
1400
|
-
flowCtx
|
|
1401
|
-
flowCtx.state[SPAN_CTX_KEY] = spanCtx;
|
|
1402
|
-
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1465
|
+
beginFlowSpan(flowCtx, options, span, spanCtx);
|
|
1403
1466
|
recordStageEvent(span, "parseInput", flowCtx.state);
|
|
1404
1467
|
}
|
|
1405
1468
|
function onResourceWillFind(flowCtx) {
|
|
@@ -1428,10 +1491,10 @@ function onResourceDidExecute(flowCtx) {
|
|
|
1428
1491
|
function onResourceDidFinalize(flowCtx) {
|
|
1429
1492
|
const rpcSpan = flowCtx.state?.[SPAN_KEY];
|
|
1430
1493
|
const resSpan = flowCtx.state?.[EXEC_SPAN_KEY];
|
|
1431
|
-
const error = flowCtx.state?.
|
|
1494
|
+
const error = flowCtx.state?.flowError;
|
|
1432
1495
|
if (error) {
|
|
1433
|
-
if (resSpan)
|
|
1434
|
-
if (rpcSpan)
|
|
1496
|
+
if (resSpan) endFailedSpan(resSpan, error);
|
|
1497
|
+
if (rpcSpan) endFailedSpan(rpcSpan, error);
|
|
1435
1498
|
} else if (rpcSpan) {
|
|
1436
1499
|
recordStageEvent(rpcSpan, "finalize", flowCtx.state);
|
|
1437
1500
|
endSpanOk(rpcSpan);
|
|
@@ -1442,7 +1505,7 @@ function onPromptWillParse(options, flowCtx) {
|
|
|
1442
1505
|
const ctx = extractContext(flowCtx);
|
|
1443
1506
|
if (!ctx) return;
|
|
1444
1507
|
const tracer = getTracer();
|
|
1445
|
-
const parentOTelCtx =
|
|
1508
|
+
const parentOTelCtx = parentContextOf(ctx);
|
|
1446
1509
|
const sessionHash = sessionTracingId(ctx.sessionId);
|
|
1447
1510
|
const { span, context: spanCtx } = startRpcSpan(tracer, {
|
|
1448
1511
|
method: "prompts/get",
|
|
@@ -1450,9 +1513,7 @@ function onPromptWillParse(options, flowCtx) {
|
|
|
1450
1513
|
sessionIdHash: sessionHash,
|
|
1451
1514
|
parentContext: parentOTelCtx
|
|
1452
1515
|
});
|
|
1453
|
-
flowCtx
|
|
1454
|
-
flowCtx.state[SPAN_CTX_KEY] = spanCtx;
|
|
1455
|
-
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1516
|
+
beginFlowSpan(flowCtx, options, span, spanCtx);
|
|
1456
1517
|
recordStageEvent(span, "parseInput", flowCtx.state);
|
|
1457
1518
|
}
|
|
1458
1519
|
function onPromptWillFind(flowCtx) {
|
|
@@ -1484,10 +1545,10 @@ function onPromptDidExecute(flowCtx) {
|
|
|
1484
1545
|
function onPromptDidFinalize(flowCtx) {
|
|
1485
1546
|
const rpcSpan = flowCtx.state?.[SPAN_KEY];
|
|
1486
1547
|
const promptSpan = flowCtx.state?.[EXEC_SPAN_KEY];
|
|
1487
|
-
const error = flowCtx.state?.
|
|
1548
|
+
const error = flowCtx.state?.flowError;
|
|
1488
1549
|
if (error) {
|
|
1489
|
-
if (promptSpan)
|
|
1490
|
-
if (rpcSpan)
|
|
1550
|
+
if (promptSpan) endFailedSpan(promptSpan, error);
|
|
1551
|
+
if (rpcSpan) endFailedSpan(rpcSpan, error);
|
|
1491
1552
|
} else if (rpcSpan) {
|
|
1492
1553
|
recordStageEvent(rpcSpan, "finalize", flowCtx.state);
|
|
1493
1554
|
endSpanOk(rpcSpan);
|
|
@@ -1498,7 +1559,7 @@ function onAgentWillParse(options, flowCtx) {
|
|
|
1498
1559
|
const ctx = extractContext(flowCtx);
|
|
1499
1560
|
if (!ctx) return;
|
|
1500
1561
|
const tracer = getTracer();
|
|
1501
|
-
const parentOTelCtx =
|
|
1562
|
+
const parentOTelCtx = parentContextOf(ctx);
|
|
1502
1563
|
const sessionHash = sessionTracingId(ctx.sessionId);
|
|
1503
1564
|
const { span, context: spanCtx } = startRpcSpan(tracer, {
|
|
1504
1565
|
method: "agents/call",
|
|
@@ -1506,9 +1567,7 @@ function onAgentWillParse(options, flowCtx) {
|
|
|
1506
1567
|
sessionIdHash: sessionHash,
|
|
1507
1568
|
parentContext: parentOTelCtx
|
|
1508
1569
|
});
|
|
1509
|
-
flowCtx
|
|
1510
|
-
flowCtx.state[SPAN_CTX_KEY] = spanCtx;
|
|
1511
|
-
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1570
|
+
beginFlowSpan(flowCtx, options, span, spanCtx);
|
|
1512
1571
|
recordStageEvent(span, "parseInput", flowCtx.state);
|
|
1513
1572
|
}
|
|
1514
1573
|
function onAgentWillFind(flowCtx) {
|
|
@@ -1522,7 +1581,7 @@ function onAgentWillExecute(options, flowCtx) {
|
|
|
1522
1581
|
const agentName = flowCtx.state?.input?.name ?? "unknown";
|
|
1523
1582
|
const { span: agentSpan } = startSpan(tracer, {
|
|
1524
1583
|
name: `agent ${agentName}`,
|
|
1525
|
-
kind:
|
|
1584
|
+
kind: SpanKind12.INTERNAL,
|
|
1526
1585
|
attributes: {
|
|
1527
1586
|
[FrontMcpAttributes.FLOW_NAME]: "agents:call-agent",
|
|
1528
1587
|
[McpAttributes.COMPONENT_TYPE]: "agent",
|
|
@@ -1547,10 +1606,10 @@ function onAgentDidExecute(flowCtx) {
|
|
|
1547
1606
|
function onAgentDidFinalize(flowCtx) {
|
|
1548
1607
|
const rpcSpan = flowCtx.state?.[SPAN_KEY];
|
|
1549
1608
|
const agentSpan = flowCtx.state?.[EXEC_SPAN_KEY];
|
|
1550
|
-
const error = flowCtx.state?.
|
|
1609
|
+
const error = flowCtx.state?.flowError;
|
|
1551
1610
|
if (error) {
|
|
1552
|
-
if (agentSpan)
|
|
1553
|
-
if (rpcSpan)
|
|
1611
|
+
if (agentSpan) endFailedSpan(agentSpan, error);
|
|
1612
|
+
if (rpcSpan) endFailedSpan(rpcSpan, error);
|
|
1554
1613
|
} else if (rpcSpan) {
|
|
1555
1614
|
recordStageEvent(rpcSpan, "finalize", flowCtx.state);
|
|
1556
1615
|
endSpanOk(rpcSpan);
|
|
@@ -1561,11 +1620,11 @@ function onGenericFlowWillStart(flowName, options, flowCtx) {
|
|
|
1561
1620
|
const ctx = extractContext(flowCtx);
|
|
1562
1621
|
if (!ctx) return;
|
|
1563
1622
|
const tracer = getTracer();
|
|
1564
|
-
const parentOTelCtx =
|
|
1623
|
+
const parentOTelCtx = parentContextOf(ctx);
|
|
1565
1624
|
const sessionHash = sessionTracingId(ctx.sessionId);
|
|
1566
1625
|
const { span, context: spanCtx } = startSpan(tracer, {
|
|
1567
1626
|
name: flowName,
|
|
1568
|
-
kind:
|
|
1627
|
+
kind: SpanKind12.INTERNAL,
|
|
1569
1628
|
attributes: {
|
|
1570
1629
|
[FrontMcpAttributes.FLOW_NAME]: flowName,
|
|
1571
1630
|
[McpAttributes.SESSION_ID]: sessionHash,
|
|
@@ -1575,9 +1634,7 @@ function onGenericFlowWillStart(flowName, options, flowCtx) {
|
|
|
1575
1634
|
},
|
|
1576
1635
|
parentContext: parentOTelCtx
|
|
1577
1636
|
});
|
|
1578
|
-
flowCtx
|
|
1579
|
-
flowCtx.state[SPAN_CTX_KEY] = spanCtx;
|
|
1580
|
-
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1637
|
+
beginFlowSpan(flowCtx, options, span, spanCtx);
|
|
1581
1638
|
}
|
|
1582
1639
|
function onGenericFlowStage(stageName, flowCtx) {
|
|
1583
1640
|
const span = flowCtx.state?.[SPAN_KEY];
|
|
@@ -1586,9 +1643,9 @@ function onGenericFlowStage(stageName, flowCtx) {
|
|
|
1586
1643
|
function onGenericFlowDidFinalize(flowCtx) {
|
|
1587
1644
|
const span = flowCtx.state?.[SPAN_KEY];
|
|
1588
1645
|
if (!span) return;
|
|
1589
|
-
const error = flowCtx.state?.
|
|
1646
|
+
const error = flowCtx.state?.flowError;
|
|
1590
1647
|
if (error) {
|
|
1591
|
-
|
|
1648
|
+
endFailedSpan(span, error);
|
|
1592
1649
|
} else {
|
|
1593
1650
|
endSpanOk(span);
|
|
1594
1651
|
}
|
|
@@ -1598,16 +1655,14 @@ function onTransportWillStart(transportType, options, flowCtx) {
|
|
|
1598
1655
|
const ctx = extractContext(flowCtx);
|
|
1599
1656
|
if (!ctx) return;
|
|
1600
1657
|
const tracer = getTracer();
|
|
1601
|
-
const parentOTelCtx =
|
|
1658
|
+
const parentOTelCtx = parentContextOf(ctx);
|
|
1602
1659
|
const sessionHash = sessionTracingId(ctx.sessionId);
|
|
1603
1660
|
const { span, context: spanCtx } = startTransportSpan(tracer, {
|
|
1604
1661
|
type: transportType,
|
|
1605
1662
|
sessionIdHash: sessionHash,
|
|
1606
1663
|
parentContext: parentOTelCtx
|
|
1607
1664
|
});
|
|
1608
|
-
flowCtx
|
|
1609
|
-
flowCtx.state[SPAN_CTX_KEY] = spanCtx;
|
|
1610
|
-
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1665
|
+
beginFlowSpan(flowCtx, options, span, spanCtx);
|
|
1611
1666
|
recordStageEvent(span, "parseInput", flowCtx.state);
|
|
1612
1667
|
}
|
|
1613
1668
|
function onTransportDidRoute(flowCtx) {
|
|
@@ -1625,9 +1680,9 @@ function onTransportDidFinalize(flowCtx) {
|
|
|
1625
1680
|
const span = flowCtx.state?.[SPAN_KEY];
|
|
1626
1681
|
if (!span) return;
|
|
1627
1682
|
recordStageEvent(span, "cleanup", flowCtx.state);
|
|
1628
|
-
const error = flowCtx.state?.
|
|
1683
|
+
const error = flowCtx.state?.flowError;
|
|
1629
1684
|
if (error) {
|
|
1630
|
-
|
|
1685
|
+
endFailedSpan(span, error);
|
|
1631
1686
|
} else {
|
|
1632
1687
|
endSpanOk(span);
|
|
1633
1688
|
}
|
|
@@ -1637,14 +1692,12 @@ function onAuthWillStart(flowName, options, flowCtx) {
|
|
|
1637
1692
|
const ctx = extractContext(flowCtx);
|
|
1638
1693
|
if (!ctx) return;
|
|
1639
1694
|
const tracer = getTracer();
|
|
1640
|
-
const parentOTelCtx =
|
|
1695
|
+
const parentOTelCtx = parentContextOf(ctx);
|
|
1641
1696
|
const { span, context: spanCtx } = startAuthSpan(tracer, {
|
|
1642
1697
|
flowName,
|
|
1643
1698
|
parentContext: parentOTelCtx
|
|
1644
1699
|
});
|
|
1645
|
-
flowCtx
|
|
1646
|
-
flowCtx.state[SPAN_CTX_KEY] = spanCtx;
|
|
1647
|
-
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1700
|
+
beginFlowSpan(flowCtx, options, span, spanCtx);
|
|
1648
1701
|
recordStageEvent(span, "parseInput", flowCtx.state);
|
|
1649
1702
|
}
|
|
1650
1703
|
function onAuthDidDetermineMode(flowCtx) {
|
|
@@ -1661,33 +1714,38 @@ function onAuthStage(stageName, flowCtx) {
|
|
|
1661
1714
|
function onAuthDidFinalize(flowCtx) {
|
|
1662
1715
|
const span = flowCtx.state?.[SPAN_KEY];
|
|
1663
1716
|
if (!span) return;
|
|
1664
|
-
const error = flowCtx.state?.
|
|
1717
|
+
const error = flowCtx.state?.flowError;
|
|
1665
1718
|
if (error) {
|
|
1666
1719
|
setAuthResult(span, "unauthorized");
|
|
1667
|
-
|
|
1720
|
+
endFailedSpan(span, error);
|
|
1668
1721
|
} else {
|
|
1669
1722
|
setAuthResult(span, "authorized");
|
|
1670
1723
|
endSpanOk(span);
|
|
1671
1724
|
}
|
|
1672
1725
|
}
|
|
1673
1726
|
function wrapContextFetch(options, flowCtx) {
|
|
1674
|
-
if (options.fetchSpans === false) return;
|
|
1675
1727
|
const toolCtx = flowCtx.state?.toolContext ?? flowCtx.state?.toolCallContext;
|
|
1676
1728
|
if (!toolCtx || typeof toolCtx.fetch !== "function") return;
|
|
1677
1729
|
const originalFetch = toolCtx.fetch.bind(toolCtx);
|
|
1678
1730
|
const parentOTelCtx = flowCtx.state?.[SPAN_CTX_KEY];
|
|
1679
1731
|
toolCtx.fetch = async (input, init) => {
|
|
1680
|
-
const
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1732
|
+
const fetchSpan = options.fetchSpans === false ? void 0 : startFetchSpan(getTracer(), {
|
|
1733
|
+
method: init?.method ?? (input instanceof Request ? input.method : "GET"),
|
|
1734
|
+
url: typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url,
|
|
1735
|
+
parentContext: parentOTelCtx
|
|
1736
|
+
}).span;
|
|
1737
|
+
const callingSpan = fetchSpan ?? flowCtx.state?.[EXEC_SPAN_KEY] ?? flowCtx.state?.[SPAN_KEY];
|
|
1738
|
+
const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
|
|
1739
|
+
if (callingSpan && !headers.has("traceparent")) headers.set("traceparent", traceparentOf(callingSpan));
|
|
1684
1740
|
try {
|
|
1685
|
-
const response = await originalFetch(input, init);
|
|
1686
|
-
|
|
1687
|
-
|
|
1741
|
+
const response = await originalFetch(input, { ...init, headers });
|
|
1742
|
+
if (fetchSpan) {
|
|
1743
|
+
setFetchResponseStatus(fetchSpan, response.status);
|
|
1744
|
+
endSpanOk(fetchSpan);
|
|
1745
|
+
}
|
|
1688
1746
|
return response;
|
|
1689
1747
|
} catch (err) {
|
|
1690
|
-
endSpanError(
|
|
1748
|
+
if (fetchSpan) endSpanError(fetchSpan, err instanceof Error ? err : String(err));
|
|
1691
1749
|
throw err;
|
|
1692
1750
|
}
|
|
1693
1751
|
};
|
|
@@ -1705,11 +1763,96 @@ function onAgentDidExecuteEnrich(flowCtx) {
|
|
|
1705
1763
|
}
|
|
1706
1764
|
}
|
|
1707
1765
|
}
|
|
1766
|
+
var HOOK_OBSERVER_KEY = /* @__PURE__ */ Symbol.for("frontmcp:hook-observer");
|
|
1767
|
+
function installHookObserver(flowCtx, observer) {
|
|
1768
|
+
const ctx = extractContext(flowCtx);
|
|
1769
|
+
if (ctx && !ctx.get?.(HOOK_OBSERVER_KEY)) ctx.set?.(HOOK_OBSERVER_KEY, observer);
|
|
1770
|
+
}
|
|
1771
|
+
async function runInHookSpan(flowCtx, hook, run) {
|
|
1772
|
+
const ctx = extractContext(flowCtx);
|
|
1773
|
+
const { span } = startHookSpan(getTracer(), {
|
|
1774
|
+
...hook,
|
|
1775
|
+
parentContext: flowCtx.state?.[SPAN_CTX_KEY] ?? (ctx && parentContextOf(ctx))
|
|
1776
|
+
});
|
|
1777
|
+
try {
|
|
1778
|
+
await run();
|
|
1779
|
+
endSpanOk(span);
|
|
1780
|
+
} catch (error) {
|
|
1781
|
+
if (error instanceof FlowControl && error.type !== "fail" && error.type !== "abort") endSpanOk(span);
|
|
1782
|
+
else endFailedSpan(span, error);
|
|
1783
|
+
throw error;
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1708
1786
|
function reportStartup(data) {
|
|
1709
1787
|
const tracer = getTracer();
|
|
1710
1788
|
emitStartupReport(tracer, data);
|
|
1711
1789
|
}
|
|
1712
1790
|
|
|
1791
|
+
// libs/observability/src/request-log/request-log.tokens.ts
|
|
1792
|
+
var REQUEST_LOG_COLLECTOR = /* @__PURE__ */ Symbol.for("frontmcp:observability:request-log-collector");
|
|
1793
|
+
|
|
1794
|
+
// libs/observability/src/request-log/request-log.hooks.ts
|
|
1795
|
+
var FRONTMCP_CONTEXT = /* @__PURE__ */ Symbol.for("frontmcp:CONTEXT");
|
|
1796
|
+
function resolve(flowCtx, token) {
|
|
1797
|
+
try {
|
|
1798
|
+
return flowCtx.get?.(token);
|
|
1799
|
+
} catch {
|
|
1800
|
+
return void 0;
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
function currentRequestLog(flowCtx) {
|
|
1804
|
+
return resolve(flowCtx, FRONTMCP_CONTEXT)?.get(REQUEST_LOG_COLLECTOR);
|
|
1805
|
+
}
|
|
1806
|
+
function startRequestLog(flowCtx, options) {
|
|
1807
|
+
const context = resolve(flowCtx, FRONTMCP_CONTEXT);
|
|
1808
|
+
if (!context) return;
|
|
1809
|
+
const collector = new RequestLogCollector(
|
|
1810
|
+
{
|
|
1811
|
+
requestId: context.requestId,
|
|
1812
|
+
traceId: context.traceContext.traceId,
|
|
1813
|
+
sessionIdHash: sessionTracingId(context.sessionId).slice(0, 12),
|
|
1814
|
+
scopeId: context.scopeId
|
|
1815
|
+
},
|
|
1816
|
+
options
|
|
1817
|
+
);
|
|
1818
|
+
context.set(REQUEST_LOG_COLLECTOR, collector);
|
|
1819
|
+
const request = flowCtx.rawInput?.request;
|
|
1820
|
+
if (request?.method && request.path) collector.setHttpInfo(request.method, request.path);
|
|
1821
|
+
const rpcMethod = request?.body?.method;
|
|
1822
|
+
if (typeof rpcMethod === "string") collector.setRpcMethod(rpcMethod);
|
|
1823
|
+
}
|
|
1824
|
+
function recordRequestLogFailure(flowCtx) {
|
|
1825
|
+
const flowError = flowCtx.state?.["flowError"];
|
|
1826
|
+
if (!flowError) return;
|
|
1827
|
+
const { type, message, code, errorId } = flowFailureOf(flowError);
|
|
1828
|
+
currentRequestLog(flowCtx)?.setError({ type, message, code, error_id: errorId });
|
|
1829
|
+
}
|
|
1830
|
+
function recordRequestLogAuth(flowCtx) {
|
|
1831
|
+
const verifyResult = flowCtx.state?.["verifyResult"];
|
|
1832
|
+
const authorization = flowCtx.rawInput?.request?.headers?.["authorization"];
|
|
1833
|
+
const scheme = typeof authorization === "string" ? authorization.trim().split(/\s+/)[0].toLowerCase() : "";
|
|
1834
|
+
const sub = verifyResult?.authorization?.user?.sub;
|
|
1835
|
+
const anonymous = typeof sub !== "string" || isAnonymousSubject(sub);
|
|
1836
|
+
currentRequestLog(flowCtx)?.setAuthInfo(
|
|
1837
|
+
anonymous || !scheme ? "anonymous" : scheme,
|
|
1838
|
+
verifyResult?.kind === "authorized" && !anonymous
|
|
1839
|
+
);
|
|
1840
|
+
}
|
|
1841
|
+
async function completeRequestLog(flowCtx) {
|
|
1842
|
+
const collector = currentRequestLog(flowCtx);
|
|
1843
|
+
if (!collector || collector.isFinalized()) return;
|
|
1844
|
+
const statusCode = flowCtx.state?.["statusCode"] ?? flowCtx.rawInput?.response?.statusCode;
|
|
1845
|
+
if (typeof statusCode === "number") collector.setStatusCode(statusCode);
|
|
1846
|
+
await collector.finalize();
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
// libs/observability/src/telemetry/telemetry.accessor.ts
|
|
1850
|
+
import {
|
|
1851
|
+
SpanKind as SpanKind13,
|
|
1852
|
+
SpanStatusCode as SpanStatusCode3,
|
|
1853
|
+
trace as trace4
|
|
1854
|
+
} from "@opentelemetry/api";
|
|
1855
|
+
|
|
1713
1856
|
// libs/observability/src/telemetry/telemetry.counters.ts
|
|
1714
1857
|
import { metrics } from "@opentelemetry/api";
|
|
1715
1858
|
function snapshotKey(name, attributes) {
|
|
@@ -1825,14 +1968,14 @@ var TelemetrySpan = class {
|
|
|
1825
1968
|
/** Record an error on this span */
|
|
1826
1969
|
recordError(error) {
|
|
1827
1970
|
this.span.recordException(error);
|
|
1828
|
-
this.span.setStatus({ code:
|
|
1971
|
+
this.span.setStatus({ code: SpanStatusCode3.ERROR, message: error.message });
|
|
1829
1972
|
this.hasError = true;
|
|
1830
1973
|
return this;
|
|
1831
1974
|
}
|
|
1832
1975
|
/** End the span (preserves ERROR status if already set) */
|
|
1833
1976
|
end() {
|
|
1834
1977
|
if (!this.hasError) {
|
|
1835
|
-
this.span.setStatus({ code:
|
|
1978
|
+
this.span.setStatus({ code: SpanStatusCode3.OK });
|
|
1836
1979
|
}
|
|
1837
1980
|
this.span.end();
|
|
1838
1981
|
}
|
|
@@ -1840,7 +1983,7 @@ var TelemetrySpan = class {
|
|
|
1840
1983
|
endWithError(error) {
|
|
1841
1984
|
const msg = typeof error === "string" ? error : error.message;
|
|
1842
1985
|
if (error instanceof Error) this.span.recordException(error);
|
|
1843
|
-
this.span.setStatus({ code:
|
|
1986
|
+
this.span.setStatus({ code: SpanStatusCode3.ERROR, message: msg });
|
|
1844
1987
|
this.span.end();
|
|
1845
1988
|
}
|
|
1846
1989
|
/** Get the underlying OTel span (escape hatch) */
|
|
@@ -1887,7 +2030,7 @@ var TelemetryAccessor = class {
|
|
|
1887
2030
|
const span = this.tracer.startSpan(
|
|
1888
2031
|
name,
|
|
1889
2032
|
{
|
|
1890
|
-
kind:
|
|
2033
|
+
kind: SpanKind13.INTERNAL,
|
|
1891
2034
|
attributes: { ...this.baseAttributes, ...attributes }
|
|
1892
2035
|
},
|
|
1893
2036
|
this.getActiveContext()
|
|
@@ -1936,11 +2079,11 @@ var TelemetryAccessor = class {
|
|
|
1936
2079
|
} else {
|
|
1937
2080
|
const span = this.tracer.startSpan(
|
|
1938
2081
|
name,
|
|
1939
|
-
{ kind:
|
|
2082
|
+
{ kind: SpanKind13.INTERNAL, attributes: this.baseAttributes },
|
|
1940
2083
|
this.fallbackContext
|
|
1941
2084
|
);
|
|
1942
2085
|
if (attributes) span.addEvent(name, attributes);
|
|
1943
|
-
span.setStatus({ code:
|
|
2086
|
+
span.setStatus({ code: SpanStatusCode3.OK });
|
|
1944
2087
|
span.end();
|
|
1945
2088
|
}
|
|
1946
2089
|
}
|
|
@@ -1988,7 +2131,7 @@ var TelemetryAccessor = class {
|
|
|
1988
2131
|
};
|
|
1989
2132
|
|
|
1990
2133
|
// libs/observability/src/telemetry/telemetry.factory.ts
|
|
1991
|
-
import { SpanStatusCode as
|
|
2134
|
+
import { SpanStatusCode as SpanStatusCode4, trace as trace5 } from "@opentelemetry/api";
|
|
1992
2135
|
var TelemetryFactory = class {
|
|
1993
2136
|
constructor(tracerName = "@frontmcp/observability") {
|
|
1994
2137
|
this.tracer = trace5.getTracer(tracerName);
|
|
@@ -2079,14 +2222,43 @@ var DEFAULT_TRACING = {
|
|
|
2079
2222
|
var ObservabilityPlugin = class extends DynamicPlugin {
|
|
2080
2223
|
constructor(options = {}) {
|
|
2081
2224
|
super();
|
|
2225
|
+
/** Wraps each other hook the request runs: names it on the request's log, and gives it a span with `hookSpans`. */
|
|
2226
|
+
this.observeHook = async ({ flowName, stage, target, flowContext }, run) => {
|
|
2227
|
+
if (target === this) return run();
|
|
2228
|
+
if (this.requestLogsEnabled) currentRequestLog(flowContext)?.addHook(`${flowName}:${stage}`);
|
|
2229
|
+
if (!this.traces("hookSpans")) return run();
|
|
2230
|
+
const owner = target?.constructor?.name;
|
|
2231
|
+
return runInHookSpan(flowContext, { flowName, stage, owner }, run);
|
|
2232
|
+
};
|
|
2082
2233
|
this.options = resolveOptions(options);
|
|
2083
2234
|
this.tracingOpts = this.options.tracing !== false ? this.options.tracing : DEFAULT_TRACING;
|
|
2084
2235
|
}
|
|
2085
2236
|
get tracingEnabled() {
|
|
2086
2237
|
return this.options.tracing !== false;
|
|
2087
2238
|
}
|
|
2239
|
+
get requestLogsEnabled() {
|
|
2240
|
+
return this.options.requestLogs !== false;
|
|
2241
|
+
}
|
|
2242
|
+
/** Whether tracing is on and its `kind` option isn't off. */
|
|
2243
|
+
traces(kind) {
|
|
2244
|
+
return this.tracingEnabled && this.tracingOpts[kind] !== false;
|
|
2245
|
+
}
|
|
2246
|
+
/** Watches the hooks of the request `ctx` serves, from the first flow of it that this plugin sees. */
|
|
2247
|
+
watchHooks(ctx) {
|
|
2248
|
+
if (this.requestLogsEnabled || this.traces("hookSpans"))
|
|
2249
|
+
installHookObserver(ctx, this.observeHook);
|
|
2250
|
+
}
|
|
2251
|
+
/** Names the entry a request serves on its request log, and records the entry flow's failure. */
|
|
2252
|
+
annotateRequestLog(ctx, set) {
|
|
2253
|
+
if (!this.requestLogsEnabled) return;
|
|
2254
|
+
const flowCtx = ctx;
|
|
2255
|
+
const log = currentRequestLog(ctx);
|
|
2256
|
+
if (log && flowCtx.state?.input) set(log, flowCtx.state.input);
|
|
2257
|
+
}
|
|
2088
2258
|
_httpWillTrace(ctx) {
|
|
2089
2259
|
if (this.tracingEnabled) onHttpWillTrace(this.tracingOpts, ctx);
|
|
2260
|
+
if (this.options.requestLogs !== false) startRequestLog(ctx, this.options.requestLogs);
|
|
2261
|
+
this.watchHooks(ctx);
|
|
2090
2262
|
}
|
|
2091
2263
|
_httpWillQuota(ctx) {
|
|
2092
2264
|
if (this.tracingEnabled) onHttpWillAcquireQuota(ctx);
|
|
@@ -2099,6 +2271,7 @@ var ObservabilityPlugin = class extends DynamicPlugin {
|
|
|
2099
2271
|
}
|
|
2100
2272
|
_httpDidAuth(ctx) {
|
|
2101
2273
|
if (this.tracingEnabled) onHttpDidCheckAuth(ctx);
|
|
2274
|
+
if (this.requestLogsEnabled) recordRequestLogAuth(ctx);
|
|
2102
2275
|
}
|
|
2103
2276
|
_httpWillRoute(ctx) {
|
|
2104
2277
|
if (this.tracingEnabled) onHttpWillRoute(ctx);
|
|
@@ -2108,12 +2281,15 @@ var ObservabilityPlugin = class extends DynamicPlugin {
|
|
|
2108
2281
|
}
|
|
2109
2282
|
_httpDidFinalize(ctx) {
|
|
2110
2283
|
if (this.tracingEnabled) onHttpDidFinalize(ctx);
|
|
2284
|
+
if (this.requestLogsEnabled) void completeRequestLog(ctx);
|
|
2111
2285
|
}
|
|
2112
2286
|
_toolWillParse(ctx) {
|
|
2287
|
+
this.watchHooks(ctx);
|
|
2113
2288
|
if (this.tracingEnabled) onToolWillParse(this.tracingOpts, ctx);
|
|
2114
2289
|
}
|
|
2115
2290
|
_toolWillFind(ctx) {
|
|
2116
2291
|
if (this.tracingEnabled) onToolWillFindTool(ctx);
|
|
2292
|
+
this.annotateRequestLog(ctx, (log, input) => log.setToolName(String(input["name"])));
|
|
2117
2293
|
}
|
|
2118
2294
|
_toolWillCheckAuth(ctx) {
|
|
2119
2295
|
if (this.tracingEnabled) onToolWillCheckAuth(ctx);
|
|
@@ -2144,12 +2320,15 @@ var ObservabilityPlugin = class extends DynamicPlugin {
|
|
|
2144
2320
|
}
|
|
2145
2321
|
_toolDidFinalize(ctx) {
|
|
2146
2322
|
if (this.tracingEnabled) onToolDidFinalize(ctx);
|
|
2323
|
+
if (this.requestLogsEnabled) recordRequestLogFailure(ctx);
|
|
2147
2324
|
}
|
|
2148
2325
|
_resourceWillParse(ctx) {
|
|
2326
|
+
this.watchHooks(ctx);
|
|
2149
2327
|
if (this.tracingEnabled) onResourceWillParse(this.tracingOpts, ctx);
|
|
2150
2328
|
}
|
|
2151
2329
|
_resourceWillFind(ctx) {
|
|
2152
2330
|
if (this.tracingEnabled) onResourceWillFind(ctx);
|
|
2331
|
+
this.annotateRequestLog(ctx, (log, input) => log.setResourceUri(String(input["uri"])));
|
|
2153
2332
|
}
|
|
2154
2333
|
_resourceWillCheckAuthorities(ctx) {
|
|
2155
2334
|
if (this.tracingEnabled) onEntryWillCheckAuthorities(ctx);
|
|
@@ -2165,12 +2344,15 @@ var ObservabilityPlugin = class extends DynamicPlugin {
|
|
|
2165
2344
|
}
|
|
2166
2345
|
_resourceDidFinalize(ctx) {
|
|
2167
2346
|
if (this.tracingEnabled) onResourceDidFinalize(ctx);
|
|
2347
|
+
if (this.requestLogsEnabled) recordRequestLogFailure(ctx);
|
|
2168
2348
|
}
|
|
2169
2349
|
_promptWillParse(ctx) {
|
|
2350
|
+
this.watchHooks(ctx);
|
|
2170
2351
|
if (this.tracingEnabled) onPromptWillParse(this.tracingOpts, ctx);
|
|
2171
2352
|
}
|
|
2172
2353
|
_promptWillFind(ctx) {
|
|
2173
2354
|
if (this.tracingEnabled) onPromptWillFind(ctx);
|
|
2355
|
+
this.annotateRequestLog(ctx, (log, input) => log.setPromptName(String(input["name"])));
|
|
2174
2356
|
}
|
|
2175
2357
|
_promptWillCheckAuthorities(ctx) {
|
|
2176
2358
|
if (this.tracingEnabled) onEntryWillCheckAuthorities(ctx);
|
|
@@ -2186,8 +2368,10 @@ var ObservabilityPlugin = class extends DynamicPlugin {
|
|
|
2186
2368
|
}
|
|
2187
2369
|
_promptDidFinalize(ctx) {
|
|
2188
2370
|
if (this.tracingEnabled) onPromptDidFinalize(ctx);
|
|
2371
|
+
if (this.requestLogsEnabled) recordRequestLogFailure(ctx);
|
|
2189
2372
|
}
|
|
2190
2373
|
_agentWillParse(ctx) {
|
|
2374
|
+
this.watchHooks(ctx);
|
|
2191
2375
|
if (this.tracingEnabled) onAgentWillParse(this.tracingOpts, ctx);
|
|
2192
2376
|
}
|
|
2193
2377
|
_agentWillFind(ctx) {
|
|
@@ -2341,46 +2525,46 @@ var ObservabilityPlugin = class extends DynamicPlugin {
|
|
|
2341
2525
|
if (this.tracingEnabled) onAuthDidFinalize(ctx);
|
|
2342
2526
|
}
|
|
2343
2527
|
_oauthTokenWill(ctx) {
|
|
2344
|
-
if (this.
|
|
2528
|
+
if (this.traces("oauthSpans")) onGenericFlowWillStart("oauth/token", this.tracingOpts, ctx);
|
|
2345
2529
|
}
|
|
2346
2530
|
_oauthTokenDone(ctx) {
|
|
2347
|
-
if (this.
|
|
2531
|
+
if (this.traces("oauthSpans")) onGenericFlowDidFinalize(ctx);
|
|
2348
2532
|
}
|
|
2349
2533
|
_oauthAuthzWill(ctx) {
|
|
2350
|
-
if (this.
|
|
2534
|
+
if (this.traces("oauthSpans")) onGenericFlowWillStart("oauth/authorize", this.tracingOpts, ctx);
|
|
2351
2535
|
}
|
|
2352
2536
|
_oauthAuthzDone(ctx) {
|
|
2353
|
-
if (this.
|
|
2537
|
+
if (this.traces("oauthSpans")) onGenericFlowDidFinalize(ctx);
|
|
2354
2538
|
}
|
|
2355
2539
|
_oauthCallbackWill(ctx) {
|
|
2356
|
-
if (this.
|
|
2540
|
+
if (this.traces("oauthSpans")) onGenericFlowWillStart("oauth/callback", this.tracingOpts, ctx);
|
|
2357
2541
|
}
|
|
2358
2542
|
_oauthCallbackDone(ctx) {
|
|
2359
|
-
if (this.
|
|
2543
|
+
if (this.traces("oauthSpans")) onGenericFlowDidFinalize(ctx);
|
|
2360
2544
|
}
|
|
2361
2545
|
_oauthProviderWill(ctx) {
|
|
2362
|
-
if (this.
|
|
2546
|
+
if (this.traces("oauthSpans")) onGenericFlowWillStart("oauth/provider-callback", this.tracingOpts, ctx);
|
|
2363
2547
|
}
|
|
2364
2548
|
_oauthProviderDone(ctx) {
|
|
2365
|
-
if (this.
|
|
2549
|
+
if (this.traces("oauthSpans")) onGenericFlowDidFinalize(ctx);
|
|
2366
2550
|
}
|
|
2367
2551
|
_oauthRegisterWill(ctx) {
|
|
2368
|
-
if (this.
|
|
2552
|
+
if (this.traces("oauthSpans")) onGenericFlowWillStart("oauth/register", this.tracingOpts, ctx);
|
|
2369
2553
|
}
|
|
2370
2554
|
_oauthRegisterDone(ctx) {
|
|
2371
|
-
if (this.
|
|
2555
|
+
if (this.traces("oauthSpans")) onGenericFlowDidFinalize(ctx);
|
|
2372
2556
|
}
|
|
2373
2557
|
_elicitReqWill(ctx) {
|
|
2374
|
-
if (this.
|
|
2558
|
+
if (this.traces("elicitationSpans")) onGenericFlowWillStart("elicitation/request", this.tracingOpts, ctx);
|
|
2375
2559
|
}
|
|
2376
2560
|
_elicitReqDone(ctx) {
|
|
2377
|
-
if (this.
|
|
2561
|
+
if (this.traces("elicitationSpans")) onGenericFlowDidFinalize(ctx);
|
|
2378
2562
|
}
|
|
2379
2563
|
_elicitResWill(ctx) {
|
|
2380
|
-
if (this.
|
|
2564
|
+
if (this.traces("elicitationSpans")) onGenericFlowWillStart("elicitation/result", this.tracingOpts, ctx);
|
|
2381
2565
|
}
|
|
2382
2566
|
_elicitResDone(ctx) {
|
|
2383
|
-
if (this.
|
|
2567
|
+
if (this.traces("elicitationSpans")) onGenericFlowDidFinalize(ctx);
|
|
2384
2568
|
}
|
|
2385
2569
|
_subscribeWill(ctx) {
|
|
2386
2570
|
if (this.tracingEnabled) onGenericFlowWillStart("resources/subscribe", this.tracingOpts, ctx);
|
|
@@ -2446,12 +2630,10 @@ var ObservabilityPlugin = class extends DynamicPlugin {
|
|
|
2446
2630
|
// Startup Report (public method)
|
|
2447
2631
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
2448
2632
|
/**
|
|
2449
|
-
* Emit startup
|
|
2450
|
-
* Call after scope.initialize() completes with component counts.
|
|
2633
|
+
* Emit the `frontmcp.startup` span (`tracing.startupReport`). The scope calls it once it is ready.
|
|
2451
2634
|
*/
|
|
2452
2635
|
reportStartupTelemetry(data) {
|
|
2453
|
-
if (this.
|
|
2454
|
-
reportStartup(data);
|
|
2636
|
+
if (this.traces("startupReport")) reportStartup(data);
|
|
2455
2637
|
}
|
|
2456
2638
|
};
|
|
2457
2639
|
ObservabilityPlugin.defaultOptions = {
|
|
@@ -2480,7 +2662,7 @@ ObservabilityPlugin.dynamicProviders = (input) => {
|
|
|
2480
2662
|
name: "observability:telemetry-accessor",
|
|
2481
2663
|
provide: TELEMETRY_ACCESSOR,
|
|
2482
2664
|
scope: ProviderScope.CONTEXT,
|
|
2483
|
-
inject: () => [
|
|
2665
|
+
inject: () => [FRONTMCP_CONTEXT2],
|
|
2484
2666
|
useFactory: (ctx) => new TelemetryAccessor(ctx)
|
|
2485
2667
|
});
|
|
2486
2668
|
providers.push({
|
|
@@ -2513,7 +2695,7 @@ ObservabilityPlugin.dynamicProviders = (input) => {
|
|
|
2513
2695
|
name: "observability:request-log-collector",
|
|
2514
2696
|
provide: REQUEST_LOG_COLLECTOR,
|
|
2515
2697
|
scope: ProviderScope.CONTEXT,
|
|
2516
|
-
inject: () => [
|
|
2698
|
+
inject: () => [FRONTMCP_CONTEXT2],
|
|
2517
2699
|
useFactory: (ctx) => {
|
|
2518
2700
|
return new RequestLogCollector(
|
|
2519
2701
|
{
|
|
@@ -3018,35 +3200,8 @@ function setupOTel(options) {
|
|
|
3018
3200
|
}
|
|
3019
3201
|
}
|
|
3020
3202
|
|
|
3021
|
-
// libs/observability/src/otel/spans/hook.span.ts
|
|
3022
|
-
import { SpanKind as SpanKind13 } from "@opentelemetry/api";
|
|
3023
|
-
function recordHookEvent(parentSpan, stage, owner) {
|
|
3024
|
-
const attributes = {};
|
|
3025
|
-
if (owner) {
|
|
3026
|
-
attributes[FrontMcpAttributes.HOOK_OWNER] = owner;
|
|
3027
|
-
}
|
|
3028
|
-
parentSpan.addEvent(`hook.${stage}`, attributes);
|
|
3029
|
-
}
|
|
3030
|
-
function startHookSpan(tracer, options) {
|
|
3031
|
-
const attributes = {
|
|
3032
|
-
[FrontMcpAttributes.HOOK_STAGE]: options.stage
|
|
3033
|
-
};
|
|
3034
|
-
if (options.owner) {
|
|
3035
|
-
attributes[FrontMcpAttributes.HOOK_OWNER] = options.owner;
|
|
3036
|
-
}
|
|
3037
|
-
if (options.flowName) {
|
|
3038
|
-
attributes[FrontMcpAttributes.FLOW_NAME] = options.flowName;
|
|
3039
|
-
}
|
|
3040
|
-
return startSpan(tracer, {
|
|
3041
|
-
name: `hook ${options.stage}`,
|
|
3042
|
-
kind: SpanKind13.INTERNAL,
|
|
3043
|
-
attributes,
|
|
3044
|
-
parentContext: options.parentContext
|
|
3045
|
-
});
|
|
3046
|
-
}
|
|
3047
|
-
|
|
3048
3203
|
// libs/observability/src/otel/spans/pretty-span-exporter.ts
|
|
3049
|
-
import { SpanKind as SpanKind14, SpanStatusCode as
|
|
3204
|
+
import { SpanKind as SpanKind14, SpanStatusCode as SpanStatusCode5 } from "@opentelemetry/api";
|
|
3050
3205
|
var RESET = "\x1B[0m";
|
|
3051
3206
|
var DIM = "\x1B[2m";
|
|
3052
3207
|
var BOLD = "\x1B[1m";
|
|
@@ -3098,7 +3253,7 @@ var PrettySpanExporter = class {
|
|
|
3098
3253
|
const traceShort = span.spanContext().traceId.slice(0, 8);
|
|
3099
3254
|
const durationMs = (span.duration[0] * 1e3 + span.duration[1] / 1e6).toFixed(1);
|
|
3100
3255
|
const kind = KIND_LABELS[span.kind] ?? "UNKNOWN";
|
|
3101
|
-
const statusOk = span.status.code !==
|
|
3256
|
+
const statusOk = span.status.code !== SpanStatusCode5.ERROR;
|
|
3102
3257
|
const attrs = Object.entries(span.attributes).filter(([k]) => SHOW_ATTRS.has(k)).map(([k, v]) => {
|
|
3103
3258
|
const short = k.replace("frontmcp.", "").replace("mcp.", "");
|
|
3104
3259
|
return `${short}=${v}`;
|
|
@@ -3212,6 +3367,13 @@ function renderJsonExposition(counters, gauges = []) {
|
|
|
3212
3367
|
import { readdirSync } from "@frontmcp/utils";
|
|
3213
3368
|
var NS_PER_SECOND = 1e9;
|
|
3214
3369
|
var MICROS_PER_SECOND = 1e6;
|
|
3370
|
+
function probeSafely(probe) {
|
|
3371
|
+
try {
|
|
3372
|
+
return probe();
|
|
3373
|
+
} catch {
|
|
3374
|
+
return void 0;
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3215
3377
|
function defaultMonitorEventLoopDelay() {
|
|
3216
3378
|
try {
|
|
3217
3379
|
const { monitorEventLoopDelay } = __require("node:perf_hooks");
|
|
@@ -3257,54 +3419,70 @@ var ProcessStatsCollector = class {
|
|
|
3257
3419
|
this.getActiveHandles = init.getActiveHandles ?? defaultGetActiveHandles;
|
|
3258
3420
|
this.getActiveRequests = init.getActiveRequests ?? defaultGetActiveRequests;
|
|
3259
3421
|
this.readFdCount = init.readFdCount ?? defaultReadFdCount;
|
|
3260
|
-
this.cpuStart = this.cpuUsage();
|
|
3422
|
+
this.cpuStart = probeSafely(() => this.cpuUsage());
|
|
3261
3423
|
if (this.options.eventLoopLag !== false) {
|
|
3262
3424
|
this.histogram = init.monitorEventLoopDelay ? init.monitorEventLoopDelay() : defaultMonitorEventLoopDelay();
|
|
3263
3425
|
}
|
|
3264
3426
|
}
|
|
3265
3427
|
collect() {
|
|
3266
3428
|
const entries = [];
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3429
|
+
this.cpuStart ??= probeSafely(() => this.cpuUsage());
|
|
3430
|
+
const cpu = this.cpuStart && probeSafely(() => this.cpuUsage(this.cpuStart));
|
|
3431
|
+
if (cpu) {
|
|
3432
|
+
entries.push({
|
|
3433
|
+
name: "frontmcp_process_cpu_seconds_total",
|
|
3434
|
+
value: cpu.user / MICROS_PER_SECOND,
|
|
3435
|
+
attributes: { mode: "user" },
|
|
3436
|
+
help: "CPU time consumed since collector start, by mode (seconds)"
|
|
3437
|
+
});
|
|
3438
|
+
entries.push({
|
|
3439
|
+
name: "frontmcp_process_cpu_seconds_total",
|
|
3440
|
+
value: cpu.system / MICROS_PER_SECOND,
|
|
3441
|
+
attributes: { mode: "system" }
|
|
3442
|
+
});
|
|
3443
|
+
}
|
|
3444
|
+
const mem = probeSafely(() => this.memoryUsage());
|
|
3445
|
+
if (mem && mem.rss > 0) {
|
|
3446
|
+
entries.push({
|
|
3447
|
+
name: "frontmcp_process_resident_memory_bytes",
|
|
3448
|
+
value: mem.rss,
|
|
3449
|
+
help: "Resident memory size in bytes"
|
|
3450
|
+
});
|
|
3451
|
+
entries.push({
|
|
3452
|
+
name: "frontmcp_process_heap_bytes",
|
|
3453
|
+
value: mem.heapTotal,
|
|
3454
|
+
help: "Total V8 heap size in bytes"
|
|
3455
|
+
});
|
|
3456
|
+
entries.push({
|
|
3457
|
+
name: "frontmcp_process_heap_used_bytes",
|
|
3458
|
+
value: mem.heapUsed,
|
|
3459
|
+
help: "Used V8 heap size in bytes"
|
|
3460
|
+
});
|
|
3461
|
+
entries.push({
|
|
3462
|
+
name: "frontmcp_process_external_bytes",
|
|
3463
|
+
value: mem.external,
|
|
3464
|
+
help: "Memory used by C++ objects bound to JS in bytes"
|
|
3465
|
+
});
|
|
3466
|
+
}
|
|
3467
|
+
const uptime = probeSafely(() => this.uptime());
|
|
3468
|
+
if (uptime !== void 0 && uptime > 0) {
|
|
3469
|
+
entries.push({
|
|
3470
|
+
name: "frontmcp_process_uptime_seconds",
|
|
3471
|
+
value: uptime,
|
|
3472
|
+
help: "Time since process start in seconds"
|
|
3473
|
+
});
|
|
3474
|
+
}
|
|
3475
|
+
const histogram = this.options.eventLoopLag !== false ? this.histogram : void 0;
|
|
3476
|
+
const lag = histogram && probeSafely(() => {
|
|
3477
|
+
const sample = {
|
|
3478
|
+
meanSeconds: histogram.mean / NS_PER_SECOND,
|
|
3479
|
+
p99Seconds: histogram.percentile(99) / NS_PER_SECOND
|
|
3480
|
+
};
|
|
3481
|
+
histogram.reset();
|
|
3482
|
+
return sample;
|
|
3304
3483
|
});
|
|
3305
|
-
if (
|
|
3306
|
-
const meanSeconds
|
|
3307
|
-
const p99Seconds = this.histogram.percentile(99) / NS_PER_SECOND;
|
|
3484
|
+
if (lag) {
|
|
3485
|
+
const { meanSeconds, p99Seconds } = lag;
|
|
3308
3486
|
if (Number.isFinite(meanSeconds)) {
|
|
3309
3487
|
entries.push({
|
|
3310
3488
|
name: "frontmcp_nodejs_eventloop_lag_seconds",
|
|
@@ -3320,7 +3498,6 @@ var ProcessStatsCollector = class {
|
|
|
3320
3498
|
attributes: { quantile: "p99" }
|
|
3321
3499
|
});
|
|
3322
3500
|
}
|
|
3323
|
-
this.histogram.reset();
|
|
3324
3501
|
}
|
|
3325
3502
|
if (this.options.activeHandles !== false) {
|
|
3326
3503
|
const handles = this.getActiveHandles?.();
|