obs

package
v0.260801.2 Latest Latest
Warning

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

Go to latest
Published: Aug 4, 2026 License: MPL-2.0 Imports: 17 Imported by: 0

Documentation

Overview

Package obs provides a unified observability layer for Go applications, integrating structured logging, metrics, and distributed tracing via OpenTelemetry.

It offers simple, high-level APIs to initialize and use telemetry components without exposing underlying complexity. Ideal for microservices and cloud-native apps.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func SessionShort added in v0.260514.1

func SessionShort(sid typ.SessionID) (short, source string)

SessionShort returns a privacy-safe 16-hex-char identifier and source label for a session. Returns ("", "") when the session is empty.

Types

type ActionType

type ActionType = string

ActionType represents the type of action performed

const (
	ActionAddProvider    ActionType = "add_provider"
	ActionDeleteProvider ActionType = "delete_provider"
	ActionUpdateProvider ActionType = "update_provider"
	ActionStartServer    ActionType = "start_server"
	ActionStopServer     ActionType = "stop_server"
	ActionRestartServer  ActionType = "restart_server"
	ActionGenerateToken  ActionType = "generate_token"
	ActionUpdateDefaults ActionType = "update_defaults"
	ActionFetchModels    ActionType = "fetch_models"
)

type BatchProcessor added in v0.260514.1

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

BatchProcessor batches Records and forwards them to a RecordExporter on a periodic ticker or when the batch reaches maxBatch size.

Emit is non-blocking: if the internal queue is full the record is dropped and a counter is incremented.

func NewBatchProcessor added in v0.260514.1

func NewBatchProcessor(exporter RecordExporter, opts BatchProcessorOptions) *BatchProcessor

NewBatchProcessor creates a BatchProcessor and starts its background worker.

func (*BatchProcessor) Emit added in v0.260514.1

func (bp *BatchProcessor) Emit(r *Record)

Emit enqueues r for export. Drops the record (incrementing the dropped counter) if the internal queue is full.

func (*BatchProcessor) ForceFlush added in v0.260514.1

func (bp *BatchProcessor) ForceFlush(ctx context.Context) error

ForceFlush triggers an immediate export of any pending records and waits for it.

func (*BatchProcessor) Shutdown added in v0.260514.1

func (bp *BatchProcessor) Shutdown(ctx context.Context) error

Shutdown stops the ticker, drains the queue, exports remaining records, then shuts down the exporter.

type BatchProcessorOptions added in v0.260514.1

type BatchProcessorOptions struct {
	// QueueSize is the capacity of the internal channel (default 1024).
	QueueSize int
	// MaxBatch is the maximum number of records per Export call (default 256).
	MaxBatch int
	// FlushInterval is how often the worker drains the queue (default 5s).
	FlushInterval time.Duration
}

BatchProcessorOptions configures a BatchProcessor.

type CASFileExporter added in v0.260514.1

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

CASFileExporter implements RecordExporter with content-addressed storage:

  1. Slim-ifies every Record (large sub-values replaced by $ref pointers).
  2. Writes new blobs atomically (tmp+rename) under {baseDir}/blobs/.
  3. Appends SlimRecords to per-session JSONL files.

CASFileExporter is NOT goroutine-safe; it must be driven by a single goroutine (the BatchProcessor worker).

func NewCASFileExporter added in v0.260514.1

func NewCASFileExporter(baseDir string) (*CASFileExporter, error)

NewCASFileExporter creates a CASFileExporter rooted at baseDir and populates the in-memory blob set by scanning existing blobs.

func (*CASFileExporter) Export added in v0.260514.1

func (e *CASFileExporter) Export(_ context.Context, records []*Record) error

Export processes a batch of Records.

func (*CASFileExporter) Shutdown added in v0.260514.1

func (e *CASFileExporter) Shutdown(_ context.Context) error

Shutdown flushes and closes all open session files.

type GzipFileExporter added in v0.260514.1

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

GzipFileExporter implements RecordExporter by appending one gzip member per batch to a per-session .jsonl.gz file. Records are emitted with all bodies inlined (no $ref / blob extraction).

On-disk layout:

{baseDir}/{scenario}/sessions/{YYYY-MM-DD}/{session}.jsonl.gz

Multiple gzip members concatenated in the same file remain a valid gzip stream; standard tools (zcat, gzip -d, gzip.Reader with Multistream(true)) decompress them transparently.

func NewGzipFileExporter added in v0.260514.1

func NewGzipFileExporter(baseDir string) *GzipFileExporter

NewGzipFileExporter creates an exporter rooted at baseDir.

func (*GzipFileExporter) Export added in v0.260514.1

func (e *GzipFileExporter) Export(_ context.Context, records []*Record) error

Export groups records by session path and appends one gzip member per session.

