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 ¶
- func SessionShort(sid typ.SessionID) (short, source string)
- type ActionType
- type BatchProcessor
- type BatchProcessorOptions
- type CASFileExporter
- type GzipFileExporter
- type MultiExporter
- type Record
- type RecordExporter
- type RecordMode
- type RecordProcessor
- type RecordRequest
- type RecordResponse
- type ResolveOpts
- type SessionIterator
- type Sink
- func (s *Sink) Close()
- func (s *Sink) Emit(r *Record)
- func (s *Sink) ForceFlush(ctx context.Context) error
- func (s *Sink) GetBaseDir() string
- func (s *Sink) GetMode() RecordMode
- func (s *Sink) IsEnabled() bool
- func (s *Sink) RecordWithScenario(provider, model, scenario string, req *RecordRequest, resp *RecordResponse, ...)
- type SinkOption
- type SlimHTTPData
- type SlimRecord
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func SessionShort ¶ added in v0.260514.1
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.
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:
- Slim-ifies every Record (large sub-values replaced by $ref pointers).
- Writes new blobs atomically (tmp+rename) under {baseDir}/blobs/.
- 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.
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.
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.
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
Emit enqueues r for asynchronous export. The call is non-blocking.
func (*Sink) ForceFlush ¶ added in v0.260531.1
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 ¶
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) 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).