Skip to content

domonda/golog

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

389 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

golog

Fast and feature-rich structured logging library for Go

GitHub go.mod Go version Go Reference License

Table of Contents

Features

  • High Performance: Zero-allocation logging for JSON, text, and complex fields (error, time.Time)
  • Structured Logging: Type-safe field methods for all Go primitives including native time.Time and UUID
  • Tag-driven Struct Logging: StructFields and TaggedStructFields honor golog, log, and json tags with omitempty, omitzero, omitnull, and redact modifiers. encoding/json-compatible where applicable
  • Multiple Output Formats: JSON and human-readable text output
  • Terminal Auto-Detection: Automatically switches between colored text (TTY) and JSON (non-TTY)
  • Configurable Log Levels: TRACE, DEBUG, INFO, WARN, ERROR, FATAL with flexible filtering
  • Context Support: Log attributes can be stored in and retrieved from context automatically
  • Duplicate Key Prevention: A message attribute is dropped when the same key was already set by the logger, a sub-logger, or the context (the inherited value wins), keeping inherited structured data clean
  • Colorized Output: Beautiful colored console output with customizable colorizers
  • Multi-Writer Architecture: Log to multiple destinations with different formats and filters
  • Rotating Log Files: Automatic file rotation based on size thresholds
  • slog Integration: Use as a backend for Go's standard log/slog package
  • HTTP Middleware: Built-in HTTP request/response logging with request ID propagation
  • UUID Support: Native UUID logging with zero allocations
  • Call Stack Tracing: Capture and log call stacks for debugging
  • Memory Safety: Nil-safe logger implementation prevents panics
  • Sub-loggers: Create child loggers with inherited attributes

Production Tested

golog has been used in production at domonda since 2023, powering mission-critical business applications with reliable, high-performance logging.

Installation

go get github.com/domonda/golog

Quick Start

Basic Usage

package main

import (
    "errors"
    "os"

    "github.com/domonda/golog"
)

func main() {
    // Create a basic text logger
    config := golog.NewConfig(
        &golog.DefaultLevels,
        golog.AllLevelsActive,
        golog.NewTextWriterConfig(os.Stdout, nil, nil),
    )

    log := golog.NewLogger(config)

    // Simple logging
    log.Info("Hello, World!").Log()
    log.Error("Something went wrong").Err(errors.New("example error")).Log()
}

JSON Output

// Create a JSON logger
config := golog.NewConfig(
    &golog.DefaultLevels,
    golog.AllLevelsActive,
    golog.NewJSONWriterConfig(os.Stdout, nil),
)

log := golog.NewLogger(config)

start := time.Now()
// ... perform login ...
log.Info("User login").
    Str("username", "john_doe").
    Str("ip", "192.168.1.1").
    Duration("login_time", time.Since(start)).
    Log()

Output (matches NewDefaultFormat: "time" key, "2006-01-02 15:04:05.000" layout):

{"time":"2024-01-15 10:30:45.000","level":"INFO","message":"User login","username":"john_doe","ip":"192.168.1.1","login_time":"150ms"}

Structured Logging with All Data Types

requestID := golog.UUIDv4()           // [16]byte UUID
jsonBytes := []byte(`{"key":"value"}`) // Raw JSON

log.Info("Processing request").
    Str("method", "POST").
    Str("path", "/api/users").
    Int("user_id", 12345).
    Bool("authenticated", true).
    Float("response_time", 0.145).
    UUID("request_id", requestID).
    Time("started_at", time.Now()).
    Strs("tags", []string{"api", "user", "create"}).
    JSON("metadata", jsonBytes).
    Log()

Log Levels

golog supports six standard log levels:

  • TRACE (-20): Most verbose, for tracing execution flow
  • DEBUG (-10): Debug information for development
  • INFO (0): General information messages
  • WARN (10): Warning messages for potentially harmful situations
  • ERROR (20): Error conditions that don't require immediate attention
  • FATAL (30): Critical errors that may cause application termination

Level-specific Methods

log.Trace("Entering function").Str("function", "processData").Log()
log.Debug("Variable state").Int("counter", 42).Log()
log.Info("Operation completed").Log()
log.Warn("Deprecated API used").Str("api", "/old/endpoint").Log()
log.Error("Failed to connect").Err(err).Log()
log.Fatal("Critical system failure").Log()

Sub-loggers and Context

Creating Sub-loggers

requestID := golog.UUIDv4()