func (*GzipFileExporter) Shutdown added in v0.260514.1

func (e *GzipFileExporter) Shutdown(_ context.Context) error

Shutdown is a no-op; GzipFileExporter holds no persistent state.

type MultiExporter added in v0.260514.1

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

MultiExporter fans Export and Shutdown calls out to multiple RecordExporters. One exporter failing does not prevent the others from running; the first non-nil error is returned.

func NewMultiExporter added in v0.260514.1

func NewMultiExporter(exporters ...RecordExporter) *MultiExporter

NewMultiExporter aggregates the given exporters behind a single RecordExporter interface. Nil exporters are skipped.

func (*MultiExporter) Export added in v0.260514.1

func (m *MultiExporter) Export(ctx context.Context, records []*Record) error

Export forwards records to every registered exporter.

func (*MultiExporter) Shutdown added in v0.260514.1

func (m *MultiExporter) Shutdown(ctx context.Context) error

Shutdown closes every registered exporter.

type Record added in v0.260514.1

type Record struct {
	Timestamp  time.Time
	RequestID  string
	SessionID  string // sha256(raw session value)[:16], empty when unknown
	SessionSrc string // "user" | "hdr" | "ip" | ""
	Provider   string
	Scenario   string
	Model      string

	// Provider connection details for debugging
	APIStyle string `json:"api_style,omitempty"` // Provider API style (e.g., "openai", "anthropic")
	BaseURL  string `json:"base_url,omitempty"`  // Provider base URL

	OriginalRequest    *RecordRequest
	TransformedRequest *RecordRequest
	ProviderResponse   *RecordResponse
	FinalResponse      *RecordResponse

	Duration time.Duration
	Err      string
	Steps    []string
}

Record is the canonical data model for one LLM request/response cycle. Construct it on the hot path and pass to Sink.Emit; the only cost is a non-blocking channel send.

type RecordExporter added in v0.260514.1

type RecordExporter interface {
	Export(ctx context.Context, records []*Record) error
	Shutdown(ctx context.Context) error
}

RecordExporter is called by BatchProcessor with a slice of Records to persist.

type RecordMode

type RecordMode string

RecordMode defines which fields are captured by the Sink.

const (
	RecordModeRequestOnly           RecordMode = "request"                 // Record transformed request only
	RecordModeRequestResponse       RecordMode = "request_response"        // Record transformed request + final response
	RecordModeStagedRequestResponse RecordMode = "staged_request_response" // Record original request + transformed request + final response
)

type RecordProcessor added in v0.260514.1

type RecordProcessor interface {
	Emit(r *Record)
	ForceFlush(ctx context.Context) error
	Shutdown(ctx context.Context) error
}

RecordProcessor is the OTel-shaped hot-path interface. Emit must be non-blocking; callers on the request goroutine must not be delayed.

type RecordRequest

type RecordRequest struct {
	Method  string                 `json:"method"`
	URL     string                 `json:"url"`
	Headers map[string]string      `json:"headers"`
	Body    map[string]interface{} `json:"body,omitempty"`
}

RecordRequest represents the HTTP request details. Kept for callers that build RecordRequest directly (e.g. client/record_roundtripper.go).

type RecordResponse

type RecordResponse struct {
	StatusCode   int                    `json:"status_code"`
	Headers      map[string]string      `json:"headers"`
	Body         map[string]interface{} `json:"body,omitempty"`
	IsStreaming  bool                   `json:"is_streaming,omitempty"`
	StreamChunks []string               `json:"-"`
}

RecordResponse represents the HTTP response details.

type ResolveOpts added in v0.260514.1

type ResolveOpts struct {
	// AllowMissingBlobs replaces missing blob refs with {"_missing_blob":"sha256:..."}
	// instead of returning an error.
	AllowMissingBlobs bool
}

ResolveOpts controls how missing blobs are handled during resolution.

type SessionIterator added in v0.260514.1

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

SessionIterator iterates over SlimRecords in a session JSONL file.

func WalkSession added in v0.260514.1

func WalkSession(baseDir, scenario, sessionShort string) (*SessionIterator, error)

WalkSession opens the JSONL file for the given scenario and session short ID under baseDir and returns an iterator.

func (*SessionIterator) Next added in v0.260514.1

func (it *SessionIterator) Next() (*SlimRecord, error)

Next advances the iterator and returns the next SlimRecord. Returns (nil, nil) at EOF.

type Sink

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

Sink manages recording of LLM request/response cycles. All writes are batched asynchronously; Emit is non-blocking.

func NewSink

func NewSink(baseDir string, mode RecordMode, opts ...SinkOption) *Sink

NewSink creates a new Sink backed by the OTel-shaped batch pipeline. Returns nil when recording is disabled (empty mode or baseDir).

