mirror of
https://github.com/zap-proto/zip.git
synced 2026-10-19 15:38:57 +00:00
Let a host name the span a call continues
A host that keeps spans in its own tracer opens positions zip never sees: a span inside a handler, or work that outlives its request. An outbound call then names the request, or nothing, as its parent, and the callee's spans land beside the span that caused them. Claude-Session: https://claude.ai/code/session_01HuMDfgFssPUptYUzgZ6Cp8
This commit is contained in:
@@ -202,6 +202,11 @@ func forwardIdentity(ctx context.Context, req *fasthttp.Request) {
|
||||
req.Header.SetBytesV(HeaderTrace, v)
|
||||
}
|
||||
}
|
||||
if Traceparent != nil {
|
||||
if v := Traceparent(ctx); v != "" {
|
||||
req.Header.Set(HeaderTrace, v)
|
||||
}
|
||||
}
|
||||
if acting, ok := ctx.Value(actingKey{}).(Caller); ok {
|
||||
for h, v := range acting.headers() {
|
||||
req.Header.Set(h, v)
|
||||
|
||||
@@ -63,6 +63,17 @@ import (
|
||||
// that is.
|
||||
const HeaderTrace = "traceparent"
|
||||
|
||||
// Traceparent, when set, answers the trace position a call made with ctx
|
||||
// continues, as a W3C traceparent value, or "" for none.
|
||||
//
|
||||
// A host that keeps spans in a tracer of its own has positions zip never sees: a
|
||||
// span opened inside a handler, or work that outlives the request it began in and
|
||||
// carries no request at all. Without this an outbound call names the inbound
|
||||
// request as its parent, or nothing, and the callee's spans land beside the span
|
||||
// that caused them instead of under it. A non-empty answer wins over the
|
||||
// request's own header. Set it once, before the first call.
|
||||
var Traceparent func(ctx context.Context) string
|
||||
|
||||
// traceVersion is the only trace-context version this parses. The spec says a
|
||||
// receiver must accept future versions by reading the fields it knows, but a
|
||||
// version it does not know may reorder them — so an unknown version is treated
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package zip
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// sentTrace is the traceparent forwardIdentity writes onto an outbound request.
|
||||
func sentTrace(ctx context.Context) string {
|
||||
req := fasthttp.AcquireRequest()
|
||||
defer fasthttp.ReleaseRequest(req)
|
||||
forwardIdentity(ctx, req)
|
||||
return string(req.Header.Peek(HeaderTrace))
|
||||
}
|
||||
|
||||
// A host's own trace position rides a call that has no request behind it, wins
|
||||
// over the request's when there is one, and an empty answer leaves the request's.
|
||||
func TestTraceparentHook(t *testing.T) {
|
||||
const own = "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"
|
||||
old := Traceparent
|
||||
t.Cleanup(func() { Traceparent = old })
|
||||
|
||||
Traceparent = nil
|
||||
if got := sentTrace(context.Background()); got != "" {
|
||||
t.Fatalf("no hook, no request: sent %q, want nothing", got)
|
||||
}
|
||||
|
||||
Traceparent = func(context.Context) string { return own }
|
||||
if got := sentTrace(context.Background()); got != own {
|
||||
t.Fatalf("detached call: sent %q, want %q", got, own)
|
||||
}
|
||||
|
||||
var fromRequest, overridden string
|
||||
app := New(Config{AppName: "front", DisableStartupMessage: true})
|
||||
app.Raw("GET", "/ask", func(c *Ctx) error {
|
||||
Traceparent = func(context.Context) string { return "" }
|
||||
fromRequest = sentTrace(c.Forward())
|
||||
Traceparent = func(context.Context) string { return own }
|
||||
overridden = sentTrace(c.Forward())
|
||||
return c.JSON(200, map[string]string{"ok": "1"})
|
||||
})
|
||||
resp, err := app.Fiber().Test(httptest.NewRequest("GET", "/ask", nil))
|
||||
if err != nil {
|
||||
t.Fatalf("GET: %v", err)
|
||||
}
|
||||
_ = resp.Body.Close()
|
||||
if fromRequest == "" || fromRequest == own {
|
||||
t.Fatalf("empty answer: sent %q, want the request's own traceparent", fromRequest)
|
||||
}
|
||||
if overridden != own {
|
||||
t.Fatalf("hook answer: sent %q, want %q over the request's", overridden, own)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user