// Create a sub-logger with common attributes
subLog := log.With().
    Str("service", "user-management").
    UUID("request_id", requestID).
    SubLogger()

// All logs from subLog will include the above attributes
subLog.Info("User created").Str("username", "john").Log()
subLog.Error("User validation failed").Err(errors.New("invalid email")).Log()

Context Integration

// Add attributes to context
ctx = golog.ContextWithAttribs(ctx,
    golog.NewString("correlation_id", "abc-123"),
    golog.NewString("user_id", "user-456"),
)

// Create logger from context
ctxLogger := log.WithCtx(ctx)
ctxLogger.Info("Operation started").Log() // Includes context attributes

// Or use the logger's InfoCtx method
logger.InfoCtx(ctx, "Operation started").Log() // Includes context attributes

Multiple Writers and Filtering

logFile, _ := os.Create("app.log")
defer logFile.Close()

// Log to multiple outputs with different formats
config := golog.NewConfig(
    &golog.DefaultLevels,
    golog.AllLevelsActive,
    golog.NewTextWriterConfig(os.Stdout, nil, golog.NewStyledColorizer()),
    // FilterOutBelow filters out less severe levels (lower numeric values)
    // Warn=10, so this filters out Trace=-20, Debug=-10, Info=0
    // Only Warn=10, Error=20, Fatal=30 will be logged to the file
    golog.NewJSONWriterConfig(logFile, nil, golog.DefaultLevels.Warn.FilterOutBelow()),
)

log := golog.NewLogger(config)

// This will appear in colored text on stdout and as JSON in the file (if WARN+)
log.Error("Database connection failed").Err(errors.New("connection refused")).Log()

Terminal Detection

golog can automatically switch between human-readable text and machine-readable JSON output based on whether the process is attached to a terminal (TTY):

config := golog.NewConfig(
    &golog.DefaultLevels,
    golog.AllLevelsActive,
    golog.DecideWriterConfigForTerminal(
        golog.NewTextWriterConfig(os.Stdout, nil, golog.NewStyledColorizer()), // Used when running in terminal
        golog.NewJSONWriterConfig(os.Stdout, nil),                             // Used when output is piped/redirected
    ),
)

log := golog.NewLogger(config)
  • Terminal (TTY): Outputs colored, human-readable text format
  • Non-terminal: Outputs machine-readable JSON format (ideal for log aggregation systems)

You can also check the terminal status directly:

if golog.IsTerminal() {
    // Running in a terminal
}

This feature is useful for:

  • Development: Human-readable logs in the terminal
  • Production: Machine-parseable JSON logs for log aggregation systems
  • CI/CD: Automatic format switching based on the environment
  • Containerized Applications: Proper format when logs are piped to files or log collectors

Ready-to-Use Logger (log subpackage)

For quick setup, the log subpackage provides a pre-configured logger with sensible defaults:

import (
    "errors"

    "github.com/domonda/golog/log"
)

func main() {
    log.Info("Application started").Log()
    log.Error("Something went wrong").Err(errors.New("example error")).Log()
    log.Debug("Debug information").Str("key", "value").Log()
}

Default Configuration

The log package is configured with:

  • Log Levels: Uses golog.DefaultLevels (TRACE, DEBUG, INFO, WARN, ERROR, FATAL)
  • Level Filter: Filters out levels below LOG_LEVEL environment variable (defaults to DEBUG if not set)
  • Output: Writes to stdout with automatic format selection:
    • Terminal: Colorized text format for human readability
    • Non-terminal: JSON format for log aggregation systems

Customizing the Default Logger

The log.Config variable can be modified at runtime:

import "github.com/domonda/golog/log"

func init() {
    // Change the minimum log level
    // Info=0, so FilterOutBelow filters out Trace=-20 and Debug=-10
    // Only Info=0, Warn=10, Error=20, Fatal=30 will be logged
    log.Config = golog.NewConfig(
        log.Levels,
        log.Levels.Info.FilterOutBelow(),
        golog.DecideWriterConfigForTerminal(
            golog.NewTextWriterConfig(os.Stdout, &log.Format, &log.Colorizer),
            golog.NewJSONWriterConfig(os.Stdout, &log.Format),
        ),
    )
}

The logger uses a DerivedConfig that references log.Config, so changes to log.Config take effect immediately without recreating the logger.

Available Package-Level Functions

The log package exposes convenience functions for all log levels:

log.Trace("message").Log()
log.Debug("message").Log()
log.Info("message").Log()
log.Warn("message").Log()
log.Error("message").Log()
log.Fatal("message").Log()

// With context - attributes added to context are automatically included
ctx := golog.ContextWithAttribs(context.Background(),
    golog.NewString("request_id", "req-123"),
    golog.NewString("user_id", "user-456"),
)
log.InfoCtx(ctx, "processing request").Log() // Includes request_id and user_id

// Formatted messages
log.Infof("User %s logged in", "john_doe").Log()

// Create sub-loggers
subLog := log.With().Str("component", "auth").SubLogger()

Rotating Log Files

Automatic file rotation based on size thresholds using the logfile subpackage:

import (
    "github.com/domonda/golog"
    "github.com/domonda/golog/logfile"
)

// Create a rotating writer that rotates at 10MB
writer, err := logfile.NewRotatingWriter(
    "/var/log/myapp.log",                    // File path
    logfile.RotatingWriterDefaultTimeFormat, // Time format for rotated files
    0644,         // File permissions
    10*1024*1024, // Rotate at 10MB
)
if err != nil {
    log.Fatal(err)
}
defer writer.Close()

// Use with golog
config := golog.NewConfig(
    &golog.DefaultLevels,
    golog.AllLevelsActive,
    golog.NewJSONWriterConfig(writer, nil),
)

log := golog.NewLogger(config)

log.Info("Application started").Log()

When the log file reaches 10MB:

  • The current file is renamed with a timestamp (e.g., myapp.log.2024-01-15_10:30:45)
  • A new file is created at the original path
  • Logging continues seamlessly to the new file

Multiple Writers with Rotation

// Console output with colors + rotating JSON file
fileWriter, _ := logfile.NewRotatingWriter("/var/log/app.log", "", 0644, 50*1024*1024)
defer fileWriter.Close()

config := golog.NewConfig(
    &golog.DefaultLevels,
    golog.AllLevelsActive,
    golog.NewTextWriterConfig(os.Stdout, nil, golog.NewStyledColorizer()),
    golog.NewJSONWriterConfig(fileWriter, nil),
)

log := golog.NewLogger(config)

See the logfile package documentation for more details.

Standard Library Integration (slog)

Use golog as a backend for Go's standard log/slog package via the goslog adapter:

import (
    "log/slog"

    "github.com/domonda/golog"
    "github.com/domonda/golog/goslog"
)

// Create golog logger
gologLogger := golog.NewLogger(
    golog.NewConfig(
        &golog.DefaultLevels,
        golog.AllLevelsActive,
        golog.NewJSONWriterConfig(os.Stdout, nil),
    ),
)

// Create slog handler that uses golog
handler := goslog.Handler(gologLogger, goslog.ConvertDefaultLevels)

// Use with slog
logger := slog.New(handler)
logger.Info("Hello from slog", "key", "value")

Benefits of slog Integration

  • Standard API: Use Go's standard library slog API
  • Existing Code: Works with existing code that uses slog
  • golog Features: Get all golog benefits (multiple writers, rotation, colors)
  • Full Compatibility: Passes slogtest compliance suite

See the goslog package documentation for more details.

HTTP Middleware

func loggingMiddleware(log *golog.Logger) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            start := time.Now()
            
            // Log request
            log.Info("HTTP request").
                Request(r).
                Log()
            
            next.ServeHTTP(w, r)
            
            // Log response
            log.Info("HTTP response").
                Str("method", r.Method).
                Str("path", r.URL.Path).
                Duration("duration", time.Since(start)).
                Log()
        })
    }
}

Advanced Features

Custom Colorizers

colorizer := golog.NewStyledColorizer()
// or implement your own Colorizer interface

config := golog.NewConfig(
    &golog.DefaultLevels,
    golog.AllLevelsActive,
    golog.NewTextWriterConfig(os.Stdout, nil, colorizer),
)

Call Stack Logging

defer func() {
    if r := recover(); r != nil {
        log.Error("Panic recovered").
            CallStack("stack").
            Any("panic_value", r).
            Log()
    }
}()

Struct Field Logging: Tags and Modifiers

StructFields and TaggedStructFields walk a struct via reflection and log each matching field as an attribute. They are driven entirely by struct tags, with modifiers that mirror encoding/json.Marshal semantics plus a handful of golog-specific additions.

Tag resolution

StructFields(s) looks for tags in this order — first match wins:

  1. golog:"..."
  2. log:"..."
  3. json:"..."

