perf

package
v0.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 1, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package perf collects OpenTelemetry spans into an in-process tree so `ox` commands and the daemon can render per-phase timing to a local sink (stderr, daemon log) without needing the OTLP backend.

The package wraps OTel — it does NOT replace it. Spans produced via perf.Start are real OTel spans that still flow to the configured exporter. perf adds a SpanProcessor that buffers each trace's spans in memory and, on root-span end, hands the assembled tree to a sink.

Index

Constants

View Source
const PerSpanSlogThreshold = 100 * time.Millisecond

PerSpanSlogThreshold is the per-span duration at or above which a single-line slog record is emitted for that span when OX_TRACE is unset. Below this, only OX_TRACE=1 forces emission.

View Source
const SlowThreshold = 3 * time.Second

SlowThreshold is the root-trace duration at or above which the tree is rendered to stderr even when OX_TRACE is unset. Tuned for "user notices ox felt slow."

Variables

This section is empty.

Functions

func RecordError

func RecordError(span trace.Span, err error)

RecordError marks span as failed and attaches err. Convenience over repeating two OTel calls at every error site. No-op when err is nil or span is nil.

func RenderTree

func RenderTree(w io.Writer, root *Node)

RenderTree writes the node tree to w using dotted-leader alignment so durations line up in a single column.

pre-push total ........................ 4.2s
├─ resolve_push_settings ............. 0.1s
├─ git_push .......................... 1.9s
└─ cleanup ........................... 0.1s

The dot column width is computed once over the whole tree so deeply nested children stay aligned with shallow ones.

func Start

func Start(ctx context.Context, name string, attrs ...attribute.KeyValue) (context.Context, trace.Span)

Start creates a child span under the active span in ctx. The returned context carries the new span; the returned trace.Span has the usual OTel API (.End, .SetAttributes, .RecordError, .SetStatus).

Use perf.Start instead of observability.Tracer().Start so that:

  1. Callers don't have to import both observability and otel/trace.
  2. Failed spans can be marked via perf.RecordError without callers pulling in the codes package.

Safe to call when tracing is disabled: returns a no-op span whose End is a cheap no-op.

func TraceEnabled

func TraceEnabled() bool

TraceEnabled returns true if OX_TRACE is set to a truthy value. Used by sinks to gate full-detail rendering and per-span slog emission.

Types

type Node

type Node struct {
	Name     string
	SpanID   trace.SpanID
	Start    time.Time
	End      time.Time
	Duration time.Duration
	Status   sdktrace.Status
	Attrs    []attribute.KeyValue
	Children []*Node

	// Detached is true when the span's parent ended before it did and the
	// parent's tree was already emitted, or when its parent SpanID was not
	// present in the trace's pending set. Detached spans render under a
	// synthetic "<detached>" node so the user sees the timing rather than
	// silently dropping it.
	Detached bool
}

Node is a span captured by the processor with its children attached. Trees are rooted at the trace's root span (no valid parent SpanID).

type Options

type Options struct {
	OnSpan SpanSink
	OnTree TreeSink
}

Options configures a TreeCollectorProcessor.

type SpanSink

type SpanSink func(s sdktrace.ReadOnlySpan)

SpanSink is called for every closed span (one call per OnEnd). Used to emit per-span slog records when OX_TRACE=1 or duration exceeds a threshold. Nil is a no-op.

func NoopSpanSink

func NoopSpanSink() SpanSink

NoopSpanSink returns a span sink that does nothing. Useful in tests or when only tree-level emission is desired.

func PerSpanSlog

func PerSpanSlog(logger *slog.Logger) SpanSink

PerSpanSlog returns a SpanSink that emits one slog record per closed span when either:

  • OX_TRACE is truthy, OR
  • the span's duration meets PerSpanSlogThreshold.

Without this gate a 5-second daemon sync loop would produce thousands of info-level lines per hour from no-op pull dedup spans, which would dominate `ox daemon logs` and worsen disk pressure.

The emitted record is single-line key=value (project convention) so it stays greppable and aggregator-friendly.

type TreeCollectorProcessor

type TreeCollectorProcessor struct {
	// contains filtered or unexported fields
}

TreeCollectorProcessor is an OTel sdktrace.SpanProcessor that buffers spans per trace and, when each trace's root span ends, builds a Node tree and calls Options.OnTree with it.

Concurrency: the OTel SDK may call OnEnd from multiple goroutines for spans in the same or different traces. All map mutations are guarded by mu.

func NewTreeProcessor

func NewTreeProcessor(opts Options) *TreeCollectorProcessor

NewTreeProcessor returns a processor ready to register with an OTel TracerProvider via sdktrace.WithSpanProcessor.

func (*TreeCollectorProcessor) ForceFlush

func (p *TreeCollectorProcessor) ForceFlush(_ context.Context) error

ForceFlush is a no-op. Trees emit synchronously on root OnEnd.

func (*TreeCollectorProcessor) OnEnd

OnEnd buffers the span. If the span is the root of its trace, we assemble the tree and hand it to OnTree.

func (*TreeCollectorProcessor) OnStart

OnStart is part of the SpanProcessor interface. We don't need it.

func (*TreeCollectorProcessor) Shutdown

Shutdown drops any pending spans. No-op flush — the underlying exporter handles real flushing.

type TreeSink

type TreeSink func(root *Node)

TreeSink is called once per closed trace, with the assembled root Node. Used to render the tree to stderr or to the daemon log. Nil is a no-op.

func CLITreeSink

func CLITreeSink(w io.Writer, verbose bool) TreeSink

CLITreeSink renders the root tree to w when:

  • OX_TRACE is truthy, OR
  • verbose is true (-v / --verbose), OR
  • the root trace took at least SlowThreshold (auto-surface).

In the auto-surface case, a footer hint about OX_TRACE is appended so users discover the env var the next time they want detail.

w is typically os.Stderr; pass a buffer in tests.

func DaemonTreeSink

func DaemonTreeSink(logger *slog.Logger) TreeSink

DaemonTreeSink renders the root tree to the daemon logger at debug level. Tree blocks land in the daemon log only when the operator opts in via OX_TRACE=1 (env var inherited by the daemon child process) — otherwise per-span slog records (from PerSpanSlog below) are sufficient for grep + jq workflows without inflating the log.

func NoopTreeSink

func NoopTreeSink() TreeSink

NoopTreeSink returns a tree sink that does nothing. Useful in tests or when only per-span emission is desired.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL