golog

package module
v1.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 11, 2026 License: MIT Imports: 3 Imported by: 10

README

golog

Go Reference Go Report Card

English | 中文

A flexible and structured logging library for Go, built on top of uber-go/zap and implementing the gsr logger interface.

Features

  • 🚀 High Performance: Built on uber-go/zap, one of the fastest structured logging libraries
  • 🎯 Structured Logging: Support for strongly-typed, structured log fields
  • 🔧 Flexible Configuration: Multiple initialization options for different environments
  • 📊 Multiple Log Levels: Debug, Info, Notice, Warn, Error, Fatal, and Panic
  • 🎨 Multiple Output Formats: JSON and console encoding
  • 🔌 Interface Compliant: Implements the gsr.Logger interface
  • 🛠️ Easy to Use: Simple and intuitive API
  • 📍 Accurate Caller Info: Logs show the correct file and line number where the log was called

Installation

go get github.com/muleiwu/golog

Quick Start

package main

import (
    "github.com/muleiwu/golog"
)

func main() {
    // Create a development logger
    logger, err := golog.NewDevelopmentLogger()
    if err != nil {
        panic(err)
    }
    defer logger.Sync()

    // Simple logging
    logger.Info("Application started")

    // Structured logging with fields
    logger.Info("User logged in",
        golog.Field("user_id", 12345),
        golog.Field("username", "john_doe"),
        golog.Field("ip", "192.168.1.1"),
    )
}

Usage

Logger Initialization
Development Logger

Best for development environments with human-readable console output:

logger, err := golog.NewDevelopmentLogger()
if err != nil {
    panic(err)
}
defer logger.Sync()
Production Logger

Optimized for production with JSON output:

logger, err := golog.NewProductionLogger()
if err != nil {
    panic(err)
}
defer logger.Sync()
Example Logger

For testing purposes only (not recommended for production):

logger := golog.NewLogger()
Custom Configuration

Create a logger with custom settings:

logger, err := golog.NewLoggerWithConfig(golog.Config{
    Level:            golog.DebugLevel,  // Use golog.Level constants
    Development:      true,
    Encoding:         "console",
    OutputPaths:      []string{"stdout", "/var/log/app.log"},
    ErrorOutputPaths: []string{"stderr"},
})
if err != nil {
    panic(err)
}
defer logger.Sync()

Available Log Levels:

  • golog.DebugLevel - Debug messages
  • golog.InfoLevel - Informational messages (default)
  • golog.WarnLevel - Warning messages
  • golog.ErrorLevel - Error messages
  • golog.FatalLevel - Fatal messages (calls os.Exit)
  • golog.PanicLevel - Panic messages (panics after logging)
From Existing Zap Logger

Wrap an existing zap.Logger:

zapLogger, _ := zap.NewProduction()
logger := golog.NewLoggerWithZap(zapLogger)
Logging Levels
logger.Debug("Debug message", golog.Field("key", "value"))
logger.Info("Info message", golog.Field("key", "value"))
logger.Notice("Notice message", golog.Field("key", "value"))  // Mapped to Info
logger.Warn("Warning message", golog.Field("key", "value"))
logger.Error("Error message", golog.Field("key", "value"))
logger.Fatal("Fatal message", golog.Field("key", "value"))    // Calls os.Exit(1)
logger.Panic("Panic message", golog.Field("key", "value"))    // Panics after logging
Structured Logging

Add context to your logs with fields:

logger.Info("Processing request",
    golog.Field("request_id", "abc-123"),
    golog.Field("method", "GET"),
    golog.Field("path", "/api/users"),
    golog.Field("duration_ms", 45),
)
Child Loggers

Create child loggers with pre-populated fields:

// Create a child logger with common fields
requestLogger := logger.With(
    golog.Field("request_id", "abc-123"),
    golog.Field("user_id", 12345),
)

// All logs from requestLogger will include these fields
requestLogger.Info("Request started")
requestLogger.Info("Request completed")
Advanced Usage
Direct Zap Access

Access the underlying zap.Logger for advanced features:

zapLogger := logger.GetZapLogger()
// Use zap-specific features
Using Zap Fields Directly

For better performance, you can use zap fields directly:

import "go.uber.org/zap"

childLogger := logger.WithZapFields(
    zap.String("service", "api"),
    zap.Int("port", 8080),
)

Configuration Options

The Config struct supports the following options:

