mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 17:31:06 +00:00
feat(diagnostics): surface provider request id hashes
This commit is contained in:
@@ -5,12 +5,14 @@ const telemetryState = vi.hoisted(() => {
|
||||
const histograms = new Map<string, { record: ReturnType<typeof vi.fn> }>();
|
||||
const spans: Array<{
|
||||
name: string;
|
||||
addEvent: ReturnType<typeof vi.fn>;
|
||||
end: ReturnType<typeof vi.fn>;
|
||||
setStatus: ReturnType<typeof vi.fn>;
|
||||
}> = [];
|
||||
const tracer = {
|
||||
startSpan: vi.fn((name: string, _opts?: unknown, _ctx?: unknown) => {
|
||||
const span = {
|
||||
addEvent: vi.fn(),
|
||||
end: vi.fn(),
|
||||
setStatus: vi.fn(),
|
||||
};
|
||||
@@ -945,6 +947,48 @@ describe("diagnostics-otel service", () => {
|
||||
await service.stop?.(ctx);
|
||||
});
|
||||
|
||||
test("records upstream request id hashes as model call span events only", async () => {
|
||||
const service = createDiagnosticsOtelService();
|
||||
const ctx = createOtelContext(OTEL_TEST_ENDPOINT, { traces: true, metrics: true });
|
||||
await service.start(ctx);
|
||||
|
||||
emitDiagnosticEvent({
|
||||
type: "model.call.error",
|
||||
runId: "run-1",
|
||||
callId: "call-1",
|
||||
provider: "openai",
|
||||
model: "gpt-5.4",
|
||||
api: "openai-responses",
|
||||
durationMs: 40,
|
||||
errorCategory: "ProviderError",
|
||||
upstreamRequestIdHash: "sha256:123456abcdef",
|
||||
});
|
||||
await flushDiagnosticEvents();
|
||||
|
||||
const modelCall = telemetryState.tracer.startSpan.mock.calls.find(
|
||||
(call) => call[0] === "openclaw.model.call",
|
||||
);
|
||||
expect(modelCall?.[1]).toEqual({
|
||||
attributes: expect.not.objectContaining({
|
||||
"openclaw.upstreamRequestIdHash": expect.anything(),
|
||||
}),
|
||||
startTime: expect.any(Number),
|
||||
});
|
||||
const span = telemetryState.spans.find((candidate) => candidate.name === "openclaw.model.call");
|
||||
expect(span?.addEvent).toHaveBeenCalledWith("openclaw.provider.request", {
|
||||
"openclaw.upstreamRequestIdHash": "sha256:123456abcdef",
|
||||
});
|
||||
expect(
|
||||
telemetryState.histograms.get("openclaw.model_call.duration_ms")?.record,
|
||||
).toHaveBeenCalledWith(
|
||||
40,
|
||||
expect.not.objectContaining({
|
||||
"openclaw.upstreamRequestIdHash": expect.anything(),
|
||||
}),
|
||||
);
|
||||
await service.stop?.(ctx);
|
||||
});
|
||||
|
||||
test("parents trusted diagnostic lifecycle spans from explicit parent ids", async () => {
|
||||
const service = createDiagnosticsOtelService();
|
||||
const ctx = createOtelContext(OTEL_TEST_ENDPOINT, { traces: true, metrics: true });
|
||||
|
||||
@@ -184,6 +184,22 @@ function assignGenAiModelCallAttrs(
|
||||
attrs["gen_ai.operation.name"] = genAiOperationName(evt.api);
|
||||
}
|
||||
|
||||
function addUpstreamRequestIdSpanEvent(
|
||||
span: { addEvent?: (name: string, attributes?: Record<string, string>) => void },
|
||||
upstreamRequestIdHash: string | undefined,
|
||||
): void {
|
||||
if (!upstreamRequestIdHash) {
|
||||
return;
|
||||
}
|
||||
const boundedHash = lowCardinalityAttr(upstreamRequestIdHash);
|
||||
if (boundedHash === "unknown") {
|
||||
return;
|
||||
}
|
||||
span.addEvent?.("openclaw.provider.request", {
|
||||
"openclaw.upstreamRequestIdHash": boundedHash,
|
||||
});
|
||||
}
|
||||
|
||||
function clampOtelLogText(value: string, maxChars: number): string {
|
||||
return value.length > maxChars ? `${value.slice(0, maxChars)}...(truncated)` : value;
|
||||
}
|
||||
@@ -1148,6 +1164,7 @@ export function createDiagnosticsOtelService(): OpenClawPluginService {
|
||||
parentContext: contextForTrustedDiagnosticSpanParent(evt, metadata),
|
||||
endTimeMs: evt.ts,
|
||||
});
|
||||
addUpstreamRequestIdSpanEvent(span, evt.upstreamRequestIdHash);
|
||||
span.end(evt.ts);
|
||||
};
|
||||
|
||||
@@ -1184,6 +1201,7 @@ export function createDiagnosticsOtelService(): OpenClawPluginService {
|
||||
parentContext: contextForTrustedDiagnosticSpanParent(evt, metadata),
|
||||
endTimeMs: evt.ts,
|
||||
});
|
||||
addUpstreamRequestIdSpanEvent(span, evt.upstreamRequestIdHash);
|
||||
span.setStatus({
|
||||
code: SpanStatusCode.ERROR,
|
||||
message: redactSensitiveText(evt.errorCategory),
|
||||
|
||||
Reference in New Issue
Block a user