Only the first tag present on a field is consulted. Other tags on the same field are ignored. Fields with none of those tags are skipped entirely (this is a deliberate difference from encoding/json.Marshal, which would use the Go field name: golog errs on the side of not leaking untagged internal fields into logs).

TaggedStructFields(s, "json") does the same with a single caller-chosen tag.

Wildcard escape hatch: passing the empty string as the key tag, TaggedStructFields(s, ""), matches every exported field with no rename and no modifiers — every field is logged under its Go name, struct tags ignored. Use this when you want to dump a struct wholesale and you don't care about per-field tags. (StructFields(s) deliberately stays strict tag-driven; the wildcard is opt-in.)

Tag value grammar

Each tag value is name,mod1,mod2,...:

Tag value Log key Notes
"field_name" field_name explicit name
"" Go field name empty → fall back to field name
",omitempty" Go field name empty name with modifier → fall back
"-" (bare, no comma) skipped only the bare form skips
"-,..." (comma follows) - literal field name - + modifiers

Whitespace around the name and each modifier is trimmed. Unknown modifier tokens are ignored silently, so json:"name,omitnull" is a valid tag for both encoding/json.Marshal (which ignores omitnull) and golog (which honors it).

Modifiers

Modifier Suppress when…
omitempty false, 0, nil pointer/interface, empty array/slice/map/string. Exactly matches encoding/json.Marshal. Does not catch zero structs or time.Time{}.
omitzero The type's IsZero() bool method returns true, or the value is the Go zero value (via reflect.Value.IsZero). Catches time.Time{} and any zero struct. Strict superset of encoding/json's Go 1.24 omitzero. Both value-receiver and pointer-receiver IsZero methods are honored.
omitnull The type's IsNull() bool method returns true. Falls back to omitzero semantics when the type has no IsNull method. Use for nullable wrappers (golog.Timestamp, sql.NullString, uu.NullableID) where "null" is richer than "all bytes zero". Both value-receiver and pointer-receiver IsNull methods are honored.
redact (not a suppression modifier) — replaces the value with "***REDACTED***" before it reaches the writer. Also spelled redacted. Suppression modifiers win over redact: json:",redact,omitempty" on an empty string emits nothing, not the marker.

Modifiers OR together — any passing check suppresses the field.

Complete example

type User struct {
    ID        int             `json:"id"`
    Name      string          `json:"name,omitempty"`
    APIKey    string          `json:",redact"`
    CreatedAt time.Time       `json:"created_at,omitzero"`
    DeletedAt golog.Timestamp `json:"deleted_at,omitnull"`
    Internal  string          // no json/log/golog tag → never logged
}

user := User{
    ID:        123,
    Name:      "john_doe",
    APIKey:    "sk-abc123",
    CreatedAt: time.Now(),
    // DeletedAt left as zero Timestamp
    Internal:  "debug-only",
}

log.Info("user").TaggedStructFields(user, "json").Log()
// Output: ... id=123 name="john_doe" APIKey="***REDACTED***" created_at="2026-04-14T..."
// (DeletedAt suppressed by omitnull, Internal never considered.)

Breaking changes in this release

  • log:",redact" (bare, single token) no longer triggers redaction — under the unified parser it names the field "redact". Migrate to golog:",redact" (or combine with other modifiers: golog:",redact,omitempty").
  • StructFields(s) on an untagged struct now logs nothing. Previously it logged every exported field by its Go name. The cleanest replacement is TaggedStructFields(s, ""), the wildcard escape hatch documented above. Per-field, you can also add an empty tag like json:"" or golog:"" to opt in.
  • TaggedStructFields(s, "json") with json:"" now logs the field (Go field name), previously it skipped. This is encoding/json.Marshal parity.

omitnull vs omitzero, concretely: sql.NullString{Valid: false, String: ""} and sql.NullString{Valid: true, String: ""} are both the reflect zero value, but only the first is actually null. omitnull with a proper IsNull method distinguishes the two; omitzero cannot.

Custom Levels

customLevels := &golog.Levels{
    Trace: -20,
    Debug: -10,
    Info:  0,
    Warn:  10,
    Error: 20,
    Fatal: 30,
    Names: map[golog.Level]string{
        -20: "TRACE",
        -10: "DEBUG",
        0:   "INFO",
        10:  "WARN",
        20:  "ERROR",
        30:  "FATAL",
    },
}

Level Filtering

