Documentation
¶
Overview ¶
Package wool is a lightweight, pluggable telemetry layer for codefly.
Loggers ("wools") attach to a context with structured fields and a hierarchy ("module/component/method"); messages are routed through a pluggable Provider that ships them to stdout, gRPC, OpenTelemetry, or any custom sink. Levels in increasing severity are TRACE, DEBUG, INFO, FOCUS, WARN, ERROR — FOCUS is a highlighted milestone shown at INFO and above. Per-scope level overrides come from CODEFLY_LOG (see SetLogScopes).
Typical usage:
w := wool.Get(ctx).In("MyMethod", wool.Field("user", id))
w.Trace("starting work")
if err := doThing(); err != nil {
return w.Wrapf(err, "could not do thing")
}
w.Info("done")
Core packages and their use:
- wool — log levels, fields, error wrapping (this package)
- wool/log — stdout / formatted log provider
- wool/grpc — gRPC log streaming provider
- wool/otel — OpenTelemetry provider
Index ¶
- Constants
- Variables
- func GRPCInstrumentation() []interface{}
- func Header(key ContextKey) string
- func HeaderKey(header string) string
- func Headers() []string
- func IsDebug() bool
- func MetadataFromRequest(_ context.Context, req *http.Request) metadata.MD
- func NotFound(what ContextKey) error
- func RegisterTelemetry(tp TelemetryProvider)
- func SetFallbackLogger(l LogProcessor)
- func SetGlobalLogLevel(loglevel Loglevel)
- func SetLogScopes(spec string)
- func SetRethrowAfterCatch(v bool)
- func TelemetryEnabled() bool
- func TypeOf[T any]() string
- type BufferedProcessor
- type Console
- type ContextKey
- type GRPC
- type HTTP
- type Identifier
- type Log
- type LogField
- func DirField(dir string) *LogField
- func ErrField(err error) *LogField
- func Field(key string, value any) *LogField
- func FileField(file string) *LogField
- func FocusField() *LogField
- func GenericField[T any]() *LogField
- func InField(s string) *LogField
- func ModuleField(name string) *LogField
- func NameField(name string) *LogField
- func NullableField[T any](key string, value T) *LogField
- func Path(dir string) *LogField
- func PointerField[T any](override *T) *LogField
- func RequestField(req any) *LogField
- func ResponseField(req any) *LogField
- func SecretField(key string, _ any) *LogField
- func ServiceField(name string) *LogField
- func SliceCountField[T any](slice []T) *LogField
- func SliceField[T any](key string, items []T) *LogField
- func StatusFailed() *LogField
- func StatusOK() *LogField
- func ThisField(this Unique) *LogField
- func WorkspaceField(name string) *LogField
- func Writer() *LogField
- type LogFunc
- type LogProcessor
- type LogProcessorWithSource
- type Loglevel
- type NotFoundError
- type Provider
- func (p *Provider) Done()
- func (p *Provider) Get(ctx context.Context) *Wool
- func (p *Provider) Inject(ctx context.Context) context.Context
- func (p *Provider) WithConsole(lvl Loglevel) *Provider
- func (p *Provider) WithLogger(l LogProcessor) *Provider
- func (p *Provider) WithTelemetry(tp TelemetryProvider) *Provider
- type Resource
- type Span
- type TelemetryProvider
- type Tracer
- type Unique
- type Wool
- func (w *Wool) Catch()
- func (w *Wool) Close() error
- func (w *Wool) Context() context.Context
- func (w *Wool) Debug(msg string, fields ...*LogField)
- func (w *Wool) DisableCatch()
- func (w *Wool) Error(msg string, fields ...*LogField)
- func (w *Wool) Fatal(msg string, fields ...*LogField)
- func (w *Wool) Focus(msg string, fields ...*LogField)
- func (w *Wool) Forward(p []byte) (n int, err error)
- func (w *Wool) Forwardf(msg string, args ...any)
- func (w *Wool) GRPC() *GRPC
- func (w *Wool) HTTP() *HTTP
- func (w *Wool) In(method string, fields ...*LogField) *Wool
- func (w *Wool) Info(msg string, fields ...*LogField)
- func (w *Wool) Inject(ctx context.Context) context.Context
- func (w *Wool) LogLevel() Loglevel
- func (w *Wool) Name() string
- func (w *Wool) NewError(format string, args ...any) error
- func (w *Wool) OrgID() (string, bool)
- func (w *Wool) Roles() (string, bool)
- func (w *Wool) Source() *Identifier
- func (w *Wool) Trace(msg string, fields ...*LogField)
- func (w *Wool) UserAuthID() (string, bool)
- func (w *Wool) UserEmail() (string, bool)
- func (w *Wool) UserID() (string, bool)
- func (w *Wool) UserName() (string, bool)
- func (w *Wool) Warn(msg string, fields ...*LogField)
- func (w *Wool) With(fields ...*LogField) *Wool
- func (w *Wool) WithLogger(l LogProcessor) *Wool
- func (w *Wool) WithLoglevel(level Loglevel)
- func (w *Wool) WithOrgID(id string)
- func (w *Wool) WithUserAuthID(authID string)
- func (w *Wool) WithUserEmail(s string)
- func (w *Wool) WithUserID(id string)
- func (w *Wool) Wrap(err error) error
- func (w *Wool) Wrapf(err error, msg string, args ...any) error
- func (w *Wool) Write(p []byte) (n int, err error)
Constants ¶
const LogEvent = "log"
const ProviderKey = ContextKey("provider")
Variables ¶
var ContextKeys []ContextKey
ContextKeys lists all user identity keys for iteration.
var HTTPMappings = map[string]ContextKey{ "X-User-Id": UserIDKey, "X-Org-Id": OrgIDKey, "X-Roles": RolesKey, "X-Auth-Id": UserAuthIDKey, "X-User-Email": UserEmailKey, "X-User-Name": UserNameKey, }
HTTPMappings maps HTTP header names to context keys. Uses standard header conventions (X-User-Id, X-Org-Id, etc.) for interoperability with API gateways and external tools.
Functions ¶
func GRPCInstrumentation ¶ added in v0.1.123
func GRPCInstrumentation() []interface{}
GRPCInstrumentation returns gRPC server options for instrumentation.
func Header ¶ added in v0.1.123
func Header(key ContextKey) string
Header returns the HTTP header name for a context key.
func Headers ¶ added in v0.1.124
func Headers() []string
Headers returns all known HTTP header names.
func IsDebug ¶ added in v0.0.60
func IsDebug() bool
IsDebug returns true if the global log level is DEBUG or TRACE.
func MetadataFromRequest ¶ added in v0.1.123
MetadataFromRequest extracts gRPC metadata from an HTTP request, mapping known HTTP headers to context keys.
func NotFound ¶ added in v0.1.17
func NotFound(what ContextKey) error
func RegisterTelemetry ¶ added in v0.1.155
func RegisterTelemetry(tp TelemetryProvider)
RegisterTelemetry sets the global telemetry provider. Typically called from an init() in a backend package (e.g. wool/otel).
func SetFallbackLogger ¶ added in v0.1.155
func SetFallbackLogger(l LogProcessor)
SetFallbackLogger sets (or clears) the global fallback LogProcessor. Pass nil to revert to the default Console logger.
func SetGlobalLogLevel ¶ added in v0.0.65
func SetGlobalLogLevel(loglevel Loglevel)
SetGlobalLogLevel sets the global log level (thread-safe).
func SetLogScopes ¶ added in v0.2.12
func SetLogScopes(spec string)
SetLogScopes installs per-scope level overrides, replacing any previously set (including the CODEFLY_LOG defaults loaded at init). The spec is a comma list of "scope=level" entries, e.g. "network=debug,resources=info,*=warn"; "*" is the catch-all. A scope matches when its prefix lines up with a leading segment of the .In(...) name (see scopeMatches); the longest matching prefix wins. Unparseable entries are ignored.
func SetRethrowAfterCatch ¶ added in v0.2.11
func SetRethrowAfterCatch(v bool)
SetRethrowAfterCatch toggles whether Catch re-raises after logging.
func TelemetryEnabled ¶ added in v0.1.155
func TelemetryEnabled() bool
TelemetryEnabled returns true if a telemetry provider has been registered.
Types ¶
type BufferedProcessor ¶ added in v0.1.155
type BufferedProcessor struct {
// contains filtered or unexported fields
}
BufferedProcessor wraps a LogProcessor with channel-based async batching. Log messages are queued and processed by a background goroutine.
func NewBufferedProcessor ¶ added in v0.1.155
func NewBufferedProcessor(inner LogProcessor, bufferSize int) *BufferedProcessor
NewBufferedProcessor creates a buffered wrapper around a LogProcessor. bufferSize controls the channel capacity.
func (*BufferedProcessor) Flush ¶ added in v0.1.155
func (bp *BufferedProcessor) Flush()
Flush closes the buffer and waits for all queued messages to be processed.
func (*BufferedProcessor) Process ¶ added in v0.1.155
func (bp *BufferedProcessor) Process(msg *Log)
Process queues a log message for async processing. If the buffer is full, the message is dropped (non-blocking). Safe to call concurrently with (and after) Flush: once Flushed, messages are dropped rather than sent on the closed channel, which would panic.
type Console ¶ added in v0.0.57
type Console struct {
// contains filtered or unexported fields
}
Console is a simple stdout LogProcessor.
func NewMessageConsole ¶ added in v0.1.89
func NewMessageConsole() *Console
NewMessageConsole returns a Console that only prints the message text.
type ContextKey ¶
type ContextKey string
const ( // Standard identity headers (set by auth sidecar) UserIDKey ContextKey = "user.id" OrgIDKey ContextKey = "org.id" RolesKey ContextKey = "user.roles" // Auth provider identity (from JWT/gateway) UserAuthIDKey ContextKey = "user.auth.id" UserEmailKey ContextKey = "user.email" UserNameKey ContextKey = "user.name" )
User identity context keys for propagation across service boundaries.
type GRPC ¶ added in v0.1.123
type GRPC struct {
// contains filtered or unexported fields
}
GRPC returns a GRPC helper for metadata propagation. Backwards-compatible wrapper.
type HTTP ¶ added in v0.1.123
type HTTP struct {
// contains filtered or unexported fields
}
HTTP provides HTTP header propagation for user identity.
type Identifier ¶ added in v0.0.55
Identifier holds the kind and unique name of a wool source.
func (*Identifier) IsSystem ¶ added in v0.0.67
func (identifier *Identifier) IsSystem() bool
IsSystem returns true if this is the system identifier.
type Log ¶
type Log struct {
Level Loglevel `json:"level"`
Header string `json:"header"`
Message string `json:"message"`
Fields []*LogField `json:"fields"`
}
Log represents a structured log entry.
type LogField ¶
type LogField struct {
Key string `json:"key"`
Level Loglevel `json:"level"`
Value any `json:"value"`
}
LogField is a key-value pair with an associated log level. A field is shown only if the log level is equal or higher than the field's level.
func ErrField ¶ added in v0.0.55
ErrField wraps an error as a structured log field. Tolerates a nil err (returns "<nil>" rather than panicking) so it's safe to call from `defer Wool.Catch()` recovery paths and conditional-logging branches where `err` may legitimately be nil.
func FocusField ¶ added in v0.0.69
func FocusField() *LogField
func GenericField ¶ added in v0.0.55
func ModuleField ¶ added in v0.1.89
func NullableField ¶ added in v0.0.81
func PointerField ¶ added in v0.0.57
func RequestField ¶ added in v0.0.55
func ResponseField ¶ added in v0.0.67
func SecretField ¶ added in v0.2.12
SecretField redacts the value at construction: the raw secret is dropped and never reaches any sink (console, file, gRPC, telemetry). The field always renders "****". This makes redaction a logging-layer guarantee rather than a convention each call site has to remember.
func ServiceField ¶ added in v0.1.17
func SliceCountField ¶ added in v0.0.57
func SliceField ¶ added in v0.2.12
SliceField formats a slice as a readable list (see sliceValue) so call sites can log domain collections without dumping internal struct layout.
func StatusFailed ¶ added in v0.0.55
func StatusFailed() *LogField
func WorkspaceField ¶ added in v0.1.89
type LogProcessor ¶ added in v0.0.55
type LogProcessor interface {
Process(msg *Log)
}
LogProcessor is the sink interface for log messages.
type LogProcessorWithSource ¶ added in v0.0.55
type LogProcessorWithSource interface {
ProcessWithSource(source *Identifier, msg *Log)
}
LogProcessorWithSource extends LogProcessor with source tracking.
type Loglevel ¶
type Loglevel int
Loglevel defines log severity.
Levels are ordered by severity; a message is shown when its level is >= the effective log level (see Wool.LogLevel). FOCUS sits just above INFO and below WARN: it is a highlighted milestone. At the default INFO level FOCUS lines are shown (the highlight a user wants to see); running at FOCUS hides routine INFO chatter while keeping milestones, warnings and errors — the "signal only" view. FOCUS must stay above INFO so it is never accidentally filtered out by an INFO-level run.
Choosing a level — the bar that keeps the default (INFO) stream readable:
- TRACE: pure internal bookkeeping only a codefly-internals dev would want — per-step load/resolve cascades, hash computation, "sending request", "running natively", "loaded agent pid=…". Never shown by default; a real workspace (15+ services) emits these dozens of times.
- DEBUG: diagnostics worth seeing when something is actually wrong, but still too noisy for a normal run — fallback paths taken, retries, resolved binary paths, GitHub-lookup failures. Surfaced with --debug or CODEFLY_LOG.
- INFO: a small, high-value set per service — one aggregated resolution line, "Will run N service(s)", lifecycle milestones. If a line repeats once per service or per agent and carries no new fact, it belongs at TRACE.
- FOCUS: highlighted milestones (>> lines). WARN/ERROR: real problems. A warning that fires identically for every agent should be emitted once, not once per agent.
func GlobalLogLevel ¶ added in v0.0.65
func GlobalLogLevel() Loglevel
GlobalLogLevel returns the current global log level (thread-safe).
func LevelFromString ¶ added in v0.2.12
LevelFromString resolves a level name (case-insensitive, e.g. "debug") to a Loglevel. The second return is false for an unrecognized name.
type NotFoundError ¶ added in v0.1.17
type NotFoundError struct {
// contains filtered or unexported fields
}
func (*NotFoundError) Error ¶ added in v0.1.17
func (err *NotFoundError) Error() string
type Provider ¶ added in v0.0.55
type Provider struct {
// contains filtered or unexported fields
}
Provider creates Wool instances and manages an optional telemetry backend.
func New ¶
New creates a new Provider for the given resource. If a TelemetryProvider has been registered (via RegisterTelemetry or WithTelemetry), tracing is automatically enabled.
func (*Provider) Done ¶ added in v0.0.55
func (p *Provider) Done()
Done shuts down telemetry if enabled.
func (*Provider) WithConsole ¶ added in v0.0.69
WithConsole sets a console logger at the given level.
func (*Provider) WithLogger ¶ added in v0.0.55
func (p *Provider) WithLogger(l LogProcessor) *Provider
WithLogger sets a custom log processor.
func (*Provider) WithTelemetry ¶ added in v0.1.155
func (p *Provider) WithTelemetry(tp TelemetryProvider) *Provider
WithTelemetry explicitly sets a telemetry provider for this provider.
type Span ¶ added in v0.1.155
type Span interface {
// AddEvent records a log entry as a span event.
AddEvent(name string, fields []*LogField)
// End completes the span.
End()
}
Span represents an active trace span. Implementations are provided by telemetry backends (e.g. wool/otel).
type TelemetryProvider ¶ added in v0.1.155
type TelemetryProvider interface {
// NewTracer creates a Tracer scoped to the given name.
NewTracer(name string) Tracer
// Shutdown flushes and closes the telemetry backend.
Shutdown(ctx context.Context) error
}
TelemetryProvider is the bridge between wool and a tracing backend. Register one via RegisterTelemetry or pass it to Provider.WithTelemetry.
func GetTelemetry ¶ added in v0.1.155
func GetTelemetry() TelemetryProvider
GetTelemetry returns the registered global telemetry provider, or nil.
type Tracer ¶ added in v0.1.155
type Tracer interface {
// Start creates a new span. The returned context carries the span.
Start(ctx context.Context, name string) (context.Context, Span)
}
Tracer creates spans.
type Wool ¶
type Wool struct {
// contains filtered or unexported fields
}
Wool is the main observability handle. It combines structured logging, optional tracing (via TelemetryProvider), and context propagation.
Usage:
w := wool.Get(ctx).In("MyService.Handle")
w.Info("processing request", wool.Field("id", 42))
if err != nil {
return w.Wrapf(err, "cannot process")
}
func Get ¶
Get retrieves or creates a Wool instance from the context. If no provider is found, falls back to a Console logger.
wool.Get is on essentially every function's hot path, so it avoids work on the common (provider-present) branch: no stack walk, no throwaway error, and getFallbackLogger() (which takes a lock + allocates a Console) is only called when there is genuinely no provider logger to use.
func StartSpan ¶ added in v0.1.163
StartSpan starts a named tracing span and returns a Wool bound to it plus an end func the caller MUST defer:
w, end := wool.StartSpan(ctx, "MyOp") defer end()
Log lines emitted by w (and any wool.Get on the returned context chain) attach as events to this span. When no telemetry provider/tracer is configured it is a no-op (end is a no-op too), so it is always safe to call.
func (*Wool) Catch ¶ added in v0.0.55
func (w *Wool) Catch()
Catch recovers from a panic and logs the error. Use with defer: defer w.Catch()
func (*Wool) DisableCatch ¶ added in v0.1.89
func (w *Wool) DisableCatch()
func (*Wool) In ¶ added in v0.0.55
In creates a scoped child Wool with a method name and optional fields.
func (*Wool) Inject ¶ added in v0.0.67
Inject stores the provider in the context for later retrieval via Get. Inject persists this Wool's provider on ctx so downstream code that calls wool.Get(ctx) sees the same call-chain context. No-op when no provider is registered (test harnesses, agent boot before telemetry is wired) — without this guard, the previous version nil-deref'd and brought down the calling goroutine.
func (*Wool) LogLevel ¶ added in v0.1.20
LogLevel returns the effective log level for this Wool instance. A zero-value (DEFAULT) instance level means "inherit the global level" — otherwise an instance never filters and custom processors (gRPC streaming, buffered, agent loggers) receive every TRACE/DEBUG line regardless of the configured level.
func (*Wool) Source ¶ added in v0.0.67
func (w *Wool) Source() *Identifier
func (*Wool) UserAuthID ¶ added in v0.1.34
func (*Wool) WithLogger ¶ added in v0.0.55
func (w *Wool) WithLogger(l LogProcessor) *Wool