Repository navigation
Conversation
The gRPC OTel stats handler was silently dropped whenever server.grpc.max_recv_msg_size was configured: Kratos' grpc.Options assigns its option slice instead of appending to it, so the second call overwrote the first and no gRPC server span was ever produced. Both raw options now travel in a single grpc.Options call. The HTTP transport had no instrumentation at all, so transcoded requests produced no server span and no traceparent extraction, leaving traces rooted at whichever application middleware ran first. Kratos' tracing.Server() middleware now heads the HTTP-only chain. Assisted-by: Claude Code Signed-off-by: Javier Rodriguez <javier@chainloop.dev> Chainloop-Trace-Sessions: 250d029e-f05e-422c-a4b2-fb568aa06da2
AI Session Checks — 🟢 93% · ✅ 0 failing
|
| Status | Attribution | File | Lines |
|---|---|---|---|
| created | ai | app/controlplane/internal/server/tracing_test.go |
+184 / -0 |
| modified | ai | app/controlplane/internal/server/http.go |
+29 / -5 |
| modified | ai | app/controlplane/internal/server/grpc.go |
+23 / -5 |
Policies (4)
| Status | Policy | Material | Messages |
|---|---|---|---|
| ✅ Passed | ai-config-ai-agents-allowed |
ai-coding-session-250d02 |
- |
| ✅ Passed | ai-config-no-dangerous-commands |
ai-coding-session-250d02 |
- |
| ✅ Passed | ai-config-no-secrets |
ai-coding-session-250d02 |
- |
| ✅ Passed | ai-config-mcp-servers-allowed |
ai-coding-session-250d02 |
- |
Security Checks — ⚠️ 1 failing
✅ secret-scan
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | secrets-detection |
- |
✅ sast-scan
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | owasp-top10-2025 |
- |
| ✅ Passed | sast |
- |
| ✅ Passed | cwe-top25 |
- |
| ✅ Passed | cwe-top26-40-cusp |
- |
PR info
| Status | Policy | Messages |
|---|---|---|
| ✅ Passed | pr-user-story-linked |
- |
pr-min-approvals |
PR/MR #3449 has 0 approving reviews, 1 required. | |
| ✅ Passed | pr-description-required |
- |
⏭️ 3 scans not applied
| Scan | Reason |
|---|---|
vulnerability-scan |
no manifest/lockfile changed |
github-actions-scan |
no workflow files changed |
iac-scan |
no IaC files changed |
Powered by Chainloop and Chainloop Trace
There was a problem hiding this comment.
2 issues found across 4 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="app/controlplane/internal/server/http.go">
<violation number="1" location="app/controlplane/internal/server/http.go:46">
P2: Every request enters `wrappedServer` with this tracing chain, and normal requests then enter `httpSrv` with the same chain again. This creates duplicate HTTP server spans and adds an unwanted HTTP span before the gRPC stats-handler span for grpc-web; give the wrapper a separate middleware option set without transport tracing.</violation>
</file>
<file name="app/controlplane/internal/server/tracing_test.go">
<violation number="1" location="app/controlplane/internal/server/tracing_test.go:121">
P2: In TestRawGRPCServerOptions, the span-end race makes the strict recorder assertion intermittently flaky. grpc-go's stats handler ends the otelgrpc server span in stream.finish after the response bytes are handed to the transport; the client-side Statusz call can therefore return before the server has run HandleRPC(stats.End). Recording Ended() immediately can intermittently yield 0 (or the wrong count) spans and fail require.Len(t, spans, 1). Poll with require.Eventually until exactly one span is recorded before asserting on it. (The HTTP tests are not affected: the kratos tracing middleware ends its span before ctx.Result writes the response.)</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
|
|
||
| var serverOpts = []http.ServerOption{ | ||
| http.Middleware(middlewares...), | ||
| http.Middleware(httpServerMiddlewares(opts)...), |
There was a problem hiding this comment.
P2: Every request enters wrappedServer with this tracing chain, and normal requests then enter httpSrv with the same chain again. This creates duplicate HTTP server spans and adds an unwanted HTTP span before the gRPC stats-handler span for grpc-web; give the wrapper a separate middleware option set without transport tracing.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/controlplane/internal/server/http.go, line 46:
<comment>Every request enters `wrappedServer` with this tracing chain, and normal requests then enter `httpSrv` with the same chain again. This creates duplicate HTTP server spans and adds an unwanted HTTP span before the gRPC stats-handler span for grpc-web; give the wrapper a separate middleware option set without transport tracing.</comment>
<file context>
@@ -40,12 +42,8 @@ import (
-
var serverOpts = []http.ServerOption{
- http.Middleware(middlewares...),
+ http.Middleware(httpServerMiddlewares(opts)...),
}
// Prevent unmatched routes from falling through to http.DefaultServeMux,
</file context>
|
|
||
| // Regardless of the outcome the transport must have opened a server span. | ||
| spans := recorder.Ended() | ||
| require.Len(t, spans, 1) |
There was a problem hiding this comment.
P2: In TestRawGRPCServerOptions, the span-end race makes the strict recorder assertion intermittently flaky. grpc-go's stats handler ends the otelgrpc server span in stream.finish after the response bytes are handed to the transport; the client-side Statusz call can therefore return before the server has run HandleRPC(stats.End). Recording Ended() immediately can intermittently yield 0 (or the wrong count) spans and fail require.Len(t, spans, 1). Poll with require.Eventually until exactly one span is recorded before asserting on it. (The HTTP tests are not affected: the kratos tracing middleware ends its span before ctx.Result writes the response.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/controlplane/internal/server/tracing_test.go, line 121:
<comment>In TestRawGRPCServerOptions, the span-end race makes the strict recorder assertion intermittently flaky. grpc-go's stats handler ends the otelgrpc server span in stream.finish after the response bytes are handed to the transport; the client-side Statusz call can therefore return before the server has run HandleRPC(stats.End). Recording Ended() immediately can intermittently yield 0 (or the wrong count) spans and fail require.Len(t, spans, 1). Poll with require.Eventually until exactly one span is recorded before asserting on it. (The HTTP tests are not affected: the kratos tracing middleware ends its span before ctx.Result writes the response.)</comment>
<file context>
@@ -0,0 +1,184 @@
+
+ // Regardless of the outcome the transport must have opened a server span.
+ spans := recorder.Ended()
+ require.Len(t, spans, 1)
+ assert.Equal(t, statuszOperation, spans[0].Name())
+ assert.Equal(t, trace.SpanKindServer, spans[0].SpanKind())
</file context>
Closes #3448
With OpenTelemetry tracing enabled, control plane traces rooted at application middleware spans instead of at a transport-level server span, for both entrypoints.
gRPC: the OTel stats handler was silently dropped whenever
server.grpc.max_recv_msg_sizewas configured. Kratos'grpc.Optionsassigns its option slice rather than appending to it, so the second call overwrote the first. Both raw options now travel in a singlegrpc.Optionscall. This also covers grpc-web, which re-dispatches throughgrpc.Server.ServeHTTP.HTTP: the transport had no instrumentation at all, so transcoded requests produced no server span and no
traceparentextraction. Kratos'tracing.Server()middleware now heads the HTTP-only middleware chain. It is deliberately not added to the shared chain, which would duplicate the gRPC stats-handler span.The tracer provider is now passed explicitly through the server
Optsinstead of being picked up from the OTel global.AI disclosure: this change was produced with the assistance of Claude Code.