···11+package bff
22+33+import (
44+ "context"
55+ "fmt"
66+77+ "go.opentelemetry.io/otel/trace"
88+)
99+1010+// Traceparent returns the W3C traceparent header value for the current span,
1111+// or an empty string if no active trace exists. This is intended to be called
1212+// from templ templates so that client-side requests (HTMX) can propagate the
1313+// trace context back to the server.
1414+func Traceparent(ctx context.Context) string {
1515+ sc := trace.SpanFromContext(ctx).SpanContext()
1616+ if !sc.HasTraceID() || !sc.HasSpanID() {
1717+ return ""
1818+ }
1919+2020+ flags := "00"
2121+ if sc.IsSampled() {
2222+ flags = "01"
2323+ }
2424+2525+ return fmt.Sprintf("00-%s-%s-%s", sc.TraceID(), sc.SpanID(), flags)
2626+}
+12
internal/web/components/layout.templ
···219219 // Increase history cache size to prevent cache misses
220220 htmx.config.historyCacheSize = 20;
221221222222+ // Propagate W3C traceparent to HTMX requests so sub-requests
223223+ // (e.g. /api/feed, /api/popular-recipes) share the page's trace
224224+ var tp = document.querySelector('meta[name="traceparent"]');
225225+ if (tp) {
226226+ document.body.addEventListener('htmx:configRequest', function(evt) {
227227+ evt.detail.headers['traceparent'] = tp.content;
228228+ });
229229+ }
230230+222231 // Show session-expired modal on 401 responses
223232 document.body.addEventListener('htmx:afterRequest', function(evt) {
224233 if (evt.detail.xhr && evt.detail.xhr.status === 401) {
···263272 <script src="/static/js/data-cache.js?v=0.3.0"></script>
264273 <script src="/static/js/sw-register.js?v=0.2.0"></script>
265274 </head>
275275+ if tp := bff.Traceparent(ctx); tp != "" {
276276+ <meta name="traceparent" content={ tp }/>
277277+ }
266278 <body
267279 class="min-h-full flex flex-col"
268280 style="background-color: var(--page-bg); color: var(--page-text);"