-
Notifications
You must be signed in to change notification settings - Fork 10
observability
Verdict: passive listener lifecycle per operation. Zero listeners β Observation.NONE (no allocation, no capture). Listener gets start(info) β ObservationScope (context propagation across thread hops via reattach) and one terminal complete(info, outcome).
Observation tachyon-core/src/main/java/dev/tachyonmcp/core/server/observability/Observation.java
| Call | Where | Meaning |
|---|---|---|
start(listeners, info) |
dispatcher entry | each listener start; faults logged, NOOP scope Observation#start
|
closeStart() |
calling thread before hop to executor | close start scopes once Observation#closeStart
|
reattach() / closeReattached
|
executor thread around decode+kickoff, result mapping | re-open scopes (e.g. OTel context) Observation#reattach
|
mark* |
handlers | override outcome: TaskHandoff, SerializationFailed, PayloadFailure OperationOutcome
|
complete(default) |
once (CAS) | override wins Observation#complete
|
Listener throwing on interrupted thread β rethrown, else warn Observation#fault.
OperationInfo (mutable): kind REQUEST|NOTIFICATION|INITIALIZE, method, requestId, sessionId, traceparent (from _meta.traceparent, always extracted McpDispatcher#extractTraceParent), protocolVersion, server address/port, request/response payload, target (tool/prompt name), exceptionCause OperationInfo.
OperationOutcome sealed OperationOutcome: Rejected(error?, httpStatus, wireCode), Completed, PayloadFailure, SerializationFailed, HandlerFailed(error, wireCode, cause?), Cancelled, TaskHandoff(taskId), NotificationAccepted, NotificationIgnored, StreamEstablished.
PayloadCapturePolicy(requestArgs, responseContent, rawMessage, exceptionDetail, maxBytes=4096) PayloadCapturePolicy. Capture only when toggle on and observation active. CapturedPayload.capture truncates on UTF-8 boundary with β¦(truncated) CapturedPayload#capture. Tool args/result captured in ToolsCallHandler#handleAsync, ToolsCallHandler#captureResponseContent. Exception cause only via DispatchContext.captureExceptionCause gated by exceptionDetail DispatchContext#captureExceptionCause.
observability { slowRequestLogging(); slowRequestThreshold(d) } β HandlerWatchdog DEBUG log after threshold + WARN on slow POST response McpDispatcher#invokeHandlerAsync, McpOperationHandler#completePostRequest.
McpOpenTelemetryListener.create(openTelemetry) McpOpenTelemetryListener: SERVER span named by method, histogram mcp.server.operation.duration (s), attributes mcp.method.name, mcp.session.id, mcp.protocol.version, gen_ai.tool.name, gen_ai.prompt.name, gen_ai.operation.name, optional gen_ai.tool.call.arguments/result McpAttributes; error.type from error kind / tool error / cause type. Follows OTel GenAI MCP semconv.
ObservationListener is @InternalApi + @Experimental yet meant for bridges β api-stability.
Related: request-lifecycle, integrations.
π source .llm-wiki/concepts/observability.md Β· updated 2026-09-14 Β· verified at 582f9c52 Β· tags [concept, observability, otel]
π§ Start
βοΈ Concepts (cross-cutting)
- request-lifecycle
- netty-pipeline
- protocol-versions
- sessions
- sse-streams
- feature-registries
- tasks
- extensions
- json-layer
- errors
- concurrency
- declarative-configuration
- configuration
- security-guards
- observability
- api-stability
π¦ Modules