Log levels have numeric values where lower = more verbose, higher = more severe: Trace=-20 < Debug=-10 < Info=0 < Warn=10 < Error=20 < Fatal=30

// FilterOutBelow filters out less severe levels (lower numeric values)
// Warn=10, so this filters out Trace=-20, Debug=-10, Info=0
// Result: only Warn, Error, Fatal are logged
filter := golog.DefaultLevels.Warn.FilterOutBelow()

// Filter out only DEBUG level (Debug=-10)
debugFilter := golog.DefaultLevels.Debug.FilterOut()

// Filter out everything except INFO (Info=0)
infoOnlyFilter := golog.DefaultLevels.Info.FilterOutAllOther()

// Combine multiple filters (filters are OR'd together)
// This filters out both Trace and Debug
combinedFilter := golog.JoinLevelFilters(
    golog.DefaultLevels.Trace.FilterOut(),
    golog.DefaultLevels.Debug.FilterOut(),
)

Logging in a Fixed Timezone

Set Format.Location to render every formatted time value, both the log line timestamp and structured time.Time attributes, in a fixed timezone. The writer calls time.Time.In on each value before formatting, so the underlying instant is preserved. Leave it nil (the default) to keep each value's original location.

tokyo, _ := time.LoadLocation("Asia/Tokyo")

format := golog.NewDefaultFormat()
format.Location = tokyo

config := golog.NewConfig(
    &golog.DefaultLevels,
    golog.AllLevelsActive,
    golog.NewJSONWriterConfig(os.Stdout, format),
)

Parsing Log Timestamps

Use golog.Timestamp when you need to read log timestamps back out of JSON, a database column, or any other place where the format varies. It wraps time.Time with JSON, sql.Scanner, driver.Valuer, and null semantics, and parses a wide range of common layouts out of the box:

// JSON unmarshal: accepts RFC3339(+nano), golog's own
// "2006-01-02 15:04:05.000", Apache/NGINX CLF, RFC1123(Z),
// syslog Stamp, compact ISO 8601, Go log default, or a JSON
// number interpreted as Unix epoch seconds. JSON null is zero.
type logLine struct {
    Time    golog.Timestamp `json:"time"`
    Level   string          `json:"level"`
    Message string          `json:"message"`
}

// Directly parse a string with any supported layout:
ts, err := golog.ParseTimestamp("2024-01-15 10:30:45.000")

// Plug into database/sql — SQL NULL maps to the zero value:
var ts golog.Timestamp
err = db.QueryRow("SELECT logged_at FROM events WHERE id = $1", id).Scan(&ts)
if ts.IsNull() {
    // column was NULL
}

The accepted layouts live in the exported golog.TimestampFormats slice, so you can extend or replace it before use if your log source needs extra layouts. MarshalJSON always emits DefaultTimeFormat (time.RFC3339Nano), or null when the value is zero.

For storing the current request's event time in context.Context, use golog.ContextWithTimestamp(ctx, t) together with golog.TimestampFromContextOrNow(ctx) (returns time.Now() when no timestamp is stored or the stored value is zero).

Upgrading from earlier versions: the old golog.Timestamp(ctx context.Context) time.Time helper has been renamed to golog.TimestampFromContextOrNow(ctx) to free the Timestamp identifier for the new type. ContextWithTimestamp is now generic over time.Time | golog.Timestamp.

Performance

golog is designed for high performance:

  • Zero-allocation logging in most cases
  • Object pooling for message and writer instances
  • Efficient JSON encoding
  • Lazy evaluation of expensive operations
  • Minimal overhead for inactive log levels

Benchmarks

Run internal benchmarks:

go test -bench=. -benchmem

For comparative benchmarks against other popular Go logging libraries (zerolog, zap, slog, logrus), the benchmarks are in a separate module to avoid adding those dependencies to the main golog module:

go test ./benchmarks -bench=. -benchmem -benchtime=1s

See benchmarks/README.md for detailed comparative analysis and performance insights.

Comparison with Other Logging Libraries

golog is designed to strike a balance between performance and flexibility. While libraries like zerolog and zap prioritize raw speed, golog provides a richer feature set that makes it more adaptable to complex logging requirements.

Performance vs Flexibility Tradeoffs

Feature zerolog zap golog
Multi-writer support Single output Limited Native, unlimited
Duplicate key prevention (inherited) No No Yes
Context attribute integration Manual Manual Automatic
Sub-logger with inherited attributes Basic Basic Full support with attrib recording
Zero allocations (simple message) Yes Yes Yes
Zero allocations (with fields) Yes No (1 alloc) Yes
slog compatibility Separate adapter Separate adapter Native goslog package

