Documentation
¶
Overview ¶
Package model holds the recorded data of a tracer: traces, spans and the snapshot read model built from them. It is the vocabulary the recorder and the front end share, and it depends on nothing else in the project.
Index ¶
- Constants
- Variables
- func Do(ctx context.Context, name string, fn func(context.Context) error, kind ...Kind) error
- func IsBytes(key string) bool
- func NewID(now time.Time) (string, error)
- func TraceHost(trace *Trace) string
- func TraceID(ctx context.Context) string
- func ValidID(id string) bool
- func WithTrace(ctx context.Context, t *Trace) context.Context
- type Attributes
- type Auth
- func (a *Auth) Authenticate(ctx context.Context, username, password string) error
- func (a *Auth) LoginRequired() bool
- func (a *Auth) NetworkAllowed(r *http.Request) bool
- func (a *Auth) RequestUser(r *http.Request) (string, bool)
- func (a *Auth) Session(username string) (string, error)
- func (a *Auth) Verify(token string) (string, error)
- type HTTPInfo
- type HostStat
- type Kind
- type LogEntry
- type Memory
- type MemoryUse
- type Options
- type PoolEstimate
- type Recorder
- type Sampler
- type SamplerFunc
- type Snapshot
- type Span
- func (s *Span) Context(ctx context.Context) context.Context
- func (s *Span) Elapsed() time.Duration
- func (s *Span) End()
- func (s *Span) EndWithError(err error)
- func (s *Span) Ended() bool
- func (s *Span) Err() error
- func (s *Span) Error(message string, args ...any)
- func (s *Span) Inert() Span
- func (s *Span) Info(message string, args ...any)
- func (s *Span) RecordError(err error)
- func (s *Span) SetAttribute(key string, value any)
- func (s *Span) SetAttributes(attributes Attributes)
- func (s *Span) SetName(name string)
- func (s *Span) SetSource(filename string, line int)
- func (s *Span) SourceText() string
- func (s *Span) Trace() *Trace
- func (s *Span) Warn(message string, args ...any)
- type Spans
- type State
- type StateDuration
- type Statistic
- type Stats
- type Storage
- type Trace
- func (t *Trace) Attribute(key string) (any, bool)
- func (t *Trace) Clone() Trace
- func (t *Trace) CloneInto(dst *Trace)
- func (t *Trace) Current() *Span
- func (t *Trace) Durations() map[State]time.Duration
- func (t *Trace) Elapsed() time.Duration
- func (t *Trace) Err() error
- func (t *Trace) Error(message string, args ...any)
- func (t *Trace) Failed() bool
- func (t *Trace) Finish()
- func (t *Trace) HasKind(kind Kind) bool
- func (t *Trace) Info(message string, args ...any)
- func (t *Trace) Kinds() []Kind
- func (t *Trace) RecordError(err error)
- func (t *Trace) RecordMemory()
- func (t *Trace) Release()
- func (t *Trace) ReleaseOnCollect()
- func (t *Trace) Root() *Span
- func (t *Trace) SetAttribute(key string, value any)
- func (t *Trace) SetAttributes(attributes Attributes)
- func (t *Trace) SetHTTPInfo(info *HTTPInfo)
- func (t *Trace) SetName(name string)
- func (t *Trace) SetResponse(status int, bytes int64, route string)
- func (t *Trace) SetState(state State)
- func (t *Trace) SpanCount() int
- func (t *Trace) StartSpan(ctx context.Context, name string, kind ...Kind) (context.Context, *Span)
- func (t *Trace) StateTimes() (out [numStates]time.Duration)
- func (t *Trace) Status() int
- func (t *Trace) TrackMemory()
- func (t *Trace) Warn(message string, args ...any)
- type TraceOptions
Constants ¶
const ( // AttrMemoryLimit is the memory a transaction was allowed to use, in bytes. // It is recorded on the trace. AttrMemoryLimit = "memory_limit" // AttrMemoryUsage is the memory in use when a span or a trace finished, in // bytes. AttrMemoryUsage = "memory_usage" )
Well known attribute keys. The set is open; these are the ones the front end renders as sizes rather than as bare integers.
const ( LevelInfo = "info" LevelWarn = "warn" LevelError = "error" )
Log levels recorded by Trace.Info, Trace.Warn and Trace.Error. The set is closed: a log line informs, flags something worth attention, or reports a failure, and anything richer belongs in attributes.
const BackgroundHost = "internal"
BackgroundHost is the host label of traces that did not arrive over the network: cron ticks, queue consumers, startup work.
const DefaultPath = "/debug/oida"
DefaultPath is the default mount path of the debug front end.
const RequestIDHeader = "Request-Id"
RequestIDHeader carries the trace identifier on the request and the response.
const SessionCookie = "oida_session"
SessionCookie is the name of the front end session cookie.
const SessionTTL = 12 * time.Hour
SessionTTL is how long an issued session token stays valid.
Variables ¶
var ( // ErrNilRouter is returned when Mount is called without a router. ErrNilRouter = errors.New("oida: router is nil") // ErrNoTracer is returned when Mount is called without a tracer, which is // a dashboard with nothing to show. ErrNoTracer = errors.New("oida: options carry no tracer") // ErrInvalidOptions is the base error for every configuration failure. ErrInvalidOptions = errors.New("oida: invalid options") // ErrInvalidPath is returned when Options.Path is not an absolute path. ErrInvalidPath = fmt.Errorf("%w: path must be an absolute path", ErrInvalidOptions) // ErrInvalidSampleRate is returned when Options.SampleRate is outside // [0,100]. ErrInvalidSampleRate = fmt.Errorf("%w: sample rate must be between 0 and 100", ErrInvalidOptions) // ErrTraceNotFound is returned when a trace ID is not in the ring buffer. ErrTraceNotFound = errors.New("oida: trace not found") // ErrDisabled is returned when a trace is requested from a disabled tracer. ErrDisabled = errors.New("oida: tracer is disabled") // ErrInvalidCredentials is returned when a login does not match any // configured user. ErrInvalidCredentials = errors.New("oida: invalid credentials") // ErrInvalidToken is returned when a session cookie or bearer token does // not verify against the signing secret, or has expired. ErrInvalidToken = errors.New("oida: invalid token") )
The errors this project returns. Every configuration failure wraps ErrInvalidOptions, so a caller can test for the class or for the case. The root package aliases each of these, so services keep spelling them oida.Err*.
Functions ¶
func Do ¶
Do runs fn inside a span, records the returned error on it and ends it. The error is returned unchanged.
func TraceHost ¶
TraceHost returns the host a trace belongs to. Background traces have none, so they group under a stable placeholder rather than an empty string.
func TraceID ¶
TraceID returns the identifier of the trace in ctx, or an empty string. It is the value of the Request-Id header for HTTP traces, which makes it the cheapest correlation key for logs.
Types ¶
type Attributes ¶
Attributes is a set of key/value pairs recorded on a trace or on a span.
type Auth ¶
type Auth struct {
// contains filtered or unexported fields
}
Auth evaluates the authentication options: the network allow list, the configured users and the token verification behind the session cookie and the Authorization header. The front end builds one per handler with NewAuth.
Every method tolerates a nil receiver, which is what an unconfigured handler holds: a nil Auth allows every network and requires no login.
func NewAuth ¶
NewAuth builds the authentication state out of the options. Invalid network entries and an unreadable users file are dropped, reported through Options.OnError and returned, so the caller decides whether to treat them as fatal; what loaded stays in force. Dropping an allow list entry only narrows access, so a typo fails closed.
It returns nil when no authentication option is set.
func (*Auth) Authenticate ¶
Authenticate checks a login against the configured users, then against the AuthorizeUser callback. Password hashes are bcrypt; a user with any other hash format never matches.
func (*Auth) LoginRequired ¶
LoginRequired reports whether requests have to carry a session or a bearer token: any configured user, the authorize callback or a pre-shared secret makes credentials the way in. A bare AllowedNetworks list requires none.
func (*Auth) NetworkAllowed ¶
NetworkAllowed reports whether the request comes from an allowed network. An empty allow list allows every network; an unparsable peer address is denied.
func (*Auth) RequestUser ¶
RequestUser returns the username a request carries, in its session cookie or in an Authorization bearer token.
type HTTPInfo ¶
type HTTPInfo struct {
Method string `json:"method"`
URI string `json:"uri"`
Route string `json:"route,omitempty"`
Host string `json:"host"`
Protocol string `json:"protocol"`
RemoteAddress string `json:"remote_address"`
UserAgent string `json:"user_agent,omitempty"`
Status int `json:"status,omitempty"`
ResponseBytes int64 `json:"response_bytes"`
}
HTTPInfo describes the request a trace was created for.
type HostStat ¶
type HostStat struct {
Host string `json:"host"`
// Requests counts every request the host was seen in, sampled or not, for
// the lifetime of the process.
Requests uint64 `json:"requests"`
// Traces counts the traces of this host retained in the rolling window.
Traces uint64 `json:"traces"`
Errors uint64 `json:"errors"`
Routes int `json:"routes"`
AverageDuration time.Duration `json:"average_duration_ns"`
MaxDuration time.Duration `json:"max_duration_ns"`
Spans uint64 `json:"spans"`
// contains filtered or unexported fields
}
HostStat aggregates the traffic of one host.
type Kind ¶
type Kind string
Kind classifies the work a span measured. The set is open: an unrecognized value is valid, renders with the fallback color and groups on its own in the timeline.
const ( KindInternal Kind = "internal" KindHTTP Kind = "http" KindDatabase Kind = "database" KindExternal Kind = "external" KindTemplate Kind = "template" KindCache Kind = "cache" KindQueue Kind = "queue" )
The kinds the front end colours and groups by. A value outside this set is still valid.
func (Kind) Color ¶
Color returns the stable UI color of the kind. This is a categorical scale, not a theme: the hues are spread and saturated so two segments of a timeline can be told apart at a glance, which matters more here than restraint. Text on top of these is the dark ink, so every one of them carries a label.
type LogEntry ¶
type LogEntry struct {
Time time.Time `json:"time"`
Level string `json:"level"`
Message string `json:"message"`
SpanID int `json:"span_id,omitempty"`
// RequestID is the ID of the trace the entry was recorded on, the value
// the Request-Id header carries. Every entry of a trace shares it, and it
// keeps an entry attributable once the log is read apart from its trace.
RequestID string `json:"request_id,omitempty"`
// Attributes carry the slog-style key/value arguments of the call. The
// message is stored verbatim, never formatted.
Attributes Attributes `json:"attributes,omitempty"`
}
LogEntry is one log line recorded on a trace. Entries live in one slice on the trace, in write order; SpanID links each entry to the span that was active when it was written, and is zero for an entry written outside any open span.
type Memory ¶
type Memory struct {
HeapAlloc uint64 `json:"heap_alloc_bytes"`
HeapInuse uint64 `json:"heap_inuse_bytes"`
HeapObjects uint64 `json:"heap_objects"`
StackInuse uint64 `json:"stack_inuse_bytes"`
System uint64 `json:"system_bytes"`
NextGC uint64 `json:"next_gc_bytes"`
NumGC uint32 `json:"gc_cycles"`
GCPauseTotal uint64 `json:"gc_pause_total_ns"`
GCCPUFraction float64 `json:"gc_cpu_fraction"`
Limit uint64 `json:"memory_limit_bytes,omitempty"`
}
Memory describes current process memory and GC pressure.
func ReadMemory ¶
func ReadMemory() Memory
ReadMemory reads the process memory and GC pressure the dashboard shows, through runtime/metrics: unlike runtime.ReadMemStats, the read does not stop the world, so an open live view does not pause the process it watches. The pause total and the GC CPU fraction are computed from counters and are indicative, the way MemoryUse documents. Limit is the caller's to fill.
type MemoryUse ¶
type MemoryUse struct {
HeapDelta int64 `json:"heap_delta_bytes"`
AllocatedBytes uint64 `json:"allocated_bytes"`
Allocations uint64 `json:"allocations"`
GCCycles uint32 `json:"gc_cycles"`
GCPause time.Duration `json:"gc_pause_ns"`
}
MemoryUse holds the process-wide allocation deltas observed while a trace ran. Concurrent traces overlap, so the values are indicative, not exact.
type Options ¶
type Options struct {
// Path is the mount path of the debug front end.
Path string `yaml:"path"`
// ServiceName is displayed in the front end and recorded on every trace.
ServiceName string `yaml:"service_name"`
// Enabled records traces. A disabled tracer passes requests through.
Enabled bool `yaml:"enabled"`
// RingBufferSize is the number of completed traces retained.
RingBufferSize int `yaml:"ring_buffer_size"`
// TopRequests is the maximum number of groups in rolling statistics.
TopRequests int `yaml:"top_requests"`
// MaxSpansPerTrace bounds the spans recorded in a single trace. Excess
// spans are counted in Trace.DroppedSpans. Zero means unlimited.
MaxSpansPerTrace int `yaml:"max_spans_per_trace"`
// SampleRate is the percentage of requests traced, between 0 and 100. It
// is ignored when Sampler is set.
SampleRate float64 `yaml:"sample_rate"`
// TrackMemoryUse records process-wide allocation changes for each trace.
TrackMemoryUse bool `yaml:"track_memory_use"`
// TrustRequestID reuses a client supplied Request-Id header. Only enable
// this behind a trusted proxy.
TrustRequestID bool `yaml:"trust_request_id"`
// IgnorePaths lists request paths that are never traced. Entries ending in
// "/*" match by prefix.
IgnorePaths []string `yaml:"ignore_paths"`
// RefreshInterval is the fallback auto refresh interval of the live view in
// seconds, used when the browser cannot stream. Zero disables it.
RefreshInterval int `yaml:"refresh_interval"`
// LiveStream serves the live view over server sent events, so recorded
// traces appear as they happen instead of on a timer.
LiveStream bool `yaml:"live_stream"`
// CaptureLogs records Trace.Info and Trace.Error log entries on traces.
// Disabled, Info does nothing and Error records its formatted text as an
// error on the active span, the way RecordError does, so the message is
// not lost.
CaptureLogs bool `yaml:"capture_logs"`
// ReadEnv applies the OIDA_* environment to these options when the tracer
// is built. NewOptions turns it on, so a service configured in code still
// takes its deployment settings from the environment; options built as a
// literal leave it off.
ReadEnv bool `yaml:"read_env"`
// Sampler decides whether a request is traced. It replaces SampleRate.
Sampler Sampler `yaml:"-"`
// Storage retains completed traces. Defaults to a memory ring buffer sized
// by RingBufferSize; disk storage retains them across restarts.
Storage Storage `yaml:"-"`
// RouteFunc returns the routed pattern of a request, so statistics group
// /users/1 and /users/2 into GET /users/{id}. With chi:
//
// opts.RouteFunc = func(r *http.Request) string {
// return chi.RouteContext(r.Context()).RoutePattern()
// }
//
// The function decides on its own: returning an empty string means the
// request has no route worth grouping by, and it groups by path instead.
// A nil function falls back to the pattern the router recorded on the
// request, which is what http.ServeMux and chi both set.
RouteFunc func(r *http.Request) string `yaml:"-"`
// OnError receives storage and recording errors. The package never writes
// to stdout or stderr, so this is the only way to observe them.
OnError func(error) `yaml:"-"`
// Authorize gates access to the debug front end. A nil function allows
// every request.
Authorize func(r *http.Request) bool `yaml:"-"`
// Clock is the time source of the tracer. Defaults to time.Now.
Clock func() time.Time `yaml:"-"`
// AllowedNetworks restricts the debug front end to peers inside these
// CIDR ranges, such as 127.0.0.0/8 or 10.0.0.0/8. IPv4 and IPv6 both
// work; an empty list allows every network. A request from outside
// receives a 404 response, like a failed Authorize.
AllowedNetworks []string `yaml:"allowed_networks"`
// Users maps usernames to bcrypt password hashes for the front end login
// screen. Setting any user puts the front end behind a login.
Users map[string]string `yaml:"users"`
// UsersFile is the path of an .htpasswd style file with one
// username:bcrypt-hash per line. It is read once, when the front end is
// mounted, and merged under Users.
UsersFile string `yaml:"users_file"`
// SigningSecret signs the session cookie issued by the login screen and
// verifies "Authorization: Bearer" JWTs (HS256). When empty a per-process
// secret is generated: logins work, but sessions do not survive a restart
// and no externally minted token verifies.
SigningSecret string `yaml:"signing_secret"`
// AuthorizeUser authenticates a login when the configured users do not.
// Returning nil grants a session naming the given username.
AuthorizeUser func(ctx context.Context, username, password string) error `yaml:"-"`
// contains filtered or unexported fields
}
Options configures telemetry behaviour, the debug front end and the middleware. Take NewOptions and override what you need, so fields added in later versions keep their defaults.
func NewOptions ¶
NewOptions returns the default options for the named service.
func (Options) Authorized ¶
Authorized reports whether r may access the debug front end. The front end asks before it serves anything, including its assets.
func (Options) Validate ¶
Validate reports whether the options are usable. Every failure wraps ErrInvalidOptions.
func (Options) WithDefaults ¶
WithDefaults returns a usable copy of the options. Options created by NewOptions preserve explicit zero values; an uninitialized Options receives the numeric defaults for backward compatibility.
type PoolEstimate ¶
type PoolEstimate struct {
Samples uint64 `json:"samples"`
AverageAllocatedBytes uint64 `json:"average_allocated_bytes"`
BeforeNextGC uint64 `json:"traces_before_next_gc,omitempty"`
WithinMemoryLimit uint64 `json:"traces_within_memory_limit,omitempty"`
}
PoolEstimate is a heuristic concurrency estimate derived from observed per-trace allocations.
type Recorder ¶
type Recorder interface {
// StartTrace begins a trace the caller must complete with Finish.
StartTrace(ctx context.Context, name string) (context.Context, *Trace, error)
// Finish completes a trace and retains it.
Finish(t *Trace)
// Snapshot returns a race free copy of the recorded state.
Snapshot() Snapshot
// Traces returns the retained traces, newest first.
Traces() []Trace
// Trace returns the retained or in flight trace with the given ID.
Trace(id string) (Trace, bool)
// Live returns the traces currently in flight, newest first.
Live() []Trace
// Subscribe returns a channel notified whenever a trace starts or
// completes, and a function releasing it.
Subscribe() (<-chan struct{}, func())
// Options returns the options the recorder was built with.
Options() Options
// Enabled reports whether the recorder records traces.
Enabled() bool
// SetEnabled turns recording on or off at runtime.
SetEnabled(enabled bool)
// Reset drops every retained trace and the lifetime counters.
Reset()
// ReportError forwards a failure to Options.OnError.
ReportError(err error)
}
Recorder is the substitutable surface of a tracer: the write side the instrumentation records through, and the read side the debug front end renders from. The root package's *Tracer implements it; code that only needs to record and read back traces can depend on this interface instead of the concrete tracer.
type Sampler ¶
Sampler decides whether a request is traced. The decision is taken before a trace is allocated, so rejecting a request costs one interface call.
type SamplerFunc ¶
SamplerFunc adapts a function to the Sampler interface.
type Snapshot ¶
type Snapshot struct {
Service string `json:"service"`
StartedAt time.Time `json:"started_at"`
Uptime time.Duration `json:"uptime_ns"`
PID int `json:"pid"`
GoVersion string `json:"go_version"`
GOMAXPROCS int `json:"gomaxprocs"`
Goroutines int `json:"goroutines"`
Total uint64 `json:"total_requests"`
Sampled uint64 `json:"sampled_traces"`
Dropped uint64 `json:"dropped_traces"`
Active int `json:"active_traces"`
// Errors counts recorded traces that failed, and SLA is the share of
// recorded traces that did not, as a percentage. Requests the sampler
// rejected have unknown outcomes, so they are not in the denominator.
Errors uint64 `json:"failed_traces"`
SLA float64 `json:"sla_percent"`
StateTime []StateDuration `json:"state_time"`
Memory Memory `json:"memory"`
Pool PoolEstimate `json:"pool_estimate"`
Live []Trace `json:"live"`
Log []Trace `json:"log"`
Statistics Stats `json:"statistics"`
}
Snapshot is the complete read model of a tracer at one point in time.
type Span ¶
type Span struct {
ID int `json:"id"`
ParentID int `json:"parent_id,omitempty"`
TraceID string `json:"trace_id"`
Name string `json:"name"`
Kind Kind `json:"kind"`
StartedAt time.Time `json:"started_at"`
Duration time.Duration `json:"duration_ns,omitempty"`
Depth int `json:"depth"`
Filename string `json:"filename,omitempty"`
Line int `json:"line,omitempty"`
Attributes Attributes `json:"attributes,omitempty"`
// ErrorText is the message of the error recorded on the span. The JSON
// key stays "error"; the Go name makes room for the Error log method.
ErrorText string `json:"error,omitempty"`
// contains filtered or unexported fields
}
Span is one timed operation within a trace. Every method is safe to call on a nil span, which is what Start returns when the context carries no trace or the trace was not sampled.
func SpanFromContext ¶
SpanFromContext returns the innermost span in ctx, or nil.
func Start ¶
Start records a span in the trace carried by ctx and returns a context carrying it. When ctx has no trace, or the trace was not sampled, it returns ctx unchanged and a nil span: every span method tolerates that.
ctx, span := oida.Start(ctx, "SELECT users", oida.KindDatabase) defer span.End()
The kind is optional and defaults to KindInternal.
func StartSpan ¶
StartSpan records a span without deriving a context. Use it for leaf spans that will not nest.
func (*Span) Context ¶
Context returns a context with the span as the active parent, so spans started from it nest below this one. The context comes preallocated with the span, so one derivation per span costs nothing; a second call rebinds that same context to the new parent.
func (*Span) Elapsed ¶
Elapsed returns the recorded duration, or the time since the span started when it has not ended yet.
func (*Span) End ¶
func (s *Span) End()
End records the span duration. It is idempotent: a deferred End plus an explicit End on an error path record one duration, not two.
func (*Span) EndWithError ¶
EndWithError records err on the span and ends it.
func (*Span) Err ¶
Err returns the error recorded on the span, or nil. A span decoded from JSON kept the message and not the value, and reports an error carrying it.
func (*Span) Error ¶
Error records an error-level log entry on the trace of the span, attributed to this span; marking the transaction failed is Span.RecordError. With log capture disabled it records the text through RecordError, and it tolerates a nil span.
func (*Span) Inert ¶
Inert returns a copy of the span detached from the trace that recorded it, safe to embed in a render model or hand to a consumer. Copying a span value on its own would carry the lock and the back reference with it, and would read the fields of a span another goroutine is still recording into.
func (*Span) Info ¶
Info records an informational log entry on the trace of the span, attributed to this span, from slog-style key/value pairs. It does nothing with log capture disabled, and tolerates a nil span.
func (*Span) RecordError ¶
RecordError records an error on the span and marks the trace as failed. A nil error is ignored.
func (*Span) SetAttribute ¶
SetAttribute records a key/value pair on the span.
func (*Span) SetAttributes ¶
func (s *Span) SetAttributes(attributes Attributes)
SetAttributes records several key/value pairs on the span.
func (*Span) SourceText ¶
SourceText returns the "file:L12" location of the span, or an empty string.
type State ¶
type State string
State is the scoreboard state of an in-flight trace. The one-character values follow the convention used by servers such as lighttpd.
const ( StateWaiting State = "_" StateStarting State = "s" StateReading State = "R" StateProcessing State = "P" StateWriting State = "W" StateKeepalive State = "K" StateClosing State = "C" StateError State = "E" )
The states a trace moves through, from waiting for work to the error it ended on.
type StateDuration ¶
type StateDuration struct {
State State `json:"state"`
Label string `json:"label"`
Duration time.Duration `json:"duration_ns"`
}
StateDuration is the lifetime trace time observed in one scoreboard state.
func StateDurations ¶
func StateDurations(durations map[State]time.Duration) []StateDuration
StateDurations converts accumulated per state time into display order with shares.
type Statistic ¶
type Statistic struct {
Name string `json:"name"`
Host string `json:"host,omitempty"`
Count uint64 `json:"count"`
Errors uint64 `json:"errors"`
AverageDuration time.Duration `json:"average_duration_ns"`
MaxDuration time.Duration `json:"max_duration_ns"`
AverageResponseBytes uint64 `json:"average_response_bytes"`
AverageAllocatedBytes uint64 `json:"average_allocated_bytes"`
AverageSpans float64 `json:"average_spans"`
// contains filtered or unexported fields
}
Statistic aggregates one group of traces in the rolling window.
type Stats ¶
type Stats struct {
WindowSize int `json:"window_size"`
WindowLimit int `json:"window_limit"`
TopLimit int `json:"top_limit"`
Top []Statistic `json:"top"`
Hosts []HostStat `json:"hosts"`
}
Stats contains the most frequent trace groups in the rolling window.
func Statistics ¶
Statistics aggregates the rolling window of completed traces. Traces group by host and by routed pattern where one is known, so /users/1 and /users/2 aggregate into GET /users/{id}. Lifetime request counts per host come from the tracer, because requests the sampler rejected never became traces.
type Storage ¶
type Storage interface {
// Save retains a completed trace. The pointer is only lent for the
// call: a driver copies what it keeps, with Clone or CloneInto, and
// must not hold on to it.
Save(ctx context.Context, trace *Trace) error
// Load returns a retained trace, or ErrTraceNotFound.
Load(ctx context.Context, id string) (Trace, error)
// List returns retained traces newest first, at most limit of them. A limit
// of zero or less returns everything retained.
List(ctx context.Context, limit int) ([]Trace, error)
// Len returns the number of retained traces.
Len(ctx context.Context) (int, error)
// Cap returns the retention limit, or zero when unbounded.
Cap() int
// Reset drops every retained trace.
Reset(ctx context.Context) error
// Prune drops retained traces older than maxAge. A driver with nothing
// to prune returns nil.
Prune(ctx context.Context, maxAge time.Duration) error
// Restore fills the read path from what the driver persisted, so a new
// process can list what an earlier one recorded. A driver holding nothing
// of its own returns nil.
Restore(ctx context.Context) error
}
Storage retains completed traces. Implementations must be safe for concurrent use: the tracer writes from request goroutines and reads from the debug front end at the same time.
Two implementations ship in the storage package: a bounded ring buffer, and a bounded folder of JSON documents.
type Trace ¶
type Trace struct {
ID string `json:"id"`
Name string `json:"name"`
Service string `json:"service,omitempty"`
State State `json:"state"`
StartedAt time.Time `json:"started_at"`
UpdatedAt time.Time `json:"updated_at"`
Duration time.Duration `json:"duration_ns"`
// ErrorText is the message of the error recorded on the trace. The JSON
// key stays "error"; the Go name makes room for the Error log method.
ErrorText string `json:"error,omitempty"`
// InFlight reports whether the trace was still running when it was copied.
InFlight bool `json:"in_flight,omitempty"`
HTTP *HTTPInfo `json:"http,omitempty"`
Memory MemoryUse `json:"memory"`
// Attributes is what the transaction recorded about itself, such as the
// memory limit it ran under.
Attributes Attributes `json:"attributes,omitempty"`
Spans Spans `json:"spans,omitempty"`
DroppedSpans int `json:"dropped_spans,omitempty"`
// Logs are the log lines recorded while the trace ran, in write order,
// each linked to the span that was active when it was written. They are
// bounded by the span limit; excess entries are counted in DroppedLogs.
Logs []LogEntry `json:"logs,omitempty"`
DroppedLogs int `json:"dropped_logs,omitempty"`
// contains filtered or unexported fields
}
Trace is one recorded unit of work: an HTTP request, a background job, a cron tick or a startup step. Every method is safe to call on a nil trace.
func NewTrace ¶
func NewTrace(id, name string, opts TraceOptions) *Trace
NewTrace returns a trace ready to record spans. The recorder passes the parts of its configuration a trace needs; everything else about a trace is set by recording it.
Every trace is backed by a pooled box: the trace, its mutex, its HTTP info and slots for the first spans share one reused allocation. A trace that is never released is collected like any other value; Release is the recorder's, for the traces whose lifetime it owns end to end.
func TraceFromContext ¶
TraceFromContext returns the trace in ctx, or nil.
func (*Trace) Clone ¶
Clone returns an inert deep copy of the trace, safe to hand to snapshot consumers. Mutating the copy cannot affect the tracer.
func (*Trace) CloneInto ¶
CloneInto writes an inert deep copy of the trace into dst, reusing the allocations dst already owns when they fit: the HTTPInfo, the span block, the span pointer slice and the log slice are rewritten in place. It is how the ring buffer retains a trace without allocating for it, and Clone with an empty destination.
func (*Trace) Current ¶
Current returns the innermost open span: the most recently started span that has not ended. It is nil on a nil trace and when no span is open, so a caller holding only the trace can still attribute work to the active span.
func (*Trace) Durations ¶
Durations returns the time spent per state, including the time accumulated in the current state up to now.
func (*Trace) Elapsed ¶
Elapsed returns the recorded duration, or the time since the trace started when it is still in flight.
func (*Trace) Err ¶
Err returns the error recorded on the trace, or nil. A trace decoded from JSON kept the message and not the value, and reports an error carrying it.
func (*Trace) Error ¶
Error records an error-level log entry on the trace, attributed to the innermost open span when one is open. It only logs: the trace state and Trace.ErrorText are untouched, which is RecordError's job. When log capture is disabled it records the formatted text through RecordError instead, on the innermost open span when one is open, so the message is not lost. Safe to call on a nil trace.
func (*Trace) Finish ¶
func (t *Trace) Finish()
Finish closes the trace, ending every open span. It is idempotent.
func (*Trace) Info ¶
Info records an informational log entry on the trace, attributed to the innermost open span when one is open. No context is needed: a caller holding only the trace still lands the entry on the right span.
trace.Info("cache warmed", "keys", 128)
Args are slog-style key/value pairs, kept on LogEntry.Attributes; the message is stored verbatim. When log capture is disabled it does nothing. Safe to call on a nil trace.
func (*Trace) Kinds ¶
Kinds returns the distinct span kinds recorded in the trace, in first use order.
func (*Trace) RecordError ¶
RecordError records an error on the trace and moves it to StateError. It is what Span.RecordError calls after recording on the span. A nil error is ignored.
func (*Trace) RecordMemory ¶
func (t *Trace) RecordMemory()
RecordMemory records the process-wide allocation deltas observed while the trace ran. Concurrent traces overlap, so the values are indicative: this is the process moving, measured across one trace, not the trace on its own.
func (*Trace) Release ¶
func (t *Trace) Release()
Release returns the trace's box to the pool. The recorded values stay in place until the box is reused, so a holder may still read the trace; once NewTrace picks the box up again, the memory belongs to the new trace. The recorder releases only the traces whose lifetime it owns end to end. It is safe on a nil trace and a no-op on a clone or a decoded trace, which have no box.
func (*Trace) ReleaseOnCollect ¶
func (t *Trace) ReleaseOnCollect()
ReleaseOnCollect arranges for the box to return to the pool once the collector finds the trace unreachable: Release for a trace whose lifetime the recorder does not own, such as one handed out by StartTrace. Any holder keeps the box reachable, so the memory moves only after the last reference is gone. It is safe on a nil trace and a no-op without a box.
func (*Trace) SetAttribute ¶
SetAttribute records a key/value pair on the trace. Use it for what holds for the whole transaction; what holds for one operation belongs on its span.
func (*Trace) SetAttributes ¶
func (t *Trace) SetAttributes(attributes Attributes)
SetAttributes records several key/value pairs on the trace.
func (*Trace) SetHTTPInfo ¶
SetHTTPInfo records the request metadata of an HTTP trace, copying the value. The pointer is not retained, so a caller's stack-allocated HTTPInfo stays on the stack; a pooled trace copies into its own box. A nil info leaves the trace as it is.
func (*Trace) SetResponse ¶
SetResponse records the response metadata of an HTTP trace.
func (*Trace) SetState ¶
SetState transitions the trace state, accumulating the time spent in the previous state.
func (*Trace) StartSpan ¶
StartSpan records a span whose parent is the active span in ctx. The returned context carries the new span, so nested StartSpan calls nest below it.
func (*Trace) StateTimes ¶
StateTimes returns the time spent per state, indexed as States lists them. It is Durations without the map, for a caller aggregating many traces.
func (*Trace) TrackMemory ¶
func (t *Trace) TrackMemory()
TrackMemory records the process memory counters the trace started with, so RecordMemory can report what it allocated.
type TraceOptions ¶
type TraceOptions struct {
// Service is recorded on every trace, so a snapshot names the process it
// came from.
Service string
// MaxSpans bounds the spans one trace records. Excess spans are counted in
// Trace.DroppedSpans. It also bounds the log entries of the trace, whose
// excess is counted in Trace.DroppedLogs. Zero means unlimited.
MaxSpans int
// Clock is the time source of the trace. A nil clock uses time.Now.
Clock func() time.Time
// CaptureLogs records Info and Error log entries on the trace. Disabled,
// Info is a no-op and Error records through RecordError instead.
CaptureLogs bool
}
TraceOptions is the configuration a trace is recorded with. The recorder derives it from its own options, so this package stays free of them.