@frontmcp/observability 1.9.2 → 1.9.4
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 +224 -135
- package/esm/package.json +3 -3
- package/index.js +316 -228
- 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 +7 -2
- package/request-log/request-log.collector.d.ts +4 -0
- package/request-log/request-log.hooks.d.ts +9 -2
- package/request-log/request-log.types.d.ts +1 -1
package/esm/index.mjs
CHANGED
|
@@ -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,12 +779,17 @@ var RequestLogCollector = class {
|
|
|
772
779
|
}
|
|
773
780
|
};
|
|
774
781
|
|
|
782
|
+
// libs/observability/src/request-log/request-log.hooks.ts
|
|
783
|
+
import { isAnonymousSubject } from "@frontmcp/sdk";
|
|
784
|
+
|
|
775
785
|
// libs/observability/src/plugin/observability.hooks.ts
|
|
776
786
|
import {
|
|
777
787
|
context as otelContext3,
|
|
778
|
-
SpanKind as
|
|
788
|
+
SpanKind as SpanKind12,
|
|
789
|
+
SpanStatusCode as SpanStatusCode2,
|
|
779
790
|
trace as trace3
|
|
780
791
|
} from "@opentelemetry/api";
|
|
792
|
+
import { FlowControl, InternalMcpError, toMcpError } from "@frontmcp/sdk";
|
|
781
793
|
import { sha256Hex } from "@frontmcp/utils";
|
|
782
794
|
|
|
783
795
|
// libs/observability/src/otel/otel.types.ts
|
|
@@ -977,8 +989,35 @@ function setFetchResponseStatus(span, statusCode) {
|
|
|
977
989
|
span.setAttribute(HttpAttributes.STATUS_CODE, statusCode);
|
|
978
990
|
}
|
|
979
991
|
|
|
980
|
-
// libs/observability/src/otel/spans/
|
|
992
|
+
// libs/observability/src/otel/spans/hook.span.ts
|
|
981
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";
|
|
982
1021
|
function startHttpServerSpan(tracer, options) {
|
|
983
1022
|
const attributes = {
|
|
984
1023
|
[HttpAttributes.METHOD]: options.method.toUpperCase(),
|
|
@@ -1001,7 +1040,7 @@ function startHttpServerSpan(tracer, options) {
|
|
|
1001
1040
|
}
|
|
1002
1041
|
return startSpan(tracer, {
|
|
1003
1042
|
name: `${options.method.toUpperCase()} ${options.path}`,
|
|
1004
|
-
kind:
|
|
1043
|
+
kind: SpanKind5.SERVER,
|
|
1005
1044
|
attributes,
|
|
1006
1045
|
parentContext: options.parentContext
|
|
1007
1046
|
});
|
|
@@ -1011,11 +1050,11 @@ function setHttpResponseStatus(span, statusCode) {
|
|
|
1011
1050
|
}
|
|
1012
1051
|
|
|
1013
1052
|
// libs/observability/src/otel/spans/prompt.span.ts
|
|
1014
|
-
import { SpanKind as
|
|
1053
|
+
import { SpanKind as SpanKind6 } from "@opentelemetry/api";
|
|
1015
1054
|
function startPromptSpan(tracer, options) {
|
|
1016
1055
|
return startSpan(tracer, {
|
|
1017
1056
|
name: `prompt ${options.name}`,
|
|
1018
|
-
kind:
|
|
1057
|
+
kind: SpanKind6.INTERNAL,
|
|
1019
1058
|
attributes: {
|
|
1020
1059
|
[FrontMcpAttributes.PROMPT_NAME]: options.name,
|
|
1021
1060
|
[McpAttributes.COMPONENT_TYPE]: "prompt",
|
|
@@ -1026,7 +1065,7 @@ function startPromptSpan(tracer, options) {
|
|
|
1026
1065
|
}
|
|
1027
1066
|
|
|
1028
1067
|
// libs/observability/src/otel/spans/resource.span.ts
|
|
1029
|
-
import { SpanKind as
|
|
1068
|
+
import { SpanKind as SpanKind7 } from "@opentelemetry/api";
|
|
1030
1069
|
function startResourceSpan(tracer, options) {
|
|
1031
1070
|
const attributes = {
|
|
1032
1071
|
[FrontMcpAttributes.RESOURCE_URI]: options.uri,
|
|
@@ -1039,14 +1078,14 @@ function startResourceSpan(tracer, options) {
|
|
|
1039
1078
|
}
|
|
1040
1079
|
return startSpan(tracer, {
|
|
1041
1080
|
name: `resource ${options.uri}`,
|
|
1042
|
-
kind:
|
|
1081
|
+
kind: SpanKind7.INTERNAL,
|
|
1043
1082
|
attributes,
|
|
1044
1083
|
parentContext: options.parentContext
|
|
1045
1084
|
});
|
|
1046
1085
|
}
|
|
1047
1086
|
|
|
1048
1087
|
// libs/observability/src/otel/spans/rpc.span.ts
|
|
1049
|
-
import { SpanKind as
|
|
1088
|
+
import { SpanKind as SpanKind8 } from "@opentelemetry/api";
|
|
1050
1089
|
function startRpcSpan(tracer, options) {
|
|
1051
1090
|
const attributes = {
|
|
1052
1091
|
[RpcAttributes.SYSTEM]: "mcp",
|
|
@@ -1070,14 +1109,14 @@ function startRpcSpan(tracer, options) {
|
|
|
1070
1109
|
}
|
|
1071
1110
|
return startSpan(tracer, {
|
|
1072
1111
|
name: options.method,
|
|
1073
|
-
kind:
|
|
1112
|
+
kind: SpanKind8.SERVER,
|
|
1074
1113
|
attributes,
|
|
1075
1114
|
parentContext: options.parentContext
|
|
1076
1115
|
});
|
|
1077
1116
|
}
|
|
1078
1117
|
|
|
1079
1118
|
// libs/observability/src/otel/spans/startup.span.ts
|
|
1080
|
-
import { SpanKind as
|
|
1119
|
+
import { SpanKind as SpanKind9 } from "@opentelemetry/api";
|
|
1081
1120
|
function emitStartupReport(tracer, data) {
|
|
1082
1121
|
const attributes = {
|
|
1083
1122
|
[FrontMcpAttributes.STARTUP_TOOLS_COUNT]: data.toolsCount,
|
|
@@ -1091,14 +1130,14 @@ function emitStartupReport(tracer, data) {
|
|
|
1091
1130
|
}
|
|
1092
1131
|
const { span } = startSpan(tracer, {
|
|
1093
1132
|
name: "frontmcp.startup",
|
|
1094
|
-
kind:
|
|
1133
|
+
kind: SpanKind9.INTERNAL,
|
|
1095
1134
|
attributes
|
|
1096
1135
|
});
|
|
1097
1136
|
endSpanOk(span);
|
|
1098
1137
|
}
|
|
1099
1138
|
|
|
1100
1139
|
// libs/observability/src/otel/spans/tool.span.ts
|
|
1101
|
-
import { SpanKind as
|
|
1140
|
+
import { SpanKind as SpanKind10 } from "@opentelemetry/api";
|
|
1102
1141
|
function startToolSpan(tracer, options) {
|
|
1103
1142
|
const attributes = {
|
|
1104
1143
|
[FrontMcpAttributes.TOOL_NAME]: options.name,
|
|
@@ -1116,14 +1155,14 @@ function startToolSpan(tracer, options) {
|
|
|
1116
1155
|
}
|
|
1117
1156
|
return startSpan(tracer, {
|
|
1118
1157
|
name: `tool ${options.name}`,
|
|
1119
|
-
kind:
|
|
1158
|
+
kind: SpanKind10.INTERNAL,
|
|
1120
1159
|
attributes,
|
|
1121
1160
|
parentContext: options.parentContext
|
|
1122
1161
|
});
|
|
1123
1162
|
}
|
|
1124
1163
|
|
|
1125
1164
|
// libs/observability/src/otel/spans/transport.span.ts
|
|
1126
|
-
import { SpanKind as
|
|
1165
|
+
import { SpanKind as SpanKind11 } from "@opentelemetry/api";
|
|
1127
1166
|
function startTransportSpan(tracer, options) {
|
|
1128
1167
|
const attributes = {
|
|
1129
1168
|
[FrontMcpAttributes.TRANSPORT_TYPE]: options.type
|
|
@@ -1136,7 +1175,7 @@ function startTransportSpan(tracer, options) {
|
|
|
1136
1175
|
}
|
|
1137
1176
|
return startSpan(tracer, {
|
|
1138
1177
|
name: `transport ${options.type}`,
|
|
1139
|
-
kind:
|
|
1178
|
+
kind: SpanKind11.INTERNAL,
|
|
1140
1179
|
attributes,
|
|
1141
1180
|
parentContext: options.parentContext
|
|
1142
1181
|
});
|
|
@@ -1181,6 +1220,8 @@ var EXEC_SPAN_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:exec-span");
|
|
|
1181
1220
|
var ACTIVE_SPAN_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:active-span");
|
|
1182
1221
|
var ACTIVE_OTEL_CTX_KEY = /* @__PURE__ */ Symbol.for("frontmcp:otel:active-otel-ctx");
|
|
1183
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");
|
|
1184
1225
|
function sessionTracingId(sessionId) {
|
|
1185
1226
|
return sha256Hex(sessionId).slice(0, 16);
|
|
1186
1227
|
}
|
|
@@ -1215,7 +1256,44 @@ function clearActiveSpan(flowCtx) {
|
|
|
1215
1256
|
} catch {
|
|
1216
1257
|
}
|
|
1217
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
|
+
}
|
|
1218
1295
|
function recordStageEvent(span, stageName, state) {
|
|
1296
|
+
if (state[STAGE_EVENTS_KEY] === false) return;
|
|
1219
1297
|
const now = Date.now();
|
|
1220
1298
|
const prev = state[STAGE_TS_KEY];
|
|
1221
1299
|
const attributes = {};
|
|
@@ -1241,9 +1319,8 @@ function onHttpWillTrace(options, flowCtx) {
|
|
|
1241
1319
|
});
|
|
1242
1320
|
span.setAttribute(McpAttributes.SESSION_ID, sessionHash);
|
|
1243
1321
|
span.setAttribute(FrontMcpAttributes.SESSION_ID_HASH, sessionHash);
|
|
1244
|
-
|
|
1245
|
-
flowCtx
|
|
1246
|
-
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1322
|
+
ctx.set?.(HTTP_SPAN_CTX_KEY, spanCtx);
|
|
1323
|
+
beginFlowSpan(flowCtx, options, span, spanCtx);
|
|
1247
1324
|
recordStageEvent(span, "traceRequest", flowCtx.state);
|
|
1248
1325
|
}
|
|
1249
1326
|
function onHttpWillAcquireQuota(flowCtx) {
|
|
@@ -1287,7 +1364,7 @@ function onToolWillParse(options, flowCtx) {
|
|
|
1287
1364
|
const ctx = extractContext(flowCtx);
|
|
1288
1365
|
if (!ctx) return;
|
|
1289
1366
|
const tracer = getTracer();
|
|
1290
|
-
const parentOTelCtx =
|
|
1367
|
+
const parentOTelCtx = parentContextOf(ctx);
|
|
1291
1368
|
const sessionHash = sessionTracingId(ctx.sessionId);
|
|
1292
1369
|
const { span, context: spanCtx } = startRpcSpan(tracer, {
|
|
1293
1370
|
method: "tools/call",
|
|
@@ -1295,9 +1372,7 @@ function onToolWillParse(options, flowCtx) {
|
|
|
1295
1372
|
sessionIdHash: sessionHash,
|
|
1296
1373
|
parentContext: parentOTelCtx
|
|
1297
1374
|
});
|
|
1298
|
-
flowCtx
|
|
1299
|
-
flowCtx.state[SPAN_CTX_KEY] = spanCtx;
|
|
1300
|
-
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1375
|
+
beginFlowSpan(flowCtx, options, span, spanCtx);
|
|
1301
1376
|
recordStageEvent(span, "parseInput", flowCtx.state);
|
|
1302
1377
|
}
|
|
1303
1378
|
function onToolWillFindTool(flowCtx) {
|
|
@@ -1367,8 +1442,8 @@ function onToolDidFinalize(flowCtx) {
|
|
|
1367
1442
|
const toolSpan = flowCtx.state?.[EXEC_SPAN_KEY];
|
|
1368
1443
|
const error = flowCtx.state?.flowError;
|
|
1369
1444
|
if (error) {
|
|
1370
|
-
if (toolSpan)
|
|
1371
|
-
if (rpcSpan)
|
|
1445
|
+
if (toolSpan) endFailedSpan(toolSpan, error);
|
|
1446
|
+
if (rpcSpan) endFailedSpan(rpcSpan, error);
|
|
1372
1447
|
} else if (rpcSpan) {
|
|
1373
1448
|
recordStageEvent(rpcSpan, "finalize", flowCtx.state);
|
|
1374
1449
|
endSpanOk(rpcSpan);
|
|
@@ -1379,7 +1454,7 @@ function onResourceWillParse(options, flowCtx) {
|
|
|
1379
1454
|
const ctx = extractContext(flowCtx);
|
|
1380
1455
|
if (!ctx) return;
|
|
1381
1456
|
const tracer = getTracer();
|
|
1382
|
-
const parentOTelCtx =
|
|
1457
|
+
const parentOTelCtx = parentContextOf(ctx);
|
|
1383
1458
|
const sessionHash = sessionTracingId(ctx.sessionId);
|
|
1384
1459
|
const { span, context: spanCtx } = startRpcSpan(tracer, {
|
|
1385
1460
|
method: "resources/read",
|
|
@@ -1387,9 +1462,7 @@ function onResourceWillParse(options, flowCtx) {
|
|
|
1387
1462
|
sessionIdHash: sessionHash,
|
|
1388
1463
|
parentContext: parentOTelCtx
|
|
1389
1464
|
});
|
|
1390
|
-
flowCtx
|
|
1391
|
-
flowCtx.state[SPAN_CTX_KEY] = spanCtx;
|
|
1392
|
-
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1465
|
+
beginFlowSpan(flowCtx, options, span, spanCtx);
|
|
1393
1466
|
recordStageEvent(span, "parseInput", flowCtx.state);
|
|
1394
1467
|
}
|
|
1395
1468
|
function onResourceWillFind(flowCtx) {
|
|
@@ -1420,8 +1493,8 @@ function onResourceDidFinalize(flowCtx) {
|
|
|
1420
1493
|
const resSpan = flowCtx.state?.[EXEC_SPAN_KEY];
|
|
1421
1494
|
const error = flowCtx.state?.flowError;
|
|
1422
1495
|
if (error) {
|
|
1423
|
-
if (resSpan)
|
|
1424
|
-
if (rpcSpan)
|
|
1496
|
+
if (resSpan) endFailedSpan(resSpan, error);
|
|
1497
|
+
if (rpcSpan) endFailedSpan(rpcSpan, error);
|
|
1425
1498
|
} else if (rpcSpan) {
|
|
1426
1499
|
recordStageEvent(rpcSpan, "finalize", flowCtx.state);
|
|
1427
1500
|
endSpanOk(rpcSpan);
|
|
@@ -1432,7 +1505,7 @@ function onPromptWillParse(options, flowCtx) {
|
|
|
1432
1505
|
const ctx = extractContext(flowCtx);
|
|
1433
1506
|
if (!ctx) return;
|
|
1434
1507
|
const tracer = getTracer();
|
|
1435
|
-
const parentOTelCtx =
|
|
1508
|
+
const parentOTelCtx = parentContextOf(ctx);
|
|
1436
1509
|
const sessionHash = sessionTracingId(ctx.sessionId);
|
|
1437
1510
|
const { span, context: spanCtx } = startRpcSpan(tracer, {
|
|
1438
1511
|
method: "prompts/get",
|
|
@@ -1440,9 +1513,7 @@ function onPromptWillParse(options, flowCtx) {
|
|
|
1440
1513
|
sessionIdHash: sessionHash,
|
|
1441
1514
|
parentContext: parentOTelCtx
|
|
1442
1515
|
});
|
|
1443
|
-
flowCtx
|
|
1444
|
-
flowCtx.state[SPAN_CTX_KEY] = spanCtx;
|
|
1445
|
-
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1516
|
+
beginFlowSpan(flowCtx, options, span, spanCtx);
|
|
1446
1517
|
recordStageEvent(span, "parseInput", flowCtx.state);
|
|
1447
1518
|
}
|
|
1448
1519
|
function onPromptWillFind(flowCtx) {
|
|
@@ -1476,8 +1547,8 @@ function onPromptDidFinalize(flowCtx) {
|
|
|
1476
1547
|
const promptSpan = flowCtx.state?.[EXEC_SPAN_KEY];
|
|
1477
1548
|
const error = flowCtx.state?.flowError;
|
|
1478
1549
|
if (error) {
|
|
1479
|
-
if (promptSpan)
|
|
1480
|
-
if (rpcSpan)
|
|
1550
|
+
if (promptSpan) endFailedSpan(promptSpan, error);
|
|
1551
|
+
if (rpcSpan) endFailedSpan(rpcSpan, error);
|
|
1481
1552
|
} else if (rpcSpan) {
|
|
1482
1553
|
recordStageEvent(rpcSpan, "finalize", flowCtx.state);
|
|
1483
1554
|
endSpanOk(rpcSpan);
|
|
@@ -1488,7 +1559,7 @@ function onAgentWillParse(options, flowCtx) {
|
|
|
1488
1559
|
const ctx = extractContext(flowCtx);
|
|
1489
1560
|
if (!ctx) return;
|
|
1490
1561
|
const tracer = getTracer();
|
|
1491
|
-
const parentOTelCtx =
|
|
1562
|
+
const parentOTelCtx = parentContextOf(ctx);
|
|
1492
1563
|
const sessionHash = sessionTracingId(ctx.sessionId);
|
|
1493
1564
|
const { span, context: spanCtx } = startRpcSpan(tracer, {
|
|
1494
1565
|
method: "agents/call",
|
|
@@ -1496,9 +1567,7 @@ function onAgentWillParse(options, flowCtx) {
|
|
|
1496
1567
|
sessionIdHash: sessionHash,
|
|
1497
1568
|
parentContext: parentOTelCtx
|
|
1498
1569
|
});
|
|
1499
|
-
flowCtx
|
|
1500
|
-
flowCtx.state[SPAN_CTX_KEY] = spanCtx;
|
|
1501
|
-
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1570
|
+
beginFlowSpan(flowCtx, options, span, spanCtx);
|
|
1502
1571
|
recordStageEvent(span, "parseInput", flowCtx.state);
|
|
1503
1572
|
}
|
|
1504
1573
|
function onAgentWillFind(flowCtx) {
|
|
@@ -1512,7 +1581,7 @@ function onAgentWillExecute(options, flowCtx) {
|
|
|
1512
1581
|
const agentName = flowCtx.state?.input?.name ?? "unknown";
|
|
1513
1582
|
const { span: agentSpan } = startSpan(tracer, {
|
|
1514
1583
|
name: `agent ${agentName}`,
|
|
1515
|
-
kind:
|
|
1584
|
+
kind: SpanKind12.INTERNAL,
|
|
1516
1585
|
attributes: {
|
|
1517
1586
|
[FrontMcpAttributes.FLOW_NAME]: "agents:call-agent",
|
|
1518
1587
|
[McpAttributes.COMPONENT_TYPE]: "agent",
|
|
@@ -1539,8 +1608,8 @@ function onAgentDidFinalize(flowCtx) {
|
|
|
1539
1608
|
const agentSpan = flowCtx.state?.[EXEC_SPAN_KEY];
|
|
1540
1609
|
const error = flowCtx.state?.flowError;
|
|
1541
1610
|
if (error) {
|
|
1542
|
-
if (agentSpan)
|
|
1543
|
-
if (rpcSpan)
|
|
1611
|
+
if (agentSpan) endFailedSpan(agentSpan, error);
|
|
1612
|
+
if (rpcSpan) endFailedSpan(rpcSpan, error);
|
|
1544
1613
|
} else if (rpcSpan) {
|
|
1545
1614
|
recordStageEvent(rpcSpan, "finalize", flowCtx.state);
|
|
1546
1615
|
endSpanOk(rpcSpan);
|
|
@@ -1551,11 +1620,11 @@ function onGenericFlowWillStart(flowName, options, flowCtx) {
|
|
|
1551
1620
|
const ctx = extractContext(flowCtx);
|
|
1552
1621
|
if (!ctx) return;
|
|
1553
1622
|
const tracer = getTracer();
|
|
1554
|
-
const parentOTelCtx =
|
|
1623
|
+
const parentOTelCtx = parentContextOf(ctx);
|
|
1555
1624
|
const sessionHash = sessionTracingId(ctx.sessionId);
|
|
1556
1625
|
const { span, context: spanCtx } = startSpan(tracer, {
|
|
1557
1626
|
name: flowName,
|
|
1558
|
-
kind:
|
|
1627
|
+
kind: SpanKind12.INTERNAL,
|
|
1559
1628
|
attributes: {
|
|
1560
1629
|
[FrontMcpAttributes.FLOW_NAME]: flowName,
|
|
1561
1630
|
[McpAttributes.SESSION_ID]: sessionHash,
|
|
@@ -1565,9 +1634,7 @@ function onGenericFlowWillStart(flowName, options, flowCtx) {
|
|
|
1565
1634
|
},
|
|
1566
1635
|
parentContext: parentOTelCtx
|
|
1567
1636
|
});
|
|
1568
|
-
flowCtx
|
|
1569
|
-
flowCtx.state[SPAN_CTX_KEY] = spanCtx;
|
|
1570
|
-
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1637
|
+
beginFlowSpan(flowCtx, options, span, spanCtx);
|
|
1571
1638
|
}
|
|
1572
1639
|
function onGenericFlowStage(stageName, flowCtx) {
|
|
1573
1640
|
const span = flowCtx.state?.[SPAN_KEY];
|
|
@@ -1578,7 +1645,7 @@ function onGenericFlowDidFinalize(flowCtx) {
|
|
|
1578
1645
|
if (!span) return;
|
|
1579
1646
|
const error = flowCtx.state?.flowError;
|
|
1580
1647
|
if (error) {
|
|
1581
|
-
|
|
1648
|
+
endFailedSpan(span, error);
|
|
1582
1649
|
} else {
|
|
1583
1650
|
endSpanOk(span);
|
|
1584
1651
|
}
|
|
@@ -1588,16 +1655,14 @@ function onTransportWillStart(transportType, options, flowCtx) {
|
|
|
1588
1655
|
const ctx = extractContext(flowCtx);
|
|
1589
1656
|
if (!ctx) return;
|
|
1590
1657
|
const tracer = getTracer();
|
|
1591
|
-
const parentOTelCtx =
|
|
1658
|
+
const parentOTelCtx = parentContextOf(ctx);
|
|
1592
1659
|
const sessionHash = sessionTracingId(ctx.sessionId);
|
|
1593
1660
|
const { span, context: spanCtx } = startTransportSpan(tracer, {
|
|
1594
1661
|
type: transportType,
|
|
1595
1662
|
sessionIdHash: sessionHash,
|
|
1596
1663
|
parentContext: parentOTelCtx
|
|
1597
1664
|
});
|
|
1598
|
-
flowCtx
|
|
1599
|
-
flowCtx.state[SPAN_CTX_KEY] = spanCtx;
|
|
1600
|
-
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1665
|
+
beginFlowSpan(flowCtx, options, span, spanCtx);
|
|
1601
1666
|
recordStageEvent(span, "parseInput", flowCtx.state);
|
|
1602
1667
|
}
|
|
1603
1668
|
function onTransportDidRoute(flowCtx) {
|
|
@@ -1617,7 +1682,7 @@ function onTransportDidFinalize(flowCtx) {
|
|
|
1617
1682
|
recordStageEvent(span, "cleanup", flowCtx.state);
|
|
1618
1683
|
const error = flowCtx.state?.flowError;
|
|
1619
1684
|
if (error) {
|
|
1620
|
-
|
|
1685
|
+
endFailedSpan(span, error);
|
|
1621
1686
|
} else {
|
|
1622
1687
|
endSpanOk(span);
|
|
1623
1688
|
}
|
|
@@ -1627,14 +1692,12 @@ function onAuthWillStart(flowName, options, flowCtx) {
|
|
|
1627
1692
|
const ctx = extractContext(flowCtx);
|
|
1628
1693
|
if (!ctx) return;
|
|
1629
1694
|
const tracer = getTracer();
|
|
1630
|
-
const parentOTelCtx =
|
|
1695
|
+
const parentOTelCtx = parentContextOf(ctx);
|
|
1631
1696
|
const { span, context: spanCtx } = startAuthSpan(tracer, {
|
|
1632
1697
|
flowName,
|
|
1633
1698
|
parentContext: parentOTelCtx
|
|
1634
1699
|
});
|
|
1635
|
-
flowCtx
|
|
1636
|
-
flowCtx.state[SPAN_CTX_KEY] = spanCtx;
|
|
1637
|
-
flowCtx.state[STAGE_TS_KEY] = Date.now();
|
|
1700
|
+
beginFlowSpan(flowCtx, options, span, spanCtx);
|
|
1638
1701
|
recordStageEvent(span, "parseInput", flowCtx.state);
|
|
1639
1702
|
}
|
|
1640
1703
|
function onAuthDidDetermineMode(flowCtx) {
|
|
@@ -1654,30 +1717,35 @@ function onAuthDidFinalize(flowCtx) {
|
|
|
1654
1717
|
const error = flowCtx.state?.flowError;
|
|
1655
1718
|
if (error) {
|
|
1656
1719
|
setAuthResult(span, "unauthorized");
|
|
1657
|
-
|
|
1720
|
+
endFailedSpan(span, error);
|
|
1658
1721
|
} else {
|
|
1659
1722
|
setAuthResult(span, "authorized");
|
|
1660
1723
|
endSpanOk(span);
|
|
1661
1724
|
}
|
|
1662
1725
|
}
|
|
1663
1726
|
function wrapContextFetch(options, flowCtx) {
|
|
1664
|
-
if (options.fetchSpans === false) return;
|
|
1665
1727
|
const toolCtx = flowCtx.state?.toolContext ?? flowCtx.state?.toolCallContext;
|
|
1666
1728
|
if (!toolCtx || typeof toolCtx.fetch !== "function") return;
|
|
1667
1729
|
const originalFetch = toolCtx.fetch.bind(toolCtx);
|
|
1668
1730
|
const parentOTelCtx = flowCtx.state?.[SPAN_CTX_KEY];
|
|
1669
1731
|
toolCtx.fetch = async (input, init) => {
|
|
1670
|
-
const
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
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));
|
|
1674
1740
|
try {
|
|
1675
|
-
const response = await originalFetch(input, init);
|
|
1676
|
-
|
|
1677
|
-
|
|
1741
|
+
const response = await originalFetch(input, { ...init, headers });
|
|
1742
|
+
if (fetchSpan) {
|
|
1743
|
+
setFetchResponseStatus(fetchSpan, response.status);
|
|
1744
|
+
endSpanOk(fetchSpan);
|
|
1745
|
+
}
|
|
1678
1746
|
return response;
|
|
1679
1747
|
} catch (err) {
|
|
1680
|
-
endSpanError(
|
|
1748
|
+
if (fetchSpan) endSpanError(fetchSpan, err instanceof Error ? err : String(err));
|
|
1681
1749
|
throw err;
|
|
1682
1750
|
}
|
|
1683
1751
|
};
|
|
@@ -1695,6 +1763,26 @@ function onAgentDidExecuteEnrich(flowCtx) {
|
|
|
1695
1763
|
}
|
|
1696
1764
|
}
|
|
1697
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
|
+
}
|
|
1698
1786
|
function reportStartup(data) {
|
|
1699
1787
|
const tracer = getTracer();
|
|
1700
1788
|
emitStartupReport(tracer, data);
|
|
@@ -1734,27 +1822,34 @@ function startRequestLog(flowCtx, options) {
|
|
|
1734
1822
|
if (typeof rpcMethod === "string") collector.setRpcMethod(rpcMethod);
|
|
1735
1823
|
}
|
|
1736
1824
|
function recordRequestLogFailure(flowCtx) {
|
|
1737
|
-
const
|
|
1738
|
-
if (!
|
|
1739
|
-
const
|
|
1740
|
-
currentRequestLog(flowCtx)?.setError({
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
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
|
+
);
|
|
1745
1840
|
}
|
|
1746
1841
|
async function completeRequestLog(flowCtx) {
|
|
1747
1842
|
const collector = currentRequestLog(flowCtx);
|
|
1748
1843
|
if (!collector || collector.isFinalized()) return;
|
|
1749
1844
|
const statusCode = flowCtx.state?.["statusCode"] ?? flowCtx.rawInput?.response?.statusCode;
|
|
1750
|
-
if (typeof statusCode === "number"
|
|
1845
|
+
if (typeof statusCode === "number") collector.setStatusCode(statusCode);
|
|
1751
1846
|
await collector.finalize();
|
|
1752
1847
|
}
|
|
1753
1848
|
|
|
1754
1849
|
// libs/observability/src/telemetry/telemetry.accessor.ts
|
|
1755
1850
|
import {
|
|
1756
|
-
SpanKind as
|
|
1757
|
-
SpanStatusCode as
|
|
1851
|
+
SpanKind as SpanKind13,
|
|
1852
|
+
SpanStatusCode as SpanStatusCode3,
|
|
1758
1853
|
trace as trace4
|
|
1759
1854
|
} from "@opentelemetry/api";
|
|
1760
1855
|
|
|
@@ -1873,14 +1968,14 @@ var TelemetrySpan = class {
|
|
|
1873
1968
|
/** Record an error on this span */
|
|
1874
1969
|
recordError(error) {
|
|
1875
1970
|
this.span.recordException(error);
|
|
1876
|
-
this.span.setStatus({ code:
|
|
1971
|
+
this.span.setStatus({ code: SpanStatusCode3.ERROR, message: error.message });
|
|
1877
1972
|
this.hasError = true;
|
|
1878
1973
|
return this;
|
|
1879
1974
|
}
|
|
1880
1975
|
/** End the span (preserves ERROR status if already set) */
|
|
1881
1976
|
end() {
|
|
1882
1977
|
if (!this.hasError) {
|
|
1883
|
-
this.span.setStatus({ code:
|
|
1978
|
+
this.span.setStatus({ code: SpanStatusCode3.OK });
|
|
1884
1979
|
}
|
|
1885
1980
|
this.span.end();
|
|
1886
1981
|
}
|
|
@@ -1888,7 +1983,7 @@ var TelemetrySpan = class {
|
|
|
1888
1983
|
endWithError(error) {
|
|
1889
1984
|
const msg = typeof error === "string" ? error : error.message;
|
|
1890
1985
|
if (error instanceof Error) this.span.recordException(error);
|
|
1891
|
-
this.span.setStatus({ code:
|
|
1986
|
+
this.span.setStatus({ code: SpanStatusCode3.ERROR, message: msg });
|
|
1892
1987
|
this.span.end();
|
|
1893
1988
|
}
|
|
1894
1989
|
/** Get the underlying OTel span (escape hatch) */
|
|
@@ -1935,7 +2030,7 @@ var TelemetryAccessor = class {
|
|
|
1935
2030
|
const span = this.tracer.startSpan(
|
|
1936
2031
|
name,
|
|
1937
2032
|
{
|
|
1938
|
-
kind:
|
|
2033
|
+
kind: SpanKind13.INTERNAL,
|
|
1939
2034
|
attributes: { ...this.baseAttributes, ...attributes }
|
|
1940
2035
|
},
|
|
1941
2036
|
this.getActiveContext()
|
|
@@ -1984,11 +2079,11 @@ var TelemetryAccessor = class {
|
|
|
1984
2079
|
} else {
|
|
1985
2080
|
const span = this.tracer.startSpan(
|
|
1986
2081
|
name,
|
|
1987
|
-
{ kind:
|
|
2082
|
+
{ kind: SpanKind13.INTERNAL, attributes: this.baseAttributes },
|
|
1988
2083
|
this.fallbackContext
|
|
1989
2084
|
);
|
|
1990
2085
|
if (attributes) span.addEvent(name, attributes);
|
|
1991
|
-
span.setStatus({ code:
|
|
2086
|
+
span.setStatus({ code: SpanStatusCode3.OK });
|
|
1992
2087
|
span.end();
|
|
1993
2088
|
}
|
|
1994
2089
|
}
|
|
@@ -2036,7 +2131,7 @@ var TelemetryAccessor = class {
|
|
|
2036
2131
|
};
|
|
2037
2132
|
|
|
2038
2133
|
// libs/observability/src/telemetry/telemetry.factory.ts
|
|
2039
|
-
import { SpanStatusCode as
|
|
2134
|
+
import { SpanStatusCode as SpanStatusCode4, trace as trace5 } from "@opentelemetry/api";
|
|
2040
2135
|
var TelemetryFactory = class {
|
|
2041
2136
|
constructor(tracerName = "@frontmcp/observability") {
|
|
2042
2137
|
this.tracer = trace5.getTracer(tracerName);
|
|
@@ -2127,6 +2222,14 @@ var DEFAULT_TRACING = {
|
|
|
2127
2222
|
var ObservabilityPlugin = class extends DynamicPlugin {
|
|
2128
2223
|
constructor(options = {}) {
|
|
2129
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
|
+
};
|
|
2130
2233
|
this.options = resolveOptions(options);
|
|
2131
2234
|
this.tracingOpts = this.options.tracing !== false ? this.options.tracing : DEFAULT_TRACING;
|
|
2132
2235
|
}
|
|
@@ -2136,6 +2239,15 @@ var ObservabilityPlugin = class extends DynamicPlugin {
|
|
|
2136
2239
|
get requestLogsEnabled() {
|
|
2137
2240
|
return this.options.requestLogs !== false;
|
|
2138
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
|
+
}
|
|
2139
2251
|
/** Names the entry a request serves on its request log, and records the entry flow's failure. */
|
|
2140
2252
|
annotateRequestLog(ctx, set) {
|
|
2141
2253
|
if (!this.requestLogsEnabled) return;
|
|
@@ -2146,6 +2258,7 @@ var ObservabilityPlugin = class extends DynamicPlugin {
|
|
|
2146
2258
|
_httpWillTrace(ctx) {
|
|
2147
2259
|
if (this.tracingEnabled) onHttpWillTrace(this.tracingOpts, ctx);
|
|
2148
2260
|
if (this.options.requestLogs !== false) startRequestLog(ctx, this.options.requestLogs);
|
|
2261
|
+
this.watchHooks(ctx);
|
|
2149
2262
|
}
|
|
2150
2263
|
_httpWillQuota(ctx) {
|
|
2151
2264
|
if (this.tracingEnabled) onHttpWillAcquireQuota(ctx);
|
|
@@ -2158,6 +2271,7 @@ var ObservabilityPlugin = class extends DynamicPlugin {
|
|
|
2158
2271
|
}
|
|
2159
2272
|
_httpDidAuth(ctx) {
|
|
2160
2273
|
if (this.tracingEnabled) onHttpDidCheckAuth(ctx);
|
|
2274
|
+
if (this.requestLogsEnabled) recordRequestLogAuth(ctx);
|
|
2161
2275
|
}
|
|
2162
2276
|
_httpWillRoute(ctx) {
|
|
2163
2277
|
if (this.tracingEnabled) onHttpWillRoute(ctx);
|
|
@@ -2170,6 +2284,7 @@ var ObservabilityPlugin = class extends DynamicPlugin {
|
|
|
2170
2284
|
if (this.requestLogsEnabled) void completeRequestLog(ctx);
|
|
2171
2285
|
}
|
|
2172
2286
|
_toolWillParse(ctx) {
|
|
2287
|
+
this.watchHooks(ctx);
|
|
2173
2288
|
if (this.tracingEnabled) onToolWillParse(this.tracingOpts, ctx);
|
|
2174
2289
|
}
|
|
2175
2290
|
_toolWillFind(ctx) {
|
|
@@ -2208,6 +2323,7 @@ var ObservabilityPlugin = class extends DynamicPlugin {
|
|
|
2208
2323
|
if (this.requestLogsEnabled) recordRequestLogFailure(ctx);
|
|
2209
2324
|
}
|
|
2210
2325
|
_resourceWillParse(ctx) {
|
|
2326
|
+
this.watchHooks(ctx);
|
|
2211
2327
|
if (this.tracingEnabled) onResourceWillParse(this.tracingOpts, ctx);
|
|
2212
2328
|
}
|
|
2213
2329
|
_resourceWillFind(ctx) {
|
|
@@ -2231,6 +2347,7 @@ var ObservabilityPlugin = class extends DynamicPlugin {
|
|
|
2231
2347
|
if (this.requestLogsEnabled) recordRequestLogFailure(ctx);
|
|
2232
2348
|
}
|
|
2233
2349
|
_promptWillParse(ctx) {
|
|
2350
|
+
this.watchHooks(ctx);
|
|
2234
2351
|
if (this.tracingEnabled) onPromptWillParse(this.tracingOpts, ctx);
|
|
2235
2352
|
}
|
|
2236
2353
|
_promptWillFind(ctx) {
|
|
@@ -2254,6 +2371,7 @@ var ObservabilityPlugin = class extends DynamicPlugin {
|
|
|
2254
2371
|
if (this.requestLogsEnabled) recordRequestLogFailure(ctx);
|
|
2255
2372
|
}
|
|
2256
2373
|
_agentWillParse(ctx) {
|
|
2374
|
+
this.watchHooks(ctx);
|
|
2257
2375
|
if (this.tracingEnabled) onAgentWillParse(this.tracingOpts, ctx);
|
|
2258
2376
|
}
|
|
2259
2377
|
_agentWillFind(ctx) {
|
|
@@ -2407,46 +2525,46 @@ var ObservabilityPlugin = class extends DynamicPlugin {
|
|
|
2407
2525
|
if (this.tracingEnabled) onAuthDidFinalize(ctx);
|
|
2408
2526
|
}
|
|
2409
2527
|
_oauthTokenWill(ctx) {
|
|
2410
|
-
if (this.
|
|
2528
|
+
if (this.traces("oauthSpans")) onGenericFlowWillStart("oauth/token", this.tracingOpts, ctx);
|
|
2411
2529
|
}
|
|
2412
2530
|
_oauthTokenDone(ctx) {
|
|
2413
|
-
if (this.
|
|
2531
|
+
if (this.traces("oauthSpans")) onGenericFlowDidFinalize(ctx);
|
|
2414
2532
|
}
|
|
2415
2533
|
_oauthAuthzWill(ctx) {
|
|
2416
|
-
if (this.
|
|
2534
|
+
if (this.traces("oauthSpans")) onGenericFlowWillStart("oauth/authorize", this.tracingOpts, ctx);
|
|
2417
2535
|
}
|
|
2418
2536
|
_oauthAuthzDone(ctx) {
|
|
2419
|
-
if (this.
|
|
2537
|
+
if (this.traces("oauthSpans")) onGenericFlowDidFinalize(ctx);
|
|
2420
2538
|
}
|
|
2421
2539
|
_oauthCallbackWill(ctx) {
|
|
2422
|
-
if (this.
|
|
2540
|
+
if (this.traces("oauthSpans")) onGenericFlowWillStart("oauth/callback", this.tracingOpts, ctx);
|
|
2423
2541
|
}
|
|
2424
2542
|
_oauthCallbackDone(ctx) {
|
|
2425
|
-
if (this.
|
|
2543
|
+
if (this.traces("oauthSpans")) onGenericFlowDidFinalize(ctx);
|
|
2426
2544
|
}
|
|
2427
2545
|
_oauthProviderWill(ctx) {
|
|
2428
|
-
if (this.
|
|
2546
|
+
if (this.traces("oauthSpans")) onGenericFlowWillStart("oauth/provider-callback", this.tracingOpts, ctx);
|
|
2429
2547
|
}
|
|
2430
2548
|
_oauthProviderDone(ctx) {
|
|
2431
|
-
if (this.
|
|
2549
|
+
if (this.traces("oauthSpans")) onGenericFlowDidFinalize(ctx);
|
|
2432
2550
|
}
|
|
2433
2551
|
_oauthRegisterWill(ctx) {
|
|
2434
|
-
if (this.
|
|
2552
|
+
if (this.traces("oauthSpans")) onGenericFlowWillStart("oauth/register", this.tracingOpts, ctx);
|
|
2435
2553
|
}
|
|
2436
2554
|
_oauthRegisterDone(ctx) {
|
|
2437
|
-
if (this.
|
|
2555
|
+
if (this.traces("oauthSpans")) onGenericFlowDidFinalize(ctx);
|
|
2438
2556
|
}
|
|
2439
2557
|
_elicitReqWill(ctx) {
|
|
2440
|
-
if (this.
|
|
2558
|
+
if (this.traces("elicitationSpans")) onGenericFlowWillStart("elicitation/request", this.tracingOpts, ctx);
|
|
2441
2559
|
}
|
|
2442
2560
|
_elicitReqDone(ctx) {
|
|
2443
|
-
if (this.
|
|
2561
|
+
if (this.traces("elicitationSpans")) onGenericFlowDidFinalize(ctx);
|
|
2444
2562
|
}
|
|
2445
2563
|
_elicitResWill(ctx) {
|
|
2446
|
-
if (this.
|
|
2564
|
+
if (this.traces("elicitationSpans")) onGenericFlowWillStart("elicitation/result", this.tracingOpts, ctx);
|
|
2447
2565
|
}
|
|
2448
2566
|
_elicitResDone(ctx) {
|
|
2449
|
-
if (this.
|
|
2567
|
+
if (this.traces("elicitationSpans")) onGenericFlowDidFinalize(ctx);
|
|
2450
2568
|
}
|
|
2451
2569
|
_subscribeWill(ctx) {
|
|
2452
2570
|
if (this.tracingEnabled) onGenericFlowWillStart("resources/subscribe", this.tracingOpts, ctx);
|
|
@@ -2512,12 +2630,10 @@ var ObservabilityPlugin = class extends DynamicPlugin {
|
|
|
2512
2630
|
// Startup Report (public method)
|
|
2513
2631
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
2514
2632
|
/**
|
|
2515
|
-
* Emit startup
|
|
2516
|
-
* Call after scope.initialize() completes with component counts.
|
|
2633
|
+
* Emit the `frontmcp.startup` span (`tracing.startupReport`). The scope calls it once it is ready.
|
|
2517
2634
|
*/
|
|
2518
2635
|
reportStartupTelemetry(data) {
|
|
2519
|
-
if (this.
|
|
2520
|
-
reportStartup(data);
|
|
2636
|
+
if (this.traces("startupReport")) reportStartup(data);
|
|
2521
2637
|
}
|
|
2522
2638
|
};
|
|
2523
2639
|
ObservabilityPlugin.defaultOptions = {
|
|
@@ -3084,35 +3200,8 @@ function setupOTel(options) {
|
|
|
3084
3200
|
}
|
|
3085
3201
|
}
|
|
3086
3202
|
|
|
3087
|
-
// libs/observability/src/otel/spans/hook.span.ts
|
|
3088
|
-
import { SpanKind as SpanKind13 } from "@opentelemetry/api";
|
|
3089
|
-
function recordHookEvent(parentSpan, stage, owner) {
|
|
3090
|
-
const attributes = {};
|
|
3091
|
-
if (owner) {
|
|
3092
|
-
attributes[FrontMcpAttributes.HOOK_OWNER] = owner;
|
|
3093
|
-
}
|
|
3094
|
-
parentSpan.addEvent(`hook.${stage}`, attributes);
|
|
3095
|
-
}
|
|
3096
|
-
function startHookSpan(tracer, options) {
|
|
3097
|
-
const attributes = {
|
|
3098
|
-
[FrontMcpAttributes.HOOK_STAGE]: options.stage
|
|
3099
|
-
};
|
|
3100
|
-
if (options.owner) {
|
|
3101
|
-
attributes[FrontMcpAttributes.HOOK_OWNER] = options.owner;
|
|
3102
|
-
}
|
|
3103
|
-
if (options.flowName) {
|
|
3104
|
-
attributes[FrontMcpAttributes.FLOW_NAME] = options.flowName;
|
|
3105
|
-
}
|
|
3106
|
-
return startSpan(tracer, {
|
|
3107
|
-
name: `hook ${options.stage}`,
|
|
3108
|
-
kind: SpanKind13.INTERNAL,
|
|
3109
|
-
attributes,
|
|
3110
|
-
parentContext: options.parentContext
|
|
3111
|
-
});
|
|
3112
|
-
}
|
|
3113
|
-
|
|
3114
3203
|
// libs/observability/src/otel/spans/pretty-span-exporter.ts
|
|
3115
|
-
import { SpanKind as SpanKind14, SpanStatusCode as
|
|
3204
|
+
import { SpanKind as SpanKind14, SpanStatusCode as SpanStatusCode5 } from "@opentelemetry/api";
|
|
3116
3205
|
var RESET = "\x1B[0m";
|
|
3117
3206
|
var DIM = "\x1B[2m";
|
|
3118
3207
|
var BOLD = "\x1B[1m";
|
|
@@ -3164,7 +3253,7 @@ var PrettySpanExporter = class {
|
|
|
3164
3253
|
const traceShort = span.spanContext().traceId.slice(0, 8);
|
|
3165
3254
|
const durationMs = (span.duration[0] * 1e3 + span.duration[1] / 1e6).toFixed(1);
|
|
3166
3255
|
const kind = KIND_LABELS[span.kind] ?? "UNKNOWN";
|
|
3167
|
-
const statusOk = span.status.code !==
|
|
3256
|
+
const statusOk = span.status.code !== SpanStatusCode5.ERROR;
|
|
3168
3257
|
const attrs = Object.entries(span.attributes).filter(([k]) => SHOW_ATTRS.has(k)).map(([k, v]) => {
|
|
3169
3258
|
const short = k.replace("frontmcp.", "").replace("mcp.", "");
|
|
3170
3259
|
return `${short}=${v}`;
|
|
@@ -3353,7 +3442,7 @@ var ProcessStatsCollector = class {
|
|
|
3353
3442
|
});
|
|
3354
3443
|
}
|
|
3355
3444
|
const mem = probeSafely(() => this.memoryUsage());
|
|
3356
|
-
if (mem) {
|
|
3445
|
+
if (mem && mem.rss > 0) {
|
|
3357
3446
|
entries.push({
|
|
3358
3447
|
name: "frontmcp_process_resident_memory_bytes",
|
|
3359
3448
|
value: mem.rss,
|
|
@@ -3376,7 +3465,7 @@ var ProcessStatsCollector = class {
|
|
|
3376
3465
|
});
|
|
3377
3466
|
}
|
|
3378
3467
|
const uptime = probeSafely(() => this.uptime());
|
|
3379
|
-
if (uptime !== void 0) {
|
|
3468
|
+
if (uptime !== void 0 && uptime > 0) {
|
|
3380
3469
|
entries.push({
|
|
3381
3470
|
name: "frontmcp_process_uptime_seconds",
|
|
3382
3471
|
value: uptime,
|