Default exporter: GzipFileExporter (one gzip member per batch, per-session .jsonl.gz files). Pass WithCASExporter() to additionally write content-addressed slim JSONL + blobs.

func (*Sink) Close

func (s *Sink) Close()

Close drains pending records and shuts down the pipeline.

func (*Sink) Emit added in v0.260514.1

func (s *Sink) Emit(r *Record)

Emit enqueues r for asynchronous export. The call is non-blocking.

func (*Sink) ForceFlush added in v0.260531.1

func (s *Sink) ForceFlush(ctx context.Context) error

ForceFlush drains any pending records by delegating to the underlying processor. Used by tests that need a synchronisation point before inspecting exported records.

func (*Sink) GetBaseDir

func (s *Sink) GetBaseDir() string

GetBaseDir returns the recording root directory.

func (*Sink) GetMode added in v0.260514.1

func (s *Sink) GetMode() RecordMode

GetMode returns the configured RecordMode, or "" when the sink is nil.

func (*Sink) IsEnabled

func (s *Sink) IsEnabled() bool

IsEnabled returns whether recording is active.

func (*Sink) RecordWithScenario

func (s *Sink) RecordWithScenario(provider, model, scenario string, req *RecordRequest, resp *RecordResponse, duration time.Duration, err error)

RecordWithScenario builds a single-stage Record (original request + final response) and emits it. Used by client-side roundtrippers that don't go through the transform pipeline. Server-side code should construct a *Record directly and call Emit.

type SinkOption added in v0.260514.1

type SinkOption func(*sinkConfig)

SinkOption customises Sink construction. Apply via NewSink(baseDir, mode, opts...).

func WithCASExporter added in v0.260514.1

func WithCASExporter() SinkOption

WithCASExporter appends a CASFileExporter to the default gzip exporter. Records are written twice (once gzipped, once as content-addressed slim JSONL + blobs) — useful for cross-session analysis or replay tooling.

func WithExporters added in v0.260514.1

func WithExporters(exporters ...RecordExporter) SinkOption

WithExporters replaces the Sink's default exporter list entirely. Useful for tests and for plugging in future exporters (SQLite, OTLP, remote collectors).

type SlimHTTPData added in v0.260514.1

type SlimHTTPData struct {
	Method      string            `json:"method,omitempty"`
	URL         string            `json:"url,omitempty"`
	Headers     map[string]string `json:"headers,omitempty"`
	StatusCode  int               `json:"status_code,omitempty"`
	Body        interface{}       `json:"body,omitempty"`
	IsStreaming bool              `json:"is_streaming,omitempty"`
}

SlimHTTPData mirrors RecordRequest / RecordResponse with a body that may contain {"$ref":"sha256:<hex>"} markers instead of large inline values.

type SlimRecord added in v0.260514.1

type SlimRecord struct {
	V         int    `json:"v"` // schema version = 3
	Timestamp string `json:"ts"`
	RequestID string `json:"rid"`

	SessionID  string `json:"sid,omitempty"`
	SessionSrc string `json:"sid_src,omitempty"`

	Provider string `json:"provider,omitempty"`
	Scenario string `json:"scenario,omitempty"`
	Model    string `json:"model,omitempty"`

	OriginalRequest    *SlimHTTPData `json:"original_request,omitempty"`
	TransformedRequest *SlimHTTPData `json:"transformed_request,omitempty"`
	ProviderResponse   *SlimHTTPData `json:"provider_response,omitempty"`
	FinalResponse      *SlimHTTPData `json:"final_response,omitempty"`

	DurationMs int64  `json:"duration_ms"`
	Error      string `json:"error,omitempty"`

	Steps []string `json:"transform_steps,omitempty"`
}

SlimRecord is the JSON-serializable slim form stored in session JSONL files. Large values are replaced by {"$ref":"sha256:<hex>"} pointers into the blob store.

func FullRecord added in v0.260514.1

func FullRecord(r *Record) *SlimRecord

FullRecord returns a SlimRecord-shaped value with all fields inlined and no $ref extraction. Used by exporters that don't dedup (e.g. GzipFileExporter).

func ResolveRecord added in v0.260514.1

func ResolveRecord(baseDir string, slim *SlimRecord, opts ResolveOpts) (*SlimRecord, error)

ResolveRecord expands all {"$ref":"sha256:<hash>"} markers in a SlimRecord by loading the corresponding blobs from baseDir.

func SlimifyRecord added in v0.260514.1

func SlimifyRecord(r *Record, knownBlobs map[string]struct{}) (*SlimRecord, map[string][]byte)

SlimifyRecord converts a Record to a SlimRecord by replacing large JSON sub-values with content-addressed $ref pointers.

knownBlobs is the set of hashes already on disk; only new blobs are returned in the second return value (hash → serialised JSON bytes).

Jump to

Keyboard shortcuts

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