Architectural Differences

zerolog: Extreme Minimalism

  • Optimized for a single use case: fast JSON logging to a single output
  • Minimal abstraction layers result in the fastest raw JSON performance
  • Disabled log levels have near-zero overhead (~5 ns/op)
  • Trade-off: Limited flexibility for complex logging scenarios
  • Trade-off: ConsoleWriter for text output is significantly slower (2363 ns/op vs 51 ns/op for JSON)

zap: Performance + Type Safety

  • Typed Field structs provide compile-time safety
  • Separate "Sugar" logger offers convenience at the cost of performance
  • Trade-off: Allocates a slice for variadic field arguments (~1 alloc per log call with fields)

golog: Flexibility + Features

  • Native multi-writer architecture: Log to console, files, and external services simultaneously with different formats and filters per destination
  • Automatic context integration: Attributes added to context.Context are automatically included in log messages without manual plumbing
  • Sub-logger attribute recording: The With().SubLogger() pattern creates child loggers that efficiently inherit and extend parent attributes
  • Duplicate key prevention: When a message sets a key already provided by the logger, a sub-logger, or the context, the inherited value is kept and the message-level duplicate is dropped, ensuring clean inherited structured data
  • Zero allocations for standard logging: Despite the richer feature set, golog achieves zero allocations for JSON logging with fields
  • Nil-safe design: A nil logger is safe to use and won't panic, simplifying error handling

When to Choose golog

golog is the right choice when you need:

  • Multiple output destinations: Log to stdout with colors for development and JSON files for production simultaneously
  • Request-scoped logging: Automatically propagate correlation IDs, user IDs, and other context through your application
  • Sub-loggers with inherited context: Create child loggers for specific components that include parent attributes
  • Clean structured data: Prevent keys inherited from sub-loggers or context from being duplicated by message-level attributes
  • slog compatibility: Use golog as a backend for Go's standard library logging interface
  • Rotating log files: Built-in support for size-based log rotation

When to Choose Alternatives

  • zerolog: When raw JSON logging speed is the only priority and you don't need multi-writer support, context integration, or text/console output (zerolog's ConsoleWriter is slow at 2363 ns/op)
  • zap: When you prefer a variadic field API with compile-time type checking
  • slog: When you want zero external dependencies and good-enough performance from the standard library

Real-World Performance

For most applications, the performance difference between logging libraries is negligible. At 250 ns/op for a simple JSON message, golog can handle 4 million log messages per second on a single core. With structured fields, golog (353 ns/op) is faster than zap (451 ns/op). With complex fields including error and time.Time, golog achieves 391 ns/op with zero allocations, outperforming zap's 475 ns/op with 1 allocation. The additional features golog provides—multi-writer support, context integration, and duplicate key prevention—often save more development time than the nanoseconds saved by faster alternatives.

Note on text output: golog is the fastest for text/console output at 394 ns/op with zero allocations. This outperforms zap (514 ns/op, 4 allocs), slog (677 ns/op, 3 allocs), and especially zerolog's ConsoleWriter (2363 ns/op with 46 allocations).

The performance gap for JSON output becomes meaningful only in extreme high-throughput scenarios (100K+ logs/second sustained), where zerolog's 51 ns/op provides measurable benefits. For typical applications, golog's flexibility and rich feature set make it a more productive choice.

API Reference

Core Types

  • Logger: Main logging interface
  • Message: Fluent message builder
  • Config: Logger configuration
  • WriterConfig: Output writer configuration
  • Level: Log level type
  • LevelFilter: Level filtering interface
  • Format: Layout config for timestamp keys, timestamp layouts, level keys, message keys, structured time.Time attributes, and an optional *time.Location to render every time value in a fixed timezone
  • Timestamp: time.Time wrapper with JSON, database/sql.Scanner/driver.Valuer, and null semantics, tuned for parsing log timestamps in many common formats

Writer Types

  • JSONWriter: Structured JSON output
  • TextWriter: Human-readable text output
  • CallbackWriter: Custom callback-based writer
  • MultiWriter: Multiple writer composition
  • NopWriter: No-operation writer for testing

For complete API documentation, see pkg.go.dev.

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Fast and feature-rich structured logging for Go

Resources

License

Stars

0 stars

Watchers

3 watching

Forks

Packages

 
 
 

Contributors

Languages