This document covers all field constructor functions for structured logging. Fields provide efficient, zero-allocation, strongly-typed structured data for log entries.
type Field = zapcore.FieldFields are the core building blocks for structured logging in zap. Each field represents a key-value pair that will be included in log output.
func Bool(key string, val bool) Field
func Boolp(key string, val *bool) Field
func Bools(key string, bs []bool) Field// int types
func Int(key string, val int) Field
func Intp(key string, val *int) Field
func Ints(key string, nums []int) Field
// int8 types
func Int8(key string, val int8) Field
func Int8p(key string, val *int8) Field
func Int8s(key string, nums []int8) Field
// int16 types
func Int16(key string, val int16) Field
func Int16p(key string, val *int16) Field
func Int16s(key string, nums []int16) Field
// int32 types
func Int32(key string, val int32) Field
func Int32p(key string, val *int32) Field
func Int32s(key string, nums []int32) Field
// int64 types
func Int64(key string, val int64) Field
func Int64p(key string, val *int64) Field
func Int64s(key string, nums []int64) Field// uint types
func Uint(key string, val uint) Field
func Uintp(key string, val *uint) Field
func Uints(key string, nums []uint) Field
// uint8 types
func Uint8(key string, val uint8) Field
func Uint8p(key string, val *uint8) Field
func Uint8s(key string, nums []uint8) Field
// uint16 types
func Uint16(key string, val uint16) Field
func Uint16p(key string, val *uint16) Field
func Uint16s(key string, nums []uint16) Field
// uint32 types
func Uint32(key string, val uint32) Field
func Uint32p(key string, val *uint32) Field
func Uint32s(key string, nums []uint32) Field
// uint64 types
func Uint64(key string, val uint64) Field
func Uint64p(key string, val *uint64) Field
func Uint64s(key string, nums []uint64) Field
// uintptr types
func Uintptr(key string, val uintptr) Field
func Uintptrp(key string, val *uintptr) Field
func Uintptrs(key string, nums []uintptr) Field// float32 types
func Float32(key string, val float32) Field
func Float32p(key string, val *float32) Field
func Float32s(key string, nums []float32) Field
// float64 types
func Float64(key string, val float64) Field
func Float64p(key string, val *float64) Field
func Float64s(key string, nums []float64) Field// complex64 types
func Complex64(key string, val complex64) Field
func Complex64p(key string, val *complex64) Field
func Complex64s(key string, nums []complex64) Field
// complex128 types
func Complex128(key string, val complex128) Field
func Complex128p(key string, val *complex128) Field
func Complex128s(key string, nums []complex128) Field// UTF-8 string
func String(key string, val string) Field
func Stringp(key string, val *string) Field
func Strings(key string, ss []string) Field
// UTF-8 encoded byte slice (for textual data)
func ByteString(key string, val []byte) Field
func ByteStrings(key string, bss [][]byte) Field
// Binary data (for opaque byte blobs)
func Binary(key string, val []byte) FieldByteString vs Binary: Use ByteString for UTF-8 encoded text that happens to be in a byte slice. Use Binary for truly opaque binary data. Encoders may base64-encode binary data but log byte strings as text.
// time.Time
func Time(key string, val time.Time) Field
func Timep(key string, val *time.Time) Field
func Times(key string, ts []time.Time) Field
// time.Duration
func Duration(key string, val time.Duration) Field
func Durationp(key string, val *time.Duration) Field
func Durations(key string, ds []time.Duration) Field// Standard error field (key is "error")
func Error(err error) Field
// Error with custom key
func NamedError(key string, err error) Field
// Multiple errors
func Errors(key string, errs []error) FieldThe Error function is shorthand for NamedError("error", err). Error fields lazily call err.Error() only when the log entry is actually written.
func Any(key string, value interface{}) FieldAny automatically chooses the best field type for the value. It supports all primitive types, errors, stringers, and falls back to reflection for unknown types. Use specific field constructors when possible for better performance.
// Custom object marshaling
func Object(key string, val zapcore.ObjectMarshaler) Field
// Array of objects
func Objects[T zapcore.ObjectMarshaler](key string, values []T) Field
// Array of objects with pointer receivers
func ObjectValues[T any, P ObjectMarshalerPtr[T]](key string, values []T) Field
// Custom array marshaling
func Array(key string, val zapcore.ArrayMarshaler) FieldThese fields allow custom types to implement efficient marshaling:
type User struct {
ID int
Name string
}
func (u User) MarshalLogObject(enc zapcore.ObjectEncoder) error {
enc.AddInt("id", u.ID)
enc.AddString("name", u.Name)
return nil
}
logger.Info("user created", zap.Object("user", user))// Create nested object from fields
func Dict(key string, val ...Field) Field
// Helper to create ObjectMarshaler from fields
func DictObject(val ...Field) zapcore.ObjectMarshalerDictionary fields create nested objects in structured output:
logger.Info("request processed",
zap.Dict("request",
zap.String("method", "GET"),
zap.String("path", "/api/users"),
zap.Int("status", 200),
),
)
// JSON output includes nested "request": {"method": "GET", ...}func Inline(val zapcore.ObjectMarshaler) FieldInline fields merge an object's fields directly into the parent, without creating a nested object:
type RequestContext struct {
RequestID string
UserID int
}
func (rc RequestContext) MarshalLogObject(enc zapcore.ObjectEncoder) error {
enc.AddString("request_id", rc.RequestID)
enc.AddInt("user_id", rc.UserID)
return nil
}
logger.Info("processing", zap.Inline(ctx))
// Fields appear at top level: {"msg": "processing", "request_id": "...", "user_id": ...}func Reflect(key string, val interface{}) FieldReflect uses Go's reflection to serialize arbitrary values. This is slower and should be avoided in hot paths. Prefer Object, Array, or specific field types when possible.
func Stringer(key string, val fmt.Stringer) Field
func Stringers[T fmt.Stringer](key string, values []T) FieldStringer calls the String() method on values implementing fmt.Stringer. The call is deferred until the log entry is written.
Stringers constructs a field for a slice of fmt.Stringer values, calling String() on each element. Note that these objects must implement fmt.Stringer directly on the value type, not the pointer type.
// Capture stack trace
func Stack(key string) Field
// Capture stack trace skipping N frames
func StackSkip(key string, skip int) FieldStack trace fields capture the current goroutine's stack and include it in the log output. Use skip to omit wrapper functions from the trace.
func Namespace(key string) FieldNamespace creates an isolated scope for subsequent fields, useful for grouping related fields in nested objects:
logger.Info("transaction",
zap.Namespace("request"),
zap.String("method", "POST"),
zap.String("path", "/api/users"),
zap.Namespace("response"),
zap.Int("status", 201),
zap.Duration("latency", 45*time.Millisecond),
)
// Creates nested structure with "request" and "response" objectsfunc Skip() FieldSkip constructs a no-op field, which is often useful when handling invalid inputs in other field constructors. It can be used in conditional logic where you need to return a field but don't want to log anything:
func getUserField(user *User) zap.Field {
if user == nil {
return zap.Skip()
}
return zap.Object("user", user)
}type ObjectMarshalerPtr[T any] interface {
*T
zapcore.ObjectMarshaler
}Helper constraint for ObjectValues to handle types with pointer receiver marshaling methods.
logger.Info("user login",
zap.String("username", "alice"),
zap.Int("user_id", 12345),
zap.Bool("admin", false),
zap.Duration("session_duration", 2*time.Hour),
)if err != nil {
logger.Error("failed to save user",
zap.Error(err),
zap.String("username", username),
zap.Int("attempt", retryCount),
)
}
// Multiple errors
logger.Error("batch operation failed",
zap.Errors("errors", errorList),
)var count *int = nil
logger.Info("stats", zap.Intp("count", count)) // Logs null for nil pointer
value := 42
logger.Info("stats", zap.Intp("count", &value)) // Logs 42tags := []string{"api", "production", "critical"}
logger.Info("alert", zap.Strings("tags", tags))
values := []int{1, 2, 3, 4, 5}
logger.Debug("values", zap.Ints("values", values))startTime := time.Now()
// ... do work ...
duration := time.Since(startTime)
logger.Info("operation completed",
zap.Time("start_time", startTime),
zap.Duration("duration", duration),
)type Address struct {
Street string
City string
}
func (a Address) MarshalLogObject(enc zapcore.ObjectEncoder) error {
enc.AddString("street", a.Street)
enc.AddString("city", a.City)
return nil
}
type Person struct {
Name string
Age int
Address Address
}
func (p Person) MarshalLogObject(enc zapcore.ObjectEncoder) error {
enc.AddString("name", p.Name)
enc.AddInt("age", p.Age)
enc.AddObject("address", p.Address)
return nil
}
person := Person{
Name: "Alice",
Age: 30,
Address: Address{
Street: "123 Main St",
City: "Springfield",
},
}
logger.Info("person details", zap.Object("person", person))
// Produces nested JSON structurelogger.Info("http request",
zap.String("method", "POST"),
zap.Dict("headers",
zap.String("content-type", "application/json"),
zap.String("user-agent", "MyApp/1.0"),
),
zap.Dict("response",
zap.Int("status", 200),
zap.Duration("latency", 45*time.Millisecond),
),
)var value interface{} = getUserInput()
logger.Info("received value",
zap.Any("value", value), // Automatically determines correct field type
)
// More efficient when type is known:
if str, ok := value.(string); ok {
logger.Info("received value", zap.String("value", str))
}type RequestContext struct {
TraceID string
SpanID string
UserID string
}
func (rc RequestContext) MarshalLogObject(enc zapcore.ObjectEncoder) error {
enc.AddString("trace_id", rc.TraceID)
enc.AddString("span_id", rc.SpanID)
enc.AddString("user_id", rc.UserID)
return nil
}
ctx := RequestContext{
TraceID: "abc123",
SpanID: "def456",
UserID: "user789",
}
// Inline merges fields at top level
logger.Info("request received", zap.Inline(ctx))
// Produces: {"msg": "request received", "trace_id": "abc123", "span_id": "def456", "user_id": "user789"}
// Without Inline, creates nested object
logger.Info("request received", zap.Object("context", ctx))
// Produces: {"msg": "request received", "context": {"trace_id": "abc123", "span_id": "def456", "user_id": "user789"}}// Capture stack trace on errors
logger.Error("unexpected condition",
zap.Error(err),
zap.Stack("stacktrace"),
)
// Skip wrapper functions
func LogError(msg string, err error) {
logger.Error(msg,
zap.Error(err),
zap.StackSkip("stacktrace", 1), // Skip this wrapper function
)
}Use specific field constructors: zap.String(), zap.Int(), etc. are faster than zap.Any() or zap.Reflect()
Avoid reflection: zap.Reflect() is slow. Implement ObjectMarshaler or ArrayMarshaler for custom types
Lazy evaluation: Error fields, Stringer fields, and Object/Array fields defer work until the log is actually written
Pointer fields: The *p variants (e.g., zap.Intp()) add overhead but handle nil correctly
Zero allocations: Most field constructors make zero allocations, making zap suitable for hot paths
| Category | Use Case | Examples |
|---|---|---|
| Primitives | Numbers, booleans | Int, Float64, Bool |
| Strings | Text data | String, ByteString |
| Time | Timestamps, durations | Time, Duration |
| Errors | Error values | Error, NamedError, Errors |
| Objects | Complex types | Object, Dict, Inline |
| Arrays | Slices | Ints, Strings, Objects |
| Advanced | Unknown/dynamic types | Any, Reflect |
| Meta | Structure/debugging | Namespace, Stack |