Documentation
¶
Index ¶
- Constants
- Variables
- func AllocEncoderSlot() int
- func CallerPC(skip int) uintptr
- func ContextWithBag(ctx context.Context, bag *Bag) context.Context
- func DefaultErrorEncoder(key string, err error, enc FieldEncoder)
- func DefaultLevelEncoder(lvl Level, m TypeEncoder)
- func EscapeString[S []byte | string](buf *Buffer, s S) error
- func FloatSecondsDurationEncoder(d time.Duration, e TypeEncoder)
- func FullCallerEncoder(pc uintptr, m TypeEncoder)
- func HasField(ctx context.Context, key string) bool
- func LogDepth(l *Logger, ctx context.Context, depth int, lvl Level, text string, fs ...Field)
- func NanoDurationEncoder(d time.Duration, e TypeEncoder)
- func NewContext(parent context.Context, logger *Logger) context.Context
- func NewSlogHandler(w Handler) slog.Handler
- func RFC3339NanoTimeEncoder(t time.Time, e TypeEncoder)
- func RFC3339TimeEncoder(t time.Time, e TypeEncoder)
- func ShortCallerEncoder(pc uintptr, m TypeEncoder)
- func ShortTextLevelEncoder(lvl Level, m TypeEncoder)
- func StringDurationEncoder(d time.Duration, m TypeEncoder)
- func UnixNanoTimeEncoder(t time.Time, e TypeEncoder)
- func UpperCaseLevelEncoder(lvl Level, m TypeEncoder)
- func With(ctx context.Context, fs ...Field) context.Context
- type ArrayEncoder
- type Bag
- func (b *Bag) Fields() []Field
- func (b *Bag) Group() string
- func (b *Bag) HasField(key string) bool
- func (b *Bag) LoadCache(slot int) []byte
- func (b *Bag) OwnFields() []Field
- func (b *Bag) Parent() *Bag
- func (b *Bag) StoreCache(slot int, data []byte)
- func (b *Bag) With(fs ...Field) *Bag
- func (b *Bag) WithGroup(name string) *Bag
- type Buffer
- func (b *Buffer) AppendBool(n bool)
- func (b *Buffer) AppendByte(data byte)
- func (b *Buffer) AppendBytes(data []byte)
- func (b *Buffer) AppendFloat32(n float32)
- func (b *Buffer) AppendFloat64(n float64)
- func (b *Buffer) AppendInt(n int64)
- func (b *Buffer) AppendString(data string)
- func (b *Buffer) AppendUint(n uint64)
- func (b *Buffer) Back() byte
- func (b *Buffer) Bytes() []byte
- func (b *Buffer) Cap() int
- func (b *Buffer) EnsureSize(s int)
- func (b *Buffer) ExtendBytes(s int) []byte
- func (b *Buffer) Free()
- func (b *Buffer) Len() int
- func (b *Buffer) Reset()
- func (b *Buffer) String() string
- func (b *Buffer) Truncate(n int)
- func (b *Buffer) Write(p []byte) (n int, err error)
- type CallerEncoder
- type ContextHandler
- type DurationEncoder
- type Encoder
- type EncoderBuilder
- type Entry
- type ErrorEncoder
- type ErrorEncoderConfig
- type Field
- func Any(k string, v interface{}) Field
- func Array(k string, v ArrayEncoder) Field
- func Bool(k string, v bool) Field
- func ByteString(k string, v []byte) Field
- func Bytes(k string, v []byte) Field
- func Duration(k string, v time.Duration) Field
- func Durations(k string, v []time.Duration) Field
- func Error(v error) Field
- func Fields(ctx context.Context) []Field
- func Float32(k string, v float32) Field
- func Float64(k string, v float64) Field
- func Floats64(k string, v []float64) Field
- func Formatter(k string, verb string, v interface{}) Field
- func FormatterV(k string, v interface{}) Field
- func Group(k string, fs ...Field) Field
- func Inline(v ObjectEncoder) Field
- func Int(k string, v int) Field
- func Int8(k string, v int8) Field
- func Int16(k string, v int16) Field
- func Int32(k string, v int32) Field
- func Int64(k string, v int64) Field
- func Ints(k string, v []int) Field
- func Ints64(k string, v []int64) Field
- func NamedError(k string, v error) Field
- func Object(k string, v ObjectEncoder) Field
- func String(k string, v string) Field
- func Stringer(k string, v fmt.Stringer) Field
- func Strings(k string, v []string) Field
- func Time(k string, v time.Time) Field
- func Uint(k string, v uint) Field
- func Uint8(k string, v uint8) Field
- func Uint16(k string, v uint16) Field
- func Uint32(k string, v uint32) Field
- func Uint64(k string, v uint64) Field
- type FieldEncoder
- type FieldSource
- type FieldType
- type Handler
- type JSONEncoderBuilder
- func (b *JSONEncoderBuilder) Build() Encoder
- func (b *JSONEncoderBuilder) CallerKey(k string) *JSONEncoderBuilder
- func (b *JSONEncoderBuilder) DisableCaller() *JSONEncoderBuilder
- func (b *JSONEncoderBuilder) DisableLevel() *JSONEncoderBuilder
- func (b *JSONEncoderBuilder) DisableMsg() *JSONEncoderBuilder
- func (b *JSONEncoderBuilder) DisableName() *JSONEncoderBuilder
- func (b *JSONEncoderBuilder) DisableTime() *JSONEncoderBuilder
- func (b *JSONEncoderBuilder) EncodeCaller(e CallerEncoder) *JSONEncoderBuilder
- func (b *JSONEncoderBuilder) EncodeDuration(e DurationEncoder) *JSONEncoderBuilder
- func (b *JSONEncoderBuilder) EncodeError(e ErrorEncoder) *JSONEncoderBuilder
- func (b *JSONEncoderBuilder) EncodeLevel(e LevelEncoder) *JSONEncoderBuilder
- func (b *JSONEncoderBuilder) EncodeTime(e TimeEncoder) *JSONEncoderBuilder
- func (b *JSONEncoderBuilder) LevelKey(k string) *JSONEncoderBuilder
- func (b *JSONEncoderBuilder) MsgKey(k string) *JSONEncoderBuilder
- func (b *JSONEncoderBuilder) NameKey(k string) *JSONEncoderBuilder
- func (b *JSONEncoderBuilder) TimeKey(k string) *JSONEncoderBuilder
- type JSONEncoderConfig
- type Level
- type LevelEncoder
- type LogFunc
- type Logger
- func (l *Logger) AtLevel(ctx context.Context, lvl Level, fn func(LogFunc))
- func (l *Logger) Debug(ctx context.Context, text string, fs ...Field)
- func (l *Logger) Debugx(text string, fs ...Field)
- func (l *Logger) Enabled(ctx context.Context, lvl Level) bool
- func (l *Logger) Error(ctx context.Context, text string, fs ...Field)
- func (l *Logger) Errorx(text string, fs ...Field)
- func (l *Logger) Info(ctx context.Context, text string, fs ...Field)
- func (l *Logger) Infox(text string, fs ...Field)
- func (l *Logger) Log(ctx context.Context, lvl Level, text string, fs ...Field)
- func (l *Logger) Slog() *slog.Logger
- func (l *Logger) Warn(ctx context.Context, text string, fs ...Field)
- func (l *Logger) Warnx(text string, fs ...Field)
- func (l *Logger) With(fs ...Field) *Logger
- func (l *Logger) WithCaller(enabled bool) *Logger
- func (l *Logger) WithCallerSkip(skip int) *Logger
- func (l *Logger) WithGroup(name string) *Logger
- func (l *Logger) WithName(n string) *Logger
- type LoggerBuilder
- func (b *LoggerBuilder) Build() *Logger
- func (b *LoggerBuilder) Context(sources ...FieldSource) *LoggerBuilder
- func (b *LoggerBuilder) Encoder(enc Encoder) *LoggerBuilder
- func (b *LoggerBuilder) EncoderFrom(eb EncoderBuilder) *LoggerBuilder
- func (b *LoggerBuilder) Level(l Level) *LoggerBuilder
- func (b *LoggerBuilder) Output(w io.Writer) *LoggerBuilder
- type MutableLevel
- type ObjectEncoder
- type RouteOption
- type RouterBuilder
- type SlabStats
- type SlabWriter
- type SlabWriterBuilder
- func (b *SlabWriterBuilder) Build() *SlabWriter
- func (b *SlabWriterBuilder) DropOnFull() *SlabWriterBuilder
- func (b *SlabWriterBuilder) ErrorWriter(w io.Writer) *SlabWriterBuilder
- func (b *SlabWriterBuilder) FlushInterval(d time.Duration) *SlabWriterBuilder
- func (b *SlabWriterBuilder) SlabCount(n int) *SlabWriterBuilder
- func (b *SlabWriterBuilder) SlabSize(n int) *SlabWriterBuilder
- type TextEncoderBuilder
- func (b *TextEncoderBuilder) Build() Encoder
- func (b *TextEncoderBuilder) DisableCaller() *TextEncoderBuilder
- func (b *TextEncoderBuilder) DisableLevel() *TextEncoderBuilder
- func (b *TextEncoderBuilder) DisableMsg() *TextEncoderBuilder
- func (b *TextEncoderBuilder) DisableName() *TextEncoderBuilder
- func (b *TextEncoderBuilder) DisableTime() *TextEncoderBuilder
- func (b *TextEncoderBuilder) EncodeCaller(e CallerEncoder) *TextEncoderBuilder
- func (b *TextEncoderBuilder) EncodeDuration(e DurationEncoder) *TextEncoderBuilder
- func (b *TextEncoderBuilder) EncodeError(e ErrorEncoder) *TextEncoderBuilder
- func (b *TextEncoderBuilder) EncodeLevel(e LevelEncoder) *TextEncoderBuilder
- func (b *TextEncoderBuilder) EncodeTime(e TimeEncoder) *TextEncoderBuilder
- func (b *TextEncoderBuilder) NoColor() *TextEncoderBuilder
- type TextEncoderConfig
- type TimeEncoder
- type TypeEncoder
- type TypeEncoderFactory
- type Writer
- type WriterSlot
- type WriterSlotOption
Constants ¶
const ( DefaultFieldKeyLevel = "level" DefaultFieldKeyMsg = "msg" DefaultFieldKeyTime = "ts" DefaultFieldKeyName = "logger" DefaultFieldKeyCaller = "caller" )
Default field keys.
const (
PageSize = 4 * 1024
)
PageSize is the recommended buffer size.
Variables ¶
var NewErrorEncoder = errorEncoderGetter( func(c ErrorEncoderConfig) ErrorEncoder { return func(key string, err error, enc FieldEncoder) { encodeError(key, err, enc, c.WithDefaults()) } }, )
NewErrorEncoder creates an ErrorEncoder with the given config. Call it as a function: NewErrorEncoder(cfg) returns an ErrorEncoder.
Functions ¶
func AllocEncoderSlot ¶
func AllocEncoderSlot() int
AllocEncoderSlot returns a unique 1-based slot index for an encoder to use with Bag.LoadCache and Bag.StoreCache. Call this once when you create an encoder — the slot lets the Bag cache encoded bytes per encoder format so repeated encoding is nearly free.
If all slots are taken, returns 0 (no caching, graceful degradation — everything still works, just without the cache speedup).
func CallerPC ¶
CallerPC captures the program counter of the caller, skipping the given number of stack frames. Returns 0 if the caller cannot be determined. You usually do not need to call this directly — the Logger handles it.
func ContextWithBag ¶
ContextWithBag returns a new context carrying the given Bag. This is the low-level API — most callers should use logf.With(ctx, fields...) instead, which handles Bag creation and chaining automatically.
func DefaultErrorEncoder ¶
func DefaultErrorEncoder(key string, err error, enc FieldEncoder)
DefaultErrorEncoder encodes an error as one or two fields: the error message under the given key, and (if the error implements fmt.Formatter) a verbose field with the full "%+v" output (stack traces, etc.).
func DefaultLevelEncoder ¶
func DefaultLevelEncoder(lvl Level, m TypeEncoder)
DefaultLevelEncoder formats levels as lower-case strings ("debug", "info", "warn", "error"). This is the default for JSON output.
func EscapeString ¶
EscapeString JSON-escapes s (which can be a string or []byte) and appends the result to buf. It handles control characters, backslash, quotes, and invalid UTF-8 sequences.
func FloatSecondsDurationEncoder ¶
func FloatSecondsDurationEncoder(d time.Duration, e TypeEncoder)
FloatSecondsDurationEncoder formats durations as floating-point seconds (e.g. 1.5 for one and a half seconds).
func FullCallerEncoder ¶
func FullCallerEncoder(pc uintptr, m TypeEncoder)
FullCallerEncoder formats the caller as the full filesystem path with line number. More verbose than ShortCallerEncoder but unambiguous when you have multiple packages with the same file name.
func HasField ¶
HasField reports whether the context's Bag contains a field with the given key. Useful for conditional field injection — for example, adding a trace ID only if one is not already present.
func LogDepth ¶
LogDepth logs at the given level, adding depth extra frames to the caller skip count. It is a package-level function (not a method) so that wrapper packages like logfc can log through an existing Logger without allocating a new one on every call.
func NanoDurationEncoder ¶
func NanoDurationEncoder(d time.Duration, e TypeEncoder)
NanoDurationEncoder formats durations as integer nanoseconds.
func NewContext ¶
NewContext returns a new Context carrying the given Logger. Retrieve it later with FromContext. No more threading loggers through your entire call stack like some kind of dependency injection nightmare.
func NewSlogHandler ¶
NewSlogHandler returns a slog.Handler that bridges the standard library's slog package to logf's pipeline. Use this when you want third-party code that speaks slog to flow through your logf Handler, Encoder, and Writer setup.
Fields added with slog.Logger.With become [Entry.LoggerBag] (cached by the encoder). The handler propagates context to [Handler.Handle], so field bags attached via With are resolved by NewContextHandler.
func RFC3339NanoTimeEncoder ¶
func RFC3339NanoTimeEncoder(t time.Time, e TypeEncoder)
RFC3339NanoTimeEncoder formats timestamps as RFC3339 strings with nanosecond precision (e.g. "2006-01-02T15:04:05.999999999Z07:00").
func RFC3339TimeEncoder ¶
func RFC3339TimeEncoder(t time.Time, e TypeEncoder)
RFC3339TimeEncoder formats timestamps as RFC3339 strings (e.g. "2006-01-02T15:04:05Z07:00"). This is the default for JSON output.
func ShortCallerEncoder ¶
func ShortCallerEncoder(pc uintptr, m TypeEncoder)
ShortCallerEncoder formats the caller as "package/file.go:line" — compact enough for log output while still letting you find the source. This is the default CallerEncoder.
func ShortTextLevelEncoder ¶
func ShortTextLevelEncoder(lvl Level, m TypeEncoder)
ShortTextLevelEncoder formats levels as compact 3-character uppercase strings (DBG, INF, WRN, ERR). This is the default for text/console output where horizontal space is precious.
func StringDurationEncoder ¶
func StringDurationEncoder(d time.Duration, m TypeEncoder)
StringDurationEncoder formats durations as human-readable strings like "4.5s", "300ms", or "1h2m3s" — the same format as time.Duration.String() but without allocating. This is the default.
func UnixNanoTimeEncoder ¶
func UnixNanoTimeEncoder(t time.Time, e TypeEncoder)
UnixNanoTimeEncoder formats timestamps as integer nanoseconds since the Unix epoch. Compact and machine-friendly, but not human-readable.
func UpperCaseLevelEncoder ¶
func UpperCaseLevelEncoder(lvl Level, m TypeEncoder)
UpperCaseLevelEncoder formats levels as upper-case strings ("DEBUG", "INFO", "WARN", "ERROR").
func With ¶
With returns a new context carrying the given fields. If the context already has a Bag, the fields are appended to it. This is the primary way to attach request-scoped data (trace IDs, user info, etc.) that will automatically appear in every log entry — no need to pass fields around manually.
Types ¶
type ArrayEncoder ¶
type ArrayEncoder interface {
EncodeLogfArray(TypeEncoder) error
}
ArrayEncoder lets your custom types serialize themselves as JSON arrays (or whatever array representation the encoder uses). Implement EncodeLogfArray and pass your type to logf.Array().
Example:
type stringArray []string
func (o stringArray) EncodeLogfArray(e TypeEncoder) error {
for i := range o {
e.EncodeTypeString(o[i])
}
return nil
}
type Bag ¶
type Bag struct {
// contains filtered or unexported fields
}
Bag is an immutable, goroutine-safe linked list of Fields — the backbone of logf's zero-copy field accumulation. Every call to With or WithGroup creates a new node pointing to the parent in O(1) time with no field copies. The encoder walks the chain at encoding time, and results are cached per encoder so repeated encoding of the same Bag is essentially free.
func BagFromContext ¶
BagFromContext returns the Bag stored in the context, or nil if none was set. Safe to call on any context.
func NewBag ¶
NewBag creates a root Bag node with the given fields. Most of the time you will not call this directly — Logger.With and logf.With handle Bag creation for you.
func (*Bag) Fields ¶
Fields collects all fields across the entire Bag chain in parent-first order. This allocates a new slice when the chain has more than one node, so for hot-path encoding prefer walking OwnFields + Parent directly.
func (*Bag) Group ¶
Group returns the group name for this Bag node, or an empty string if it is a regular field node.
func (*Bag) HasField ¶
HasField reports whether any node in the Bag chain contains a field with the given key. Walks the full chain from this node up to the root.
func (*Bag) LoadCache ¶
LoadCache returns previously cached encoded bytes for the given encoder slot, or nil on a cache miss. Slot 0 (no caching) always returns nil.
func (*Bag) OwnFields ¶
OwnFields returns only the fields stored directly in this Bag node, without walking up to parents. Useful for cache-aware encoding.
func (*Bag) Parent ¶
Parent returns the parent Bag in the linked list, or nil if this is the root node.
func (*Bag) StoreCache ¶
StoreCache saves encoded bytes for the given encoder slot so future Encode calls can skip re-encoding this Bag. Slot 0 (no caching) is a no-op. The internal cache structure is allocated lazily on first store.
func (*Bag) With ¶
With returns a new Bag that includes the given additional fields. The original Bag is not modified — the new node simply points to the parent. O(1) time, zero copies.
type Buffer ¶
type Buffer struct {
Data []byte
}
Buffer is a lightweight byte buffer used throughout the encoder pipeline. It wraps a []byte with append-oriented methods and integrates with sync.Pool via GetBuffer/Free for allocation-free encoding.
func GetBuffer ¶
func GetBuffer() *Buffer
GetBuffer grabs a *Buffer from the pool, reset and ready to use. When you are done, call Buffer.Free to return it — this keeps allocations close to zero on the hot path.
func NewBuffer ¶
func NewBuffer() *Buffer
NewBuffer creates a new Buffer with the default 4 KB capacity.
func NewBufferWithCapacity ¶
NewBufferWithCapacity creates a new Buffer pre-allocated to the given number of bytes.
func (*Buffer) AppendBool ¶
AppendBool appends "true" or "false" according to the given bool.
func (*Buffer) AppendByte ¶
AppendByte appends a single byte to the Buffer.
func (*Buffer) AppendBytes ¶
AppendBytes appends a byte slice to the Buffer.
func (*Buffer) AppendFloat32 ¶
AppendFloat32 appends the string form of the given float32.
func (*Buffer) AppendFloat64 ¶
AppendFloat64 appends the string form of the given float64.
func (*Buffer) AppendInt ¶
AppendInt appends the base-10 string representation of the given integer.
func (*Buffer) AppendString ¶
AppendString appends a string to the Buffer.
func (*Buffer) AppendUint ¶
AppendUint appends the base-10 string representation of the given unsigned integer.
func (*Buffer) Back ¶
Back returns the last byte in the Buffer. The caller must ensure the Buffer is not empty.
func (*Buffer) EnsureSize ¶
EnsureSize guarantees that at least s bytes can be appended without a reallocation.
func (*Buffer) ExtendBytes ¶
ExtendBytes grows the Buffer by s bytes and returns a slice pointing to the newly added region. Useful for in-place encoding (e.g., base64).
func (*Buffer) Free ¶
func (b *Buffer) Free()
Free returns the Buffer to the pool for reuse. The Buffer must not be accessed after calling Free.
type CallerEncoder ¶
type CallerEncoder func(pc uintptr, m TypeEncoder)
CallerEncoder is a function that resolves a program counter and writes the caller location (file + line) into the log output via TypeEncoder.
type ContextHandler ¶
type ContextHandler struct {
// contains filtered or unexported fields
}
ContextHandler is the Handler middleware that makes context-based logging work. It extracts the Bag from the context (populated by logf.With) and any external fields from FieldSource functions, attaches them to the Entry, and passes it downstream. Without a ContextHandler in the pipeline, context fields are silently ignored.
func NewContextHandler ¶
func NewContextHandler(next Handler, sources ...FieldSource) *ContextHandler
NewContextHandler returns a new ContextHandler wrapping the given Handler. Optional FieldSource functions are called on every Handle to pull in additional fields from the context (prepended to Entry.Fields so they appear before per-call fields).
type DurationEncoder ¶
type DurationEncoder func(time.Duration, TypeEncoder)
DurationEncoder is a function that formats a time.Duration into the log output via the TypeEncoder.
type Encoder ¶
Encoder is the interface that turns an Entry into bytes — it decides your log format (JSON, text, or whatever you dream up). The built-in JSON and Text encoders handle most needs, but implementing Encoder lets you go fully custom.
Encode serializes the Entry and returns a pooled *Buffer. The caller must call Buffer.Free when done. Encode is safe for concurrent use — implementations handle internal cloning and buffer pooling.
Clone returns an independent copy that shares immutable config but has its own mutable state, suitable for use in another goroutine.
func NewJSONEncoder ¶
func NewJSONEncoder(cfg JSONEncoderConfig) Encoder
NewJSONEncoder creates a JSON Encoder from a JSONEncoderConfig struct. For a friendlier builder-style API, use JSON() instead.
func NewTextEncoder ¶
func NewTextEncoder(cfg TextEncoderConfig) Encoder
NewTextEncoder creates a text Encoder from a TextEncoderConfig struct. For a friendlier builder-style API, use Text() instead.
type EncoderBuilder ¶
type EncoderBuilder interface {
Build() Encoder
}
EncoderBuilder builds an Encoder from accumulated configuration. Implemented by JSONEncoderBuilder and TextEncoderBuilder, and accepted by LoggerBuilder.EncoderFrom for composable builder chains.
type Entry ¶
type Entry struct {
// LoggerBag holds logger-scoped fields added via Logger.With. These are
// typically service-level context like "component" or "version".
LoggerBag *Bag
// Bag holds request-scoped fields extracted from context by ContextHandler.
// Think trace IDs, request metadata — anything you stuff into the context
// via logf.With(ctx, ...).
Bag *Bag
// Fields are the per-call fields passed directly to Debug/Info/Warn/Error.
Fields []Field
// Level is the severity of this log record.
Level Level
// Time is when this log record was created (usually time.Now()).
Time time.Time
// LoggerName is the dot-separated name set via Logger.WithName.
// Empty string means the logger has no name.
LoggerName string
// Text is the human-readable log message.
Text string
// CallerPC is the program counter of the call site. Zero means caller
// reporting is disabled or unavailable.
CallerPC uintptr
}
Entry is a single log record — the thing that travels through the pipeline from Logger to Handler to Encoder. It carries the message, level, timestamp, caller info, and all accumulated fields (both from Logger.With and from context). You rarely create one yourself; the Logger builds it for you on every Debug/Info/Warn/Error call.
type ErrorEncoder ¶
type ErrorEncoder func(string, error, FieldEncoder)
ErrorEncoder is a function that writes an error into the log output. It receives the field key, the error, and a FieldEncoder so it can emit one or more fields (e.g., a short message plus a verbose stack).
type ErrorEncoderConfig ¶
ErrorEncoderConfig controls how errors are encoded — specifically the verbose field suffix and whether verbose output is included at all.
func (ErrorEncoderConfig) WithDefaults ¶
func (c ErrorEncoderConfig) WithDefaults() ErrorEncoderConfig
WithDefaults returns a copy of the config with zero-value fields replaced by defaults (verbose suffix ".verbose").
type Field ¶
Field is the fundamental key-value unit in logf's structured logging. Every Bool(), String(), Int(), etc. call creates one of these. Fields are designed to be small (56 bytes) and allocation-free for scalar types — the value is packed inline rather than boxed into an interface.
Layout (56 bytes):
Key string // 16 field name
Type FieldType // 8 (1 byte + 7 padding)
Any interface{} // 16 error, object, array, stringer, any
Ptr unsafe.Pointer // 8 slice/string data pointer
Val int64 // 8 scalar value OR slice/string length
func Any ¶
Any returns a Field for an arbitrary value, picking the most efficient typed representation it can via a type switch. It handles all the common Go types (scalars, pointers, slices, time, errors, Stringer) and falls back to reflection for named types.
For hot paths, prefer the specific constructors (String, Int, etc.) — they avoid the type switch overhead entirely.
func Array ¶
func Array(k string, v ArrayEncoder) Field
Array returns a Field that carries a custom array value under the given key. The ArrayEncoder's EncodeLogfArray method is called at encoding time.
func ByteString ¶
ByteString returns a Field that interprets the []byte as a UTF-8 string (not base64-encoded like Bytes). Use this when you have text data in a byte slice and want it logged as a readable string.
func Bytes ¶
Bytes returns a Field that carries a []byte value under the given key. The bytes are base64-encoded in JSON output.
func Durations ¶
Durations returns a Field that carries a []time.Duration value under the given key.
func Error ¶
Error returns a Field that carries an error under the key "error". It is shorthand for NamedError("error", v).
func Formatter ¶
Formatter returns a Field that formats the value with fmt.Sprintf using the given verb and stores the result as a string.
func FormatterV ¶
FormatterV returns a Field that formats the value with "%#v" (Go-syntax representation) and stores the result as a string under the given key.
func Group ¶
Group returns a Field that nests the given fields as a sub-object under the given key. Think of it as an inline WithGroup for a single log call.
Example:
logger.Info(ctx, "done",
logf.Group("request", logf.String("id", "abc"), logf.Int("status", 200)),
)
// → {"msg":"done", "request":{"id":"abc", "status":200}}
func Inline ¶
func Inline(v ObjectEncoder) Field
Inline returns a Field that splices the ObjectEncoder's fields directly into the parent object — no wrapping key, no nesting. Perfect for flattening a struct's fields into the log entry.
Example:
logger.Info(ctx, "request handled",
logf.Inline(requestInfo),
logf.Int("status", 200),
)
// → {"msg":"request handled", "trace_id":"abc", "method":"GET", "status":200}
func NamedError ¶
NamedError returns a Field that carries an error value under the given key.
func Object ¶
func Object(k string, v ObjectEncoder) Field
Object returns a Field that carries a custom object value under the given key. The ObjectEncoder's EncodeLogfObject method is called at encoding time.
func Stringer ¶
Stringer returns a Field that calls v.String() and logs the result as a string under the given key. Nil values are logged as "nil".
func (Field) Accept ¶
func (fd Field) Accept(v FieldEncoder)
Accept dispatches the Field to the appropriate FieldEncoder method based on its FieldType. This is the bridge between the type-erased Field storage and the strongly-typed encoder interface.
type FieldEncoder ¶
type FieldEncoder interface {
EncodeFieldAny(string, interface{})
EncodeFieldBool(string, bool)
EncodeFieldInt64(string, int64)
EncodeFieldUint64(string, uint64)
EncodeFieldFloat64(string, float64)
EncodeFieldDuration(string, time.Duration)
EncodeFieldError(string, error)
EncodeFieldTime(string, time.Time)
EncodeFieldString(string, string)
EncodeFieldStrings(string, []string)
EncodeFieldBytes(string, []byte)
EncodeFieldInts64(string, []int64)
EncodeFieldFloats64(string, []float64)
EncodeFieldDurations(string, []time.Duration)
EncodeFieldArray(string, ArrayEncoder)
EncodeFieldObject(string, ObjectEncoder)
EncodeFieldGroup(string, []Field)
}
FieldEncoder provides methods for encoding key-value pairs. It is the interface that ObjectEncoder and ErrorEncoder receive to write named fields into the output. Each method encodes one field with the given key and typed value.
type FieldSource ¶
FieldSource is a function that extracts fields from a context. Pass one to NewContextHandler or LoggerBuilder.Context to automatically inject fields from external sources — tracing libraries, request ID middleware, authentication context, you name it.
type FieldType ¶
type FieldType byte
FieldType tells the encoder how to interpret the data packed inside a Field. Each type corresponds to a specific encoding path in the FieldEncoder.
const ( FieldTypeUnknown FieldType = iota // Scalars (value stored in Val/Any). FieldTypeAny FieldTypeBool FieldTypeInt64 FieldTypeUint64 FieldTypeFloat64 FieldTypeDuration FieldTypeError FieldTypeTime // Unsafe pointer slices (data in Ptr, length in Val). FieldTypeBytes FieldTypeBytesToString FieldTypeBytesToInts64 FieldTypeBytesToFloats64 FieldTypeBytesToDurations FieldTypeBytesToStrings // Interface-based (encoder callback in Any). FieldTypeArray FieldTypeObject FieldTypeGroup )
Set of FileType values.
type Handler ¶
type Handler interface {
Handle(context.Context, Entry) error
Enabled(context.Context, Level) bool
}
Handler is the core interface that processes log entries. Implement it to control where and how logs are written. The built-in handlers — SyncHandler, ContextHandler, and Router — cover most use cases, but you can wrap or replace them for custom behavior like sampling, rate-limiting, or sending logs to an external service.
func NewSyncHandler ¶
NewSyncHandler returns the simplest possible Handler — it encodes each entry right there in the calling goroutine and writes it immediately. No routing, no buffering, no background goroutines. Think of it as the "just write it" handler.
Encoding is fully parallel across goroutines (the Encoder handles its own cloning and buffer pooling), but the provided io.Writer must be safe for concurrent use. Great for benchmarks, tests, and simple single-destination setups.
type JSONEncoderBuilder ¶
type JSONEncoderBuilder struct {
// contains filtered or unexported fields
}
JSONEncoderBuilder configures and builds a JSON Encoder using a clean builder-style API. Create one with JSON(), chain methods to customize, then call Build() or pass directly to LoggerBuilder.EncoderFrom().
func JSON ¶
func JSON() *JSONEncoderBuilder
JSON returns a new JSONEncoderBuilder with default settings. This is the recommended way to create a JSON encoder — chain the methods you need and call Build:
enc := logf.JSON().Build()
enc := logf.JSON().TimeKey("time").LevelKey("severity").Build()
func (*JSONEncoderBuilder) Build ¶
func (b *JSONEncoderBuilder) Build() Encoder
Build finalizes the configuration and returns a ready-to-use JSON Encoder.
func (*JSONEncoderBuilder) CallerKey ¶
func (b *JSONEncoderBuilder) CallerKey(k string) *JSONEncoderBuilder
CallerKey sets the JSON key for the caller location field (default "caller").
func (*JSONEncoderBuilder) DisableCaller ¶
func (b *JSONEncoderBuilder) DisableCaller() *JSONEncoderBuilder
DisableCaller omits the caller location field from JSON output entirely.
func (*JSONEncoderBuilder) DisableLevel ¶
func (b *JSONEncoderBuilder) DisableLevel() *JSONEncoderBuilder
DisableLevel omits the severity level field from JSON output entirely.
func (*JSONEncoderBuilder) DisableMsg ¶
func (b *JSONEncoderBuilder) DisableMsg() *JSONEncoderBuilder
DisableMsg omits the message text field from JSON output entirely.
func (*JSONEncoderBuilder) DisableName ¶
func (b *JSONEncoderBuilder) DisableName() *JSONEncoderBuilder
DisableName omits the logger name field from JSON output entirely.
func (*JSONEncoderBuilder) DisableTime ¶
func (b *JSONEncoderBuilder) DisableTime() *JSONEncoderBuilder
DisableTime omits the timestamp field from JSON output entirely.
func (*JSONEncoderBuilder) EncodeCaller ¶
func (b *JSONEncoderBuilder) EncodeCaller(e CallerEncoder) *JSONEncoderBuilder
EncodeCaller sets a custom CallerEncoder for formatting caller locations (default short format).
func (*JSONEncoderBuilder) EncodeDuration ¶
func (b *JSONEncoderBuilder) EncodeDuration(e DurationEncoder) *JSONEncoderBuilder
EncodeDuration sets a custom DurationEncoder for formatting durations (default string representation).
func (*JSONEncoderBuilder) EncodeError ¶
func (b *JSONEncoderBuilder) EncodeError(e ErrorEncoder) *JSONEncoderBuilder
EncodeError sets a custom ErrorEncoder for formatting error values.
func (*JSONEncoderBuilder) EncodeLevel ¶
func (b *JSONEncoderBuilder) EncodeLevel(e LevelEncoder) *JSONEncoderBuilder
EncodeLevel sets a custom LevelEncoder for formatting severity levels.
func (*JSONEncoderBuilder) EncodeTime ¶
func (b *JSONEncoderBuilder) EncodeTime(e TimeEncoder) *JSONEncoderBuilder
EncodeTime sets a custom TimeEncoder for formatting timestamps (default RFC3339).
func (*JSONEncoderBuilder) LevelKey ¶
func (b *JSONEncoderBuilder) LevelKey(k string) *JSONEncoderBuilder
LevelKey sets the JSON key for the severity level field (default "level").
func (*JSONEncoderBuilder) MsgKey ¶
func (b *JSONEncoderBuilder) MsgKey(k string) *JSONEncoderBuilder
MsgKey sets the JSON key for the log message field (default "msg").
func (*JSONEncoderBuilder) NameKey ¶
func (b *JSONEncoderBuilder) NameKey(k string) *JSONEncoderBuilder
NameKey sets the JSON key for the logger name field (default "logger").
func (*JSONEncoderBuilder) TimeKey ¶
func (b *JSONEncoderBuilder) TimeKey(k string) *JSONEncoderBuilder
TimeKey sets the JSON key for the timestamp field (default "ts").
type JSONEncoderConfig ¶
type JSONEncoderConfig struct {
FieldKeyMsg string
FieldKeyTime string
FieldKeyLevel string
FieldKeyName string
FieldKeyCaller string
DisableFieldMsg bool
DisableFieldTime bool
DisableFieldLevel bool
DisableFieldName bool
DisableFieldCaller bool
EncodeTime TimeEncoder
EncodeDuration DurationEncoder
EncodeError ErrorEncoder
EncodeLevel LevelEncoder
EncodeCaller CallerEncoder
// contains filtered or unexported fields
}
JSONEncoderConfig controls how the JSON encoder formats log entries — field keys, which fields to include, and how types like time, duration, and errors are rendered. For a friendlier builder-style API, use JSON() instead.
func (JSONEncoderConfig) WithDefaults ¶
func (c JSONEncoderConfig) WithDefaults() JSONEncoderConfig
WithDefaults returns a copy of the config with all zero-value fields replaced by sensible defaults (RFC3339 timestamps, string durations, short caller format, etc.).
type Level ¶
type Level int8
Level represents the severity of a log message. Higher numeric values mean more verbose output — LevelDebug (3) lets everything through, while LevelError (0) only lets errors pass.
const ( // LevelError logs errors only — the quietest setting. LevelError Level = iota // LevelWarn logs errors and warnings. LevelWarn // LevelInfo logs errors, warnings, and informational messages. This is // the typical production setting. LevelInfo // LevelDebug logs everything — all severity levels pass through. LevelDebug )
Severity levels.
func LevelFromString ¶
LevelFromString parses a level name (case-insensitive) and returns the corresponding Level. Returns false if the name is not recognized.
func (Level) Enabled ¶
Enabled reports whether a message at level o would be logged under this level threshold. For example, LevelInfo.Enabled(LevelDebug) is false.
func (Level) MarshalText ¶
MarshalText marshals the Level to its lower-case text representation.
func (Level) String ¶
String returns a lower-case string representation of the Level ("debug", "info", "warn", "error").
func (*Level) UnmarshalText ¶
UnmarshalText parses a level string (case-insensitive) and sets the Level. Returns an error for unrecognized values.
func (Level) UpperCaseString ¶
UpperCaseString returns an upper-case string representation of the Level ("DEBUG", "INFO", "WARN", "ERROR").
type LevelEncoder ¶
type LevelEncoder func(Level, TypeEncoder)
LevelEncoder is a function that formats a Level into the log output via TypeEncoder. Swap it out to control how levels appear in your logs.
type Logger ¶
type Logger struct {
// contains filtered or unexported fields
}
Logger is the main entry point for structured logging. It wraps a Handler, checks levels before doing any work, and provides the familiar Debug/Info/Warn/Error methods plus context-aware field accumulation via With and WithGroup. Loggers are immutable — every With/WithName/WithGroup call returns a new Logger, so they are safe to share across goroutines.
func DisabledLogger ¶
func DisabledLogger() *Logger
DisabledLogger returns a Logger that silently discards everything as fast as possible. Handy as a safe default when no logger is configured, and it is what FromContext returns when there is no Logger in the context.
func FromContext ¶
FromContext returns the Logger stored in the context by NewContext, or a DisabledLogger if no Logger was stored. It is always safe to call — you will never get nil.
func New ¶
New returns a Logger wired to the given Handler. Level filtering is controlled by the handler's Enabled method, so you can use any Handler implementation — SyncHandler, Router, ContextHandler, or your own. For a friendlier builder-style API, use NewLogger() instead.
func (*Logger) AtLevel ¶
AtLevel calls fn only if the specified level is enabled, passing it a LogFunc pre-bound to that level. This is perfect for guarding expensive log preparation without a separate Enabled check. A nil ctx is treated as context.Background().
func (*Logger) Debug ¶
Debug logs a message at LevelDebug. If debug logging is disabled, this is a no-op — no fields are evaluated, no allocations happen. A nil ctx is treated as context.Background().
func (*Logger) Debugx ¶
Debugx is like Debug but without a context parameter. Equivalent to Debug(context.Background(), text, fs...).
func (*Logger) Enabled ¶
Enabled reports whether logging at the given level would actually produce output. Use this to guard expensive argument preparation. A nil ctx is treated as context.Background().
func (*Logger) Error ¶
Error logs a message at LevelError. Something went wrong and you want everyone to know about it. A nil ctx is treated as context.Background().
func (*Logger) Errorx ¶
Errorx is like Error but without a context parameter. Equivalent to Error(context.Background(), text, fs...).
func (*Logger) Info ¶
Info logs a message at LevelInfo. This is the default "something happened" level for normal operational events. A nil ctx is treated as context.Background().
func (*Logger) Infox ¶
Infox is like Info but without a context parameter. Equivalent to Info(context.Background(), text, fs...).
func (*Logger) Log ¶
Log logs a message at an arbitrary level. Use this when the level is determined at runtime; for the common cases prefer Debug/Info/Warn/Error. A nil ctx is treated as context.Background().
func (*Logger) Slog ¶
Slog returns a *slog.Logger backed by the same Handler, fields, and name as this Logger. Use it when you need to hand a standard library slog.Logger to code that does not know about logf.
func (*Logger) Warn ¶
Warn logs a message at LevelWarn. Use this for situations that are unexpected but not broken — things a human should probably look at. A nil ctx is treated as context.Background().
func (*Logger) Warnx ¶
Warnx is like Warn but without a context parameter. Equivalent to Warn(context.Background(), text, fs...).
func (*Logger) With ¶
With returns a new Logger that includes the given fields in every subsequent log entry. Fields are accumulated, not replaced — so calling With multiple times builds up context over time.
func (*Logger) WithCaller ¶
WithCaller returns a new Logger with caller reporting toggled on or off. When enabled (the default), every log entry includes the source file and line.
func (*Logger) WithCallerSkip ¶
WithCallerSkip returns a new Logger that skips additional stack frames when capturing caller info. Use this when you wrap the Logger in your own helper function so the reported caller points to your caller, not your wrapper.
type LoggerBuilder ¶
type LoggerBuilder struct {
// contains filtered or unexported fields
}
LoggerBuilder accumulates options and builds a Logger with a sync pipeline: Encoder -> SyncHandler -> ContextHandler -> Logger. Chain its methods and finish with Build. For advanced multi-destination pipelines, use NewRouter directly.
func NewLogger ¶
func NewLogger() *LoggerBuilder
NewLogger returns a LoggerBuilder — the easiest way to get a Logger up and running. It builds a single-destination sync pipeline, which is perfect for most applications. For async buffered or multi-destination setups, reach for NewRouter + SlabWriter instead.
Defaults: JSON encoder, LevelDebug, os.Stderr, caller enabled, no ContextHandler.
logger := logf.NewLogger().Build()
// Customized:
logger := logf.NewLogger().
Level(logf.LevelInfo).
EncoderFrom(logf.JSON().TimeKey("time")).
Output(file).
Context().
Build()
func (*LoggerBuilder) Build ¶
func (b *LoggerBuilder) Build() *Logger
Build finalizes the configuration and returns a ready-to-use Logger.
logger := logf.NewLogger().Build()
func (*LoggerBuilder) Context ¶
func (b *LoggerBuilder) Context(sources ...FieldSource) *LoggerBuilder
Context enables the ContextHandler middleware, which extracts Bag fields from context on every log call. This is what makes logf.With(ctx, ...) work — without it, context fields are silently ignored. Optional FieldSource functions let you pull in external fields too (trace IDs, request metadata, etc.).
func (*LoggerBuilder) Encoder ¶
func (b *LoggerBuilder) Encoder(enc Encoder) *LoggerBuilder
Encoder sets a pre-built Encoder directly. Use this when you already have an Encoder instance; otherwise prefer EncoderFrom for builder composition.
func (*LoggerBuilder) EncoderFrom ¶
func (b *LoggerBuilder) EncoderFrom(eb EncoderBuilder) *LoggerBuilder
EncoderFrom sets an EncoderBuilder whose Build method will be called when LoggerBuilder.Build is called. This enables clean builder composition — no need to call Build on the encoder separately:
logf.NewLogger().EncoderFrom(logf.JSON().TimeKey("time")).Build()
func (*LoggerBuilder) Level ¶
func (b *LoggerBuilder) Level(l Level) *LoggerBuilder
Level sets the minimum severity level. Messages below this level are discarded. Default is LevelDebug (everything gets through).
func (*LoggerBuilder) Output ¶
func (b *LoggerBuilder) Output(w io.Writer) *LoggerBuilder
Output sets where encoded log entries are written. Default is os.Stderr.
type MutableLevel ¶
type MutableLevel struct {
// contains filtered or unexported fields
}
MutableLevel is a concurrency-safe level that can be changed at runtime without rebuilding the Logger. Perfect for admin endpoints that toggle debug logging on a live system — just call Set and every subsequent log call picks up the new level atomically.
func NewMutableLevel ¶
func NewMutableLevel(l Level) *MutableLevel
NewMutableLevel creates a MutableLevel starting at the given level. Pass it where a Level is expected and call Set later to change the threshold at runtime without restarting.
func (*MutableLevel) Enabled ¶
func (l *MutableLevel) Enabled(_ context.Context, lvl Level) bool
Enabled reports whether the given level is enabled at the current mutable level. Safe for concurrent use.
func (*MutableLevel) Level ¶
func (l *MutableLevel) Level() Level
Level returns the current logging level atomically.
func (*MutableLevel) Set ¶
func (l *MutableLevel) Set(o Level)
Set atomically switches the logging level. All subsequent log calls will use the new threshold.
type ObjectEncoder ¶
type ObjectEncoder interface {
EncodeLogfObject(FieldEncoder) error
}
ObjectEncoder lets your custom types serialize themselves as structured objects with named fields. This is how you get zero-allocation logging for your domain types — no reflection, no fmt.Sprintf, just direct calls to the encoder.
Example:
type user struct {
Username string
Password string
}
func (u user) EncodeLogfObject(e FieldEncoder) error {
e.EncodeFieldString("username", u.Username)
e.EncodeFieldString("password", u.Password)
return nil
}
type RouteOption ¶
type RouteOption func(*encoderGroup)
RouteOption configures a destination within a Route's encoder group. Use Output and OutputCloser to create RouteOptions.
func Output ¶
func Output(level Level, w io.Writer) RouteOption
Output returns a RouteOption that adds a destination with the given level filter and writer. Writes happen directly in the caller's goroutine — no channel, no background goroutine, zero per-message allocations. The Writer must be safe for concurrent use.
For async I/O with batching and spike tolerance, wrap the writer in a SlabWriter before passing it to Output:
sw := logf.NewSlabWriter(conn).SlabSize(64*1024).SlabCount(8).FlushInterval(100*time.Millisecond).Build()
defer sw.Close()
router, close, _ := logf.NewRouter().
Route(enc, logf.Output(logf.LevelDebug, sw)).
Build()
func OutputCloser ¶
func OutputCloser(level Level, w io.WriteCloser) RouteOption
OutputCloser is like Output but transfers ownership of the writer to the router — the router's close function will call Close on w after flushing. Perfect for SlabWriters and other resources you want the router to manage:
sw := logf.NewSlabWriter(conn).SlabSize(64*1024).SlabCount(8).Build()
router, close, _ := logf.NewRouter().
Route(enc, logf.OutputCloser(logf.LevelDebug, sw)).
Build()
defer close() // flushes and closes sw
type RouterBuilder ¶
type RouterBuilder struct {
// contains filtered or unexported fields
}
RouterBuilder accumulates routes and builds a fan-out Handler. Add as many routes as you need — each route has an encoder and one or more outputs with independent level filters.
Usage:
router, close, err := NewRouter().
Route(jsonEnc,
Output(LevelDebug, kibana),
Output(LevelInfo, stderr),
).
Build()
func NewRouter ¶
func NewRouter() *RouterBuilder
NewRouter returns a RouterBuilder for constructing a fan-out Handler that sends log entries to multiple destinations. Each route groups outputs that share an encoder, so one Encode call serves all outputs in the group.
func (*RouterBuilder) Build ¶
func (b *RouterBuilder) Build() (Handler, func() error, error)
Build validates the configuration and returns the Router as a Handler, plus a close function that flushes and syncs all writers. Always defer the close function to ensure data reaches its destination. Build returns an error if the configuration is invalid (no routes or no outputs).
func (*RouterBuilder) Route ¶
func (b *RouterBuilder) Route(enc Encoder, opts ...RouteOption) *RouterBuilder
Route adds an encoder group with the given outputs. All outputs in the same route share a single Encode call per entry — so sending JSON to both a file and a network socket costs exactly one encode, not two.
type SlabStats ¶
type SlabStats struct {
QueuedSlabs int // slabs waiting for I/O
FreeSlabs int // slabs available in pool
TotalSlabs int // total slab count
Dropped int64 // total messages dropped (dropOnFull mode)
Written int64 // total messages accepted by Write
WriteErrors int64 // total write errors
}
SlabStats is a snapshot of SlabWriter runtime statistics. Pull it from Stats() and feed it to your metrics system to keep an eye on queue depth, drop rates, and write errors.
type SlabWriter ¶
type SlabWriter struct {
// contains filtered or unexported fields
}
SlabWriter is an async buffered writer that keeps your logging goroutines fast by decoupling them from slow I/O. It uses a pool of pre-allocated linear byte slabs — producers memcpy into a slab, and a background goroutine writes full slabs to the destination in big, efficient batches.
Architecture:
N goroutines (producers) background I/O goroutine
Write(p) ┌──────────┐
↓ mu.Lock │ pool │ ←── recycle after write
↓ memcpy into slab ←── └──────────┘
↓ slab full?
↓ yes → send slab ──→ full chan ──→ w.Write(slab) → destination
↓ grab fresh slab ←── pool
↓ mu.Unlock
Each slab is a contiguous []byte. The I/O goroutine writes it in a single Write call — always linear, no wrap-around, no partial writes. After Write completes, the slab is returned to the pool for reuse.
Capacity planning:
The two parameters — slabSize and slabCount — control throughput and burst tolerance independently.
slabSize determines the batch size per Write call. With a typical log message of 256 bytes and slabSize of 16 KB, each Write delivers 64 messages. The maximum sustained throughput is:
throughput = slabSize / (writeLatency × msgSize)
For example, with 1 ms network latency and 256-byte messages:
16 KB slab: 64,000 msgs/sec 64 KB slab: 256,000 msgs/sec
slabCount determines burst tolerance — how many slabs producers can fill while the I/O goroutine is blocked on a slow Write. This absorbs temporary latency spikes without dropping messages or blocking the consumer.
During a latency spike the consumer keeps filling free slabs. The number of slabs acts as a time buffer:
burstTime = slabCount × slabSize / (msgRate × msgSize)
For example, with 4 slabs × 4 KB (default) and 256-byte messages:
1,000 msgs/sec: absorbs a ~64 ms spike 10,000 msgs/sec: absorbs a ~6 ms spike 50,000 msgs/sec: absorbs a ~1.2 ms spike
More configurations (256-byte messages, 50,000 msgs/sec):
4 slabs × 16 KB: absorbs a ~5 ms spike 8 slabs × 16 KB: absorbs a ~10 ms spike 16 slabs × 16 KB: absorbs a ~20 ms spike 8 slabs × 64 KB: absorbs a ~40 ms spike 16 slabs × 64 KB: absorbs a ~80 ms spike
Memory cost is slabCount × slabSize, plus one extra slabSize when FlushInterval is enabled (reusable buffer for idle flush). Typical configurations:
4 × 4 KB = 16 KB (default, lightweight) 8 × 64 KB = 512 KB (general purpose, good burst tolerance) 16 × 64 KB = 1 MB (high throughput + long spike tolerance)
When all slabs are in flight and the pool is empty, Write blocks until a slab is recycled. With DropOnFull, Write never blocks: if the I/O goroutine cannot keep up, the current slab's data is silently discarded and the slab is reused. Use Dropped to monitor the total number of messages lost.
Message integrity ¶
Write guarantees that each message is either fully delivered or fully dropped — never partially written (torn). This holds for messages of any size:
len(p) <= slabSize: if the message does not fit in the remaining slab space, an early swap is performed before writing. The message always lands in a single slab. On drop the entire slab (including the message) is discarded atomically.
len(p) > slabSize: the message is allocated in a dedicated oversized buffer and sent through the I/O goroutine as a single write. The oversized buffer is discarded after write (not returned to the pool). One allocation per oversized message.
Performance notes ¶
The early swap may leave unused space at the tail of a slab when a message does not fit in the remainder. With typical log messages (200–500 bytes) and slab sizes (16–64 KB), utilization stays above 99 %. Fragmentation becomes noticeable only when message size approaches slab size (e.g. msg/slab > 50 %), which is unusual for structured logging.
Oversized messages (> slabSize) incur one heap allocation (make + copy) per write. This is acceptable because such messages are rare; typical log entries are 100–500 bytes while the default slab is 4 KB.
Concurrency ¶
Write and Flush are safe for concurrent use. Write and Flush must not be called after or concurrently with Close. Close itself is idempotent.
func (*SlabWriter) Close ¶
func (sb *SlabWriter) Close() error
Close flushes remaining data, drains the queue, stops the background I/O goroutine, and calls Flush + Sync on the underlying Writer. Safe to call multiple times — subsequent calls return the same error.
func (*SlabWriter) Flush ¶
func (sb *SlabWriter) Flush() error
Flush enqueues the current partial slab for writing by the background I/O goroutine. It returns immediately without waiting for the write to complete — if you need a durable flush, use Close instead. Must not be called after Close.
func (*SlabWriter) Stats ¶
func (sb *SlabWriter) Stats() SlabStats
Stats returns a point-in-time snapshot of runtime statistics. Safe to call concurrently from a metrics scraper or health check endpoint.
func (*SlabWriter) Sync ¶
func (sb *SlabWriter) Sync() error
Sync is a no-op on SlabWriter — the real Sync on the underlying writer happens during Close.
func (*SlabWriter) Write ¶
func (sb *SlabWriter) Write(p []byte) (int, error)
Write copies p into the current slab. Every message is guaranteed to be either fully written or fully dropped — never partially torn. If the message does not fit in the remaining slab space, an early swap puts it in a fresh slab. Messages larger than slabSize get a dedicated buffer.
Write is safe for concurrent use. It must not be called after Close.
type SlabWriterBuilder ¶
type SlabWriterBuilder struct {
// contains filtered or unexported fields
}
SlabWriterBuilder accumulates configuration for a SlabWriter. Create one with NewSlabWriter, set options via chained method calls, and finalize with Build.
func NewSlabWriter ¶
func NewSlabWriter(w io.Writer) *SlabWriterBuilder
NewSlabWriter returns a builder for a SlabWriter that will write to w. Call Build to create the SlabWriter. Default slab size is 4 KB and default slab count is 4.
func (*SlabWriterBuilder) Build ¶
func (b *SlabWriterBuilder) Build() *SlabWriter
Build creates and starts the SlabWriter. You must call Close when you are done to flush remaining data and stop the background I/O goroutine — a defer sw.Close() right after creation is the way to go.
func (*SlabWriterBuilder) DropOnFull ¶
func (b *SlabWriterBuilder) DropOnFull() *SlabWriterBuilder
DropOnFull makes Write non-blocking: if the I/O goroutine cannot keep up and all slabs are in flight, the current slab's data is silently dropped instead of blocking the caller. Use this when you would rather lose log messages than add latency to your hot path. Monitor dropped messages via Stats().Dropped.
func (*SlabWriterBuilder) ErrorWriter ¶
func (b *SlabWriterBuilder) ErrorWriter(w io.Writer) *SlabWriterBuilder
ErrorWriter sets where I/O errors are reported. When the background goroutine fails to write a slab, it formats the error and writes it to w. By default errors are silently discarded — pass os.Stderr here if you want to know about write failures.
func (*SlabWriterBuilder) FlushInterval ¶
func (b *SlabWriterBuilder) FlushInterval(d time.Duration) *SlabWriterBuilder
FlushInterval sets how long the SlabWriter waits for new data before flushing a partial slab. Without this, a quiet period could leave recent log entries sitting in the buffer. Default is 0 (no idle flush — data only goes out when a slab fills up or Close is called).
func (*SlabWriterBuilder) SlabCount ¶
func (b *SlabWriterBuilder) SlabCount(n int) *SlabWriterBuilder
SlabCount sets the number of slab buffers in the pool. More slabs give better burst tolerance but use more memory. Default is 4.
func (*SlabWriterBuilder) SlabSize ¶
func (b *SlabWriterBuilder) SlabSize(n int) *SlabWriterBuilder
SlabSize sets the size of each slab buffer in bytes. Larger slabs mean fewer I/O calls but more memory. Default is 4 KB.
type TextEncoderBuilder ¶
type TextEncoderBuilder struct {
// contains filtered or unexported fields
}
TextEncoderBuilder configures and builds a text Encoder using a clean builder-style API. Create one with Text(), chain methods to customize, then call Build().
func Text ¶
func Text() *TextEncoderBuilder
Text returns a new TextEncoderBuilder — the recommended way to create a human-readable text encoder with ANSI colors. Colors are on by default; use NoColor() to disable them or check the NO_COLOR environment variable (https://no-color.org):
enc := logf.Text().Build() enc := logf.Text().NoColor().Build()
Respect the NO_COLOR convention:
b := logf.Text()
if _, ok := os.LookupEnv("NO_COLOR"); ok {
b = b.NoColor()
}
func (*TextEncoderBuilder) Build ¶
func (b *TextEncoderBuilder) Build() Encoder
Build finalizes the configuration and returns a ready-to-use text Encoder.
func (*TextEncoderBuilder) DisableCaller ¶
func (b *TextEncoderBuilder) DisableCaller() *TextEncoderBuilder
DisableCaller omits the caller location from text output entirely.
func (*TextEncoderBuilder) DisableLevel ¶
func (b *TextEncoderBuilder) DisableLevel() *TextEncoderBuilder
DisableLevel omits the severity level from text output entirely.
func (*TextEncoderBuilder) DisableMsg ¶
func (b *TextEncoderBuilder) DisableMsg() *TextEncoderBuilder
DisableMsg omits the message text from text output entirely.
func (*TextEncoderBuilder) DisableName ¶
func (b *TextEncoderBuilder) DisableName() *TextEncoderBuilder
DisableName omits the logger name from text output entirely.
func (*TextEncoderBuilder) DisableTime ¶
func (b *TextEncoderBuilder) DisableTime() *TextEncoderBuilder
DisableTime omits the timestamp from text output entirely.
func (*TextEncoderBuilder) EncodeCaller ¶
func (b *TextEncoderBuilder) EncodeCaller(e CallerEncoder) *TextEncoderBuilder
EncodeCaller sets a custom CallerEncoder for formatting caller locations (default short format).
func (*TextEncoderBuilder) EncodeDuration ¶
func (b *TextEncoderBuilder) EncodeDuration(e DurationEncoder) *TextEncoderBuilder
EncodeDuration sets a custom DurationEncoder for formatting durations (default string representation).
func (*TextEncoderBuilder) EncodeError ¶
func (b *TextEncoderBuilder) EncodeError(e ErrorEncoder) *TextEncoderBuilder
EncodeError sets a custom ErrorEncoder for formatting error values.
func (*TextEncoderBuilder) EncodeLevel ¶
func (b *TextEncoderBuilder) EncodeLevel(e LevelEncoder) *TextEncoderBuilder
EncodeLevel sets a custom LevelEncoder for formatting severity levels.
func (*TextEncoderBuilder) EncodeTime ¶
func (b *TextEncoderBuilder) EncodeTime(e TimeEncoder) *TextEncoderBuilder
EncodeTime sets a custom TimeEncoder for formatting timestamps (default time.StampMilli).
func (*TextEncoderBuilder) NoColor ¶
func (b *TextEncoderBuilder) NoColor() *TextEncoderBuilder
NoColor disables ANSI color escape sequences in the output. Use this when writing to files or non-TTY destinations.
type TextEncoderConfig ¶
type TextEncoderConfig struct {
NoColor bool
DisableFieldTime bool
DisableFieldLevel bool
DisableFieldName bool
DisableFieldMsg bool
DisableFieldCaller bool
EncodeTime TimeEncoder
EncodeDuration DurationEncoder
EncodeError ErrorEncoder
EncodeLevel LevelEncoder
EncodeCaller CallerEncoder
}
TextEncoderConfig controls how the text encoder formats log entries — colors, which fields to show, and how types like time, duration, and errors are rendered. For a friendlier builder-style API, use Text() instead.
func (TextEncoderConfig) WithDefaults ¶
func (c TextEncoderConfig) WithDefaults() TextEncoderConfig
WithDefaults returns a copy of the config with all zero-value fields replaced by sensible defaults (StampMilli timestamps, string durations, short caller format, short level names, etc.).
type TimeEncoder ¶
type TimeEncoder func(time.Time, TypeEncoder)
TimeEncoder is a function that formats a time.Time into the log output via the TypeEncoder. Swap it out to control timestamp format globally.
func LayoutTimeEncoder ¶
func LayoutTimeEncoder(layout string) TimeEncoder
LayoutTimeEncoder returns a TimeEncoder that formats timestamps using the given Go time layout string (same format as time.Format).
type TypeEncoder ¶
type TypeEncoder interface {
EncodeTypeAny(interface{})
EncodeTypeBool(bool)
EncodeTypeInt64(int64)
EncodeTypeUint64(uint64)
EncodeTypeFloat64(float64)
EncodeTypeDuration(time.Duration)
EncodeTypeTime(time.Time)
EncodeTypeString(string)
EncodeTypeStrings([]string)
EncodeTypeBytes([]byte)
EncodeTypeInts64([]int64)
EncodeTypeFloats64([]float64)
EncodeTypeDurations([]time.Duration)
EncodeTypeArray(ArrayEncoder)
EncodeTypeObject(ObjectEncoder)
EncodeTypeUnsafeBytes(unsafe.Pointer)
}
TypeEncoder provides methods for encoding individual values (scalars, slices, arrays, objects) without field names. It is the companion interface used by TimeEncoder, DurationEncoder, LevelEncoder, and CallerEncoder to write their output into the buffer.
type TypeEncoderFactory ¶
type TypeEncoderFactory interface {
TypeEncoder(*Buffer) TypeEncoder
}
TypeEncoderFactory creates a TypeEncoder that writes into the given Buffer. This lets one encoder borrow another encoder's formatting — for example, the text encoder uses the JSON encoder's TypeEncoderFactory to render nested objects and arrays in JSON syntax within otherwise plain-text output.
type Writer ¶
Writer extends io.Writer with Flush and Sync — the two operations needed for reliable log delivery. Flush pushes buffered data to the underlying output, and Sync commits it to stable storage (think fsync). The Router calls Flush and Sync during its close sequence to make sure nothing is left in flight.
func WriterFromIO ¶
WriterFromIO upgrades a plain io.Writer to a Writer. If w already implements Writer, it is returned as-is — no wrapping overhead. Otherwise, the wrapper discovers Flush and Sync capabilities from the underlying type:
- Sync calls w.Sync() if available (e.g. *os.File)
- Flush calls w.Flush() if available (e.g. *bufio.Writer)
- Missing methods become no-ops
type WriterSlot ¶
type WriterSlot struct {
// contains filtered or unexported fields
}
WriterSlot is a placeholder Writer you can wire into a Logger now and connect to a real destination later via Set. This solves the chicken-and-egg problem where you need a Logger at startup but the actual output (file, network, etc.) is not ready yet.
Before Set is called, writes are either dropped or buffered (if WithSlotBuffer was used). After Set, all writes go straight to the real writer with no extra overhead.
slot := logf.NewWriterSlot() logger := logf.NewLogger().Output(slot).Build() // ... later, when destination is ready: slot.Set(file)
WriterSlot is safe for concurrent Write/Flush/Sync calls. Set itself is NOT safe for concurrent calls — call it from a single goroutine.
func NewWriterSlot ¶
func NewWriterSlot(opts ...WriterSlotOption) *WriterSlot
NewWriterSlot returns a new WriterSlot ready for use. Before Set is called, writes are either silently dropped or buffered (if you pass WithSlotBuffer).
func (*WriterSlot) Flush ¶
func (s *WriterSlot) Flush() error
Flush delegates to the real writer's Flush. No-op before Set.
func (*WriterSlot) Set ¶
func (s *WriterSlot) Set(w io.Writer)
Set connects the slot to a real writer. Any buffered data will be flushed on the next Write call, preserving temporal ordering without blocking Set itself. The writer is automatically wrapped via WriterFromIO if needed.
Set is NOT safe for concurrent calls — call it from a single goroutine.
func (*WriterSlot) Sync ¶
func (s *WriterSlot) Sync() error
Sync delegates to the real writer's Sync. No-op before Set.
type WriterSlotOption ¶
type WriterSlotOption func(*WriterSlot)
WriterSlotOption configures a WriterSlot at creation time.
func WithSlotBuffer ¶
func WithSlotBuffer(size int) WriterSlotOption
WithSlotBuffer enables buffering of early writes before Set is called, keeping up to size bytes in memory so you do not lose startup logs. Writes that do not fit entirely are dropped (no partial writes). The buffer is flushed to the real writer on the first Write after Set.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
basic
command
Basic example: NewLogger builder, logging levels, fields, groups.
|
Basic example: NewLogger builder, logging levels, fields, groups. |
|
context
command
Context example: request-scoped fields via context.Context.
|
Context example: request-scoped fields via context.Context. |
|
logfc
command
logfc example: logger-in-context pattern.
|
logfc example: logger-in-context pattern. |
|
router
command
Router example: multi-destination logging with independent encoders, level filters, and sync/async I/O.
|
Router example: multi-destination logging with independent encoders, level filters, and sync/async I/O. |
|
slog
command
slog integration example: logf as backend for slog, third-party libraries, and mixed logf/slog usage in the same application.
|
slog integration example: logf as backend for slog, third-party libraries, and mixed logf/slog usage in the same application. |
|
writerslot
command
WriterSlot example: lazy destination initialization.
|
WriterSlot example: lazy destination initialization. |