Field Type Description
Level golog.Level Minimum logging level (DebugLevel, InfoLevel, WarnLevel, ErrorLevel, FatalLevel, PanicLevel)
Development bool Enable development mode (more human-readable)
Encoding string Output format: "json" or "console"
OutputPaths []string Output destinations (e.g., "stdout", file paths)
ErrorOutputPaths []string Error output destinations (e.g., "stderr")
CallerSkip uint Additional stack frames to skip (automatically +1 for golog). Default: 0 (total skip=1). Set to 1 for single wrapper, 2 for double wrapper, etc.
Log Levels
  • DebugLevel: Fine-grained debugging information
  • InfoLevel: General informational messages
  • WarnLevel: Warning messages for potentially harmful situations
  • ErrorLevel: Error messages for serious problems
  • FatalLevel: Very severe errors that will lead to program exit
  • PanicLevel: Very severe errors that will cause a panic

Best Practices

  1. Always call Sync(): Ensure logs are flushed before program exit

    defer logger.Sync()  // Safe to call, handles stdout/stderr gracefully
    

    Note: Sync() automatically ignores errors from stdout/stderr (which cannot be synced on some systems), so you can safely use defer logger.Sync() without worrying about "bad file descriptor" errors.

  2. Use appropriate log levels:

    • Debug for development debugging
    • Info for general information
    • Warn for potentially harmful situations
    • Error for errors that need attention
    • Fatal/Panic only for critical failures
  3. Use structured fields: Instead of string formatting, use fields

    // Good
    logger.Info("User action", golog.Field("user_id", userID), golog.Field("action", "login"))
    
    // Avoid
    logger.Info(fmt.Sprintf("User %d performed action: login", userID))
    
  4. Create child loggers: For request-scoped or context-specific logging

    requestLogger := logger.With(golog.Field("request_id", requestID))
    
  5. Use production logger in production: Development logger is not optimized for performance

Examples

Web Server Example
package main

import (
    "net/http"
    "github.com/muleiwu/golog"
    "go.uber.org/zap/zapcore"
)

func main() {
    logger, err := golog.NewLoggerWithConfig(golog.Config{
        Level:            golog.InfoLevel,
        Development:      false,
        Encoding:         "json",
        OutputPaths:      []string{"stdout", "/var/log/server.log"},
        ErrorOutputPaths: []string{"stderr"},
    })
    if err != nil {
        panic(err)
    }
    defer logger.Sync()

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        logger.Info("Request received",
            golog.Field("method", r.Method),
            golog.Field("path", r.URL.Path),
            golog.Field("remote_addr", r.RemoteAddr),
        )
        w.Write([]byte("Hello, World!"))
    })

    logger.Info("Server starting", golog.Field("port", 8080))
    if err := http.ListenAndServe(":8080", nil); err != nil {
        logger.Fatal("Server failed to start", golog.Field("error", err))
    }
}
Error Handling Example
func processUser(logger *golog.Logger, userID int) error {
    userLogger := logger.With(golog.Field("user_id", userID))

    userLogger.Debug("Starting user processing")

    user, err := fetchUser(userID)
    if err != nil {
        userLogger.Error("Failed to fetch user", golog.Field("error", err))
        return err
    }

    userLogger.Info("User fetched successfully", golog.Field("username", user.Name))
    return nil
}
Wrapping Logger Example

If you wrap golog in your own logger, set CallerSkip to the number of wrapper layers:

type MyLogger struct {
    logger *golog.Logger
}

func NewMyLogger() (*MyLogger, error) {
    logger, err := golog.NewLoggerWithConfig(golog.Config{
        Level:       golog.InfoLevel,
        Development: true,
        Encoding:    "console",
        OutputPaths: []string{"stdout"},
        CallerSkip:  1, // 1 wrapper layer (auto +1 for golog = total 2)
    })
    if err != nil {
        return nil, err
    }
    return &MyLogger{logger: logger}, nil
}

func (m *MyLogger) Info(msg string, fields ...gsr.LoggerField) {
    m.logger.Info(msg, fields...)  // Now shows correct caller location
}

CallerSkip Guide:

  • 0 = Direct usage (skip 1: golog only) - same as preset loggers
  • 1 = Single wrapper (skip 2: golog + your wrapper)
  • 2 = Double wrapper (skip 3: golog + 2 wrappers)
  • N = N wrappers (skip N+1: golog + N wrappers)

Dependencies

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

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

Acknowledgments

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Field

func Field(key string, value any) gsr.LoggerField

Field creates a new LoggerField with the given key and value This is the primary way to add structured context to log messages

Example:

logger.Info("user logged in", Field("user_id", 123), Field("ip", "192.168.1.1"))

Types

type Config

type Config struct {
	// Level sets the minimum enabled logging level
	Level Level
	// Development puts the logger in development mode
	Development bool
	// Encoding sets the logger's encoding (json or console)
	Encoding string
	// OutputPaths is a list of URLs or file paths to write logging output to
	OutputPaths []string
	// ErrorOutputPaths is a list of URLs to write internal logger errors to
	ErrorOutputPaths []string
	// CallerSkip increases the number of callers skipped by caller annotation
	// Default is 0, which will be automatically set to 1 (skip golog wrapper).
	// Set to 1+ if you wrap golog in your own logger (1 = single wrap, 2 = double wrap, etc.).
	CallerSkip uint
}

Config holds the configuration for creating a new logger

type Level added in v1.1.0

type Level int8

Level represents the logging level

const (
	// DebugLevel logs are typically voluminous, and are usually disabled in production.
	DebugLevel Level = iota - 1
	// InfoLevel is the default logging priority.
	InfoLevel
	// WarnLevel logs are more important than Info, but don't need individual human review.
	WarnLevel
	// ErrorLevel logs are high-priority. If an application is running smoothly,
	// it shouldn't generate any error-level logs.
	ErrorLevel
	// FatalLevel logs a message, then calls os.Exit(1).
	FatalLevel
	// PanicLevel logs a message, then panics.
	PanicLevel
)

func (Level) String added in v1.1.0

func (l Level) String() string

String returns a lower-case ASCII representation of the log level.

type Logger

type Logger struct {
	// contains filtered or unexported fields
}

Logger wraps zap.Logger and implements the gsr.Logger interface

func NewDevelopmentLogger

func NewDevelopmentLogger() (*Logger, error)

NewDevelopmentLogger creates a logger suitable for development with human-readable console output and debug-level logging

func NewLogger

func NewLogger() *Logger

NewLogger creates a new logger with example configuration (for testing only)

func NewLoggerWithConfig

func NewLoggerWithConfig(config Config) (*Logger, error)

NewLoggerWithConfig creates a new logger with custom configuration

func NewLoggerWithZap

func NewLoggerWithZap(zapLogger *zap.Logger) *Logger

NewLoggerWithZap creates a logger from an existing zap.Logger Note: If you need caller skip, pass a logger with AddCallerSkip already configured

func NewProductionLogger

func NewProductionLogger() (*Logger, error)

NewProductionLogger creates a logger suitable for production with JSON output and info-level logging

func (*Logger) Debug

func (l *Logger) Debug(format string, args ...gsr.LoggerField)

Debug logs a message at DebugLevel

func (*Logger) Error

func (l *Logger) Error(format string, args ...gsr.LoggerField)

Error logs a message at ErrorLevel

func (*Logger) Fatal

func (l *Logger) Fatal(format string, args ...gsr.LoggerField)

Fatal logs a message at FatalLevel and then calls os.Exit(1)

func (*Logger) GetZapLogger

func (l *Logger) GetZapLogger() *zap.Logger

GetZapLogger returns the underlying zap.Logger This is useful when you need direct access to zap features

func (*Logger) Info

func (l *Logger) Info(format string, args ...gsr.LoggerField)

Info logs a message at InfoLevel

func (*Logger) Notice

func (l *Logger) Notice(format string, args ...gsr.LoggerField)

Notice logs a message at InfoLevel (alias for Info) Notice level is mapped to Info as zap doesn't have a separate Notice level

func (*Logger) Panic

func (l *Logger) Panic(format string, args ...gsr.LoggerField)

Panic logs a message at PanicLevel and then panics

func (*Logger) Sync

func (l *Logger) Sync() error

Sync flushes any buffered log entries Applications should call Sync before exiting Note: Sync errors on stdout/stderr are ignored as they cannot be synced on some systems

func (*Logger) Warn

func (l *Logger) Warn(format string, args ...gsr.LoggerField)

Warn logs a message at WarnLevel

func (*Logger) With

func (l *Logger) With(args ...gsr.LoggerField) *Logger

With creates a child logger with additional fields

func (*Logger) WithZapFields

func (l *Logger) WithZapFields(fields ...zap.Field) *Logger

WithZapFields creates a child logger with additional zap fields

type LoggerField

type LoggerField struct {
	Key   string
	Value any
}

LoggerField represents a key-value pair for structured logging

func (*LoggerField) GetKey

func (f *LoggerField) GetKey() string

GetKey returns the field's key

func (*LoggerField) GetValue

func (f *LoggerField) GetValue() any

GetValue returns the field's value

Directories

Path Synopsis
examples
basic command
caller_test command
custom command
webserver command
wrapped_logger command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL