textmachine/backend/internal/obs/trace.go

69 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package obs is the observability seam: trace ids threaded through context,
// the slog handler that stamps them on every log line, gated LLM-exchange
// logging, and recovered goroutines. Порт vojo trace.go/logging.go +
// вынесенный из bot.go safego; ось запроса заменена с (sender, verbose) на
// (book, chapter, chunk, stage, role) — домен перевода.
package obs
import (
"context"
"crypto/rand"
"encoding/hex"
)
// trace.go threads a per-run correlation id and the small request facts the
// logger needs through context — the userver / OpenTelemetry idiom: mint once
// at the top of a unit of work, and every log line below it (down to the HTTP
// call to the model) carries the same trace_id without passing a logger by
// hand.
//
// The id is 16 random bytes rendered as 32 hex chars — the W3C Trace-Context /
// OTel trace-id shape — so the field maps straight onto an OTel exporter later.
type ctxKey int
const reqInfoKey ctxKey = iota
// ReqInfo is the per-call data carried in context. Zero-value fields are
// simply omitted from logs.
type ReqInfo struct {
TraceID string
Book string
Chapter int
Chunk int
Stage string // pipeline stage: draft | edit | ...
Role string // agent role: translator | editor | ...
// LogBodies gates raw LLM request/response body logging (privacy by
// default; decided once at admission so the transport just reads the flag).
LogBodies bool
}
// WithReqInfo stamps the call's info onto ctx. WithTimeout/WithCancel preserve
// values, so it flows down into the model transport.
func WithReqInfo(ctx context.Context, ri ReqInfo) context.Context {
return context.WithValue(ctx, reqInfoKey, ri)
}
// ReqInfoFromContext returns the call info, if any.
func ReqInfoFromContext(ctx context.Context) (ReqInfo, bool) {
ri, ok := ctx.Value(reqInfoKey).(ReqInfo)
return ri, ok
}
// TraceFromContext returns the trace id, or "" when ctx carries none (startup
// paths) — the slog handler then simply omits trace_id.
func TraceFromContext(ctx context.Context) string {
if ri, ok := ReqInfoFromContext(ctx); ok {
return ri.TraceID
}
return ""
}
// NewTraceID mints a random 16-byte id as 32 hex chars (the OTel trace-id
// shape). crypto/rand.Read never returns an error on modern Go (an entropy
// failure crashes the process rather than returning a short read).
func NewTraceID() string {
var b [16]byte
_, _ = rand.Read(b[:])
return hex.EncodeToString(b[:])
}