feat: per-request observability (method, latency, errors) - #12
Merged
Merged
Conversation
The server had no per-request observability — only a startup line and error/panic logs — so there was no way to see which LSP methods are called or how long they take. Add ObserveHandler, a jsonrpc2 middleware (mirroring RecoverHandler) wrapping the server handler. It logs method, duration_ms (request body to reply — server processing time), and any error for every request, at Debug level so it is silent unless RIDL_LSP_LOG_LEVEL=debug. Output goes to the server logger (stderr / the editor's output channel), never stdout. Placed outermost so even recovered-panic requests are timed and logged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The server had no per-request observability — only a startup line plus error/panic logs — so there was no way to see which LSP methods are called or how long they take. This adds request-level logging.
Change
ObserveHandler— a jsonrpc2 middleware (mirroringRecoverHandler) wrapping the server handler. For every request it logs:method(e.g.textDocument/completion)duration_ms— request body → reply, i.e. server processing time (not transport or queue wait; handlers are serialized byAsyncHandler)error— only when the request failedIt logs at Debug, so it's silent unless
RIDL_LSP_LOG_LEVEL=debug, and writes only to the server logger (stderr → the editor's output channel). stdout is the JSON-RPC transport and is never logged to. Placed outermost (ObserveHandler(RecoverHandler(ServerHandler))) so even recovered-panic requests are timed and logged with their error.Usage
Set
RIDL_LSP_LOG_LEVEL=debugin the server's environment. Every request then logs a structured line, e.g.{"method":"textDocument/hover","duration_ms":1.83}.Test plan
New tests use zap's
observerto assert the logged fields: method + duration_ms present on success (no error field), an error field on a failed request, and reply forwarding intact.Not included (deliberate)
Aggregation (per-method p50/p95) and metrics export (Prometheus/OTel) were scoped out — this is the per-request log layer. Grep
duration_msfor latency; aggregation can follow if needed.