logos

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Dec 16, 2025 License: MIT Imports: 11 Imported by: 0

README

logos


λόγος (logos)

Meaning "word," "speech," "reason," or "account"

Logos is a lightweight, flexible logging library for Go. It focuses on simplicity, clarity, and customizability—especially when it comes to log levels, output formatting, and structured logging.

Getting Started

Basic Usage
import (
    "os"
    "github.com/goodblaster/logos"
)

func main() {
    // Create a logger
    log := logos.NewLogger(logos.LevelDebug, logos.ConsoleFormatter(), os.Stdout)
    log.Debug("Starting app...")
    log.Info("Application initialized")
    log.With("port", 8080).Info("Server starting")
}
Using the Global Logger

The package provides a default global logger that can be used without creating an instance:

// Use the default logger directly
logos.Info("This uses the default logger")
logos.With("key", "value").Debug("Debug message with field")

// Or customize the default logger
logos.SetLevel(logos.LevelInfo)
logos.SetDefaultLogger(customLogger)

Environment Variables

Logos respects environment variables for easy configuration:

  • LOG_LEVEL: Set the default log level (debug, info, warn, error, fatal)
  • LOG_FORMAT: Set the default format (console, text, json)
LOG_LEVEL=info LOG_FORMAT=json ./myapp

Features

  • Easily adjustable log levels with filtering
  • Structured field and error logging
  • Multiple built-in formatters (Text, JSON, Console)
  • Global and per-instance logging
  • Context-aware logging for request-scoped loggers
  • Tee logging (write to multiple destinations)
  • Custom log levels with names and colors
  • Lazy evaluation and conditional logging
  • Error handlers for write failures
  • Thread-safe for concurrent use
  • Immutable logger pattern (copy-on-write)

Custom Log Levels

Unlike many logging packages, Logos allows you to rename or define your own log levels easily:

const (
    LevelApple logos.Level = iota
    LevelBanana
    LevelCherry
)

// Register custom level names (thread-safe)
logos.SetLevelName(LevelApple, "apple")
logos.SetLevelName(LevelBanana, "banana")
logos.SetLevelName(LevelCherry, "cherry")

// Register custom colors for console formatter
logos.SetLevelColor(LevelApple, logos.ColorTextGreen)
logos.SetLevelColor(LevelBanana, logos.ColorTextYellow)
logos.SetLevelColor(LevelCherry, logos.ColorTextRed)

log := logos.NewLogger(LevelApple, logos.ConsoleFormatter(), os.Stdout)
log.Log(LevelApple, "apple log")
log.Log(LevelBanana, "banana log")
log.Log(LevelCherry, "cherry log")

Adding Fields and Errors

log.With("user_id", 42).Info("User logged in")
log.WithFields(map[string]any{"path": "/login", "method": "POST"}).Info("Handling request")
log.WithError(err).Error("Something went wrong")

// Fields can be chained
log.With("user_id", 42).With("session_id", "abc123").Info("User action")

Formatters

You can choose how logs are rendered:

  • FormatConsole — colorized terminal output
  • FormatText — plain, human-readable text
  • FormatJSON — structured JSON for machines

Conditional and Lazy Logging

// LogFunc: evaluates function only if level is enabled
log.LogFunc(logos.LevelDebug, func() string {
    return expensiveComputation()
})

// LogIf: executes function only if level is enabled
log.LogIf(logos.LevelInfo, func() {
    fmt.Println("This block runs only if info level is enabled")
})

// IsLevelEnabled: check if a level is enabled before expensive operations
if log.IsLevelEnabled(logos.LevelDebug) {
    // Do expensive debug formatting
    log.Debugf("Complex data: %+v", generateComplexDebugInfo())
}

Changing Log Levels

Loggers are immutable, so changing the level returns a new logger:

log := logos.NewLogger(logos.LevelDebug, logos.ConsoleFormatter(), os.Stdout)
log.Debug("This will show")

// Create new logger with different level
log = log.WithLevel(logos.LevelError)
log.Debug("This won't show")
log.Error("This will show")

Error Handling

Handle errors that occur during logging with WithError and WithErrorHandler:

// Attach errors to log entries
err := errors.New("database timeout")
log.WithError(err).Error("Failed to connect")

// Handle write errors with an error handler
log = log.WithErrorHandler(func(writeErr error) {
    fmt.Fprintf(os.Stderr, "Log write failed: %v\n", writeErr)
})

Context Logging

Logos supports storing and retrieving loggers from Go's context.Context, making it easy to pass request-scoped loggers through your application:

// Create a logger with request-specific fields
requestLogger := logos.NewLogger(logos.LevelInfo, logos.ConsoleFormatter(), os.Stdout).
    With("request_id", "req-123").
    With("user_id", "user-456")

// Store logger in context
ctx := logos.WithLogger(context.Background(), requestLogger)

// In any function, retrieve the logger from context
func handleRequest(ctx context.Context) {
    logger := logos.FromContext(ctx)
    logger.Info("Processing request")
    logger.With("action", "validate").Info("Validating input")
}

// If no logger is in context, FromContext returns the DefaultLogger
logger := logos.FromContext(context.Background())
logger.Info("Using default logger")

Tee Logging

Write logs to multiple destinations simultaneously, each with its own level, formatter, and fields. This is the recommended approach for flexible multi-destination logging:

// Create separate loggers for different destinations
debugFile, _ := os.Create("debug.log")
infoFile, _ := os.Create("info.log")

debugLogger := logos.NewLogger(logos.LevelDebug, logos.JSONFormatter(), debugFile)
infoLogger := logos.NewLogger(logos.LevelInfo, logos.ConsoleFormatter(), infoFile)

// Tee the loggers together
mainLogger := logos.NewLogger(logos.LevelInfo, logos.ConsoleFormatter(), os.Stdout)
teeLogger := mainLogger.Tee(debugLogger, infoLogger)

// Info message: goes to mainLogger (Info), infoLogger (Info), and debugLogger (Debug accepts Info)
teeLogger.Info("This goes to all three")

// Debug message: only goes to debugLogger (mainLogger and infoLogger filter it)
teeLogger.Debug("This only goes to debug.log")

// Each logger can have different formatters
jsonLogger := logos.NewLogger(logos.LevelDebug, logos.JSONFormatter(), jsonFile)
consoleLogger := logos.NewLogger(logos.LevelInfo, logos.ConsoleFormatter(), os.Stderr)
teeLogger = mainLogger.Tee(jsonLogger, consoleLogger)
Different Levels Per Destination

Each tee logger can have its own level, allowing you to capture more detailed logs in some destinations:

// Main logger at Info, but debug file captures Debug level
mainLogger := logos.NewLogger(logos.LevelInfo, logos.ConsoleFormatter(), os.Stdout)
debugLogger := logos.NewLogger(logos.LevelDebug, logos.JSONFormatter(), debugFile)

teeLogger := mainLogger.Tee(debugLogger)
teeLogger.Info("Info message")  // Goes to both
teeLogger.Debug("Debug message") // Only goes to debugLogger
Different Formatters Per Destination

Each logger can format independently:

// Console gets colorized output, file gets JSON
consoleLogger := logos.NewLogger(logos.LevelInfo, logos.ConsoleFormatter(), os.Stdout)
fileLogger := logos.NewLogger(logos.LevelInfo, logos.JSONFormatter(), logFile)

teeLogger := consoleLogger.Tee(fileLogger)
teeLogger.Info("Same message, different formats")
Package-Level Tee Logging
debugLogger := logos.NewLogger(logos.LevelDebug, logos.JSONFormatter(), debugFile)
teeLogger := logos.Tee(debugLogger)
teeLogger.Info("Message goes to DefaultLogger plus debugLogger")

Examples and Demos

The demos/ directory contains comprehensive examples of all features:

  • master - Start here! Shows the most commonly used features
  • Individual demos for each feature (basic logging, fields, levels, formatters, context, tee, errors, etc.)
  • comprehensive_demo - Advanced examples including custom levels and formatters

Run any demo with:

cd demos/master
go run main.go

See demos/README.md for the full list.

Default Log Levels

  • LevelDebug
  • LevelInfo
  • LevelWarn
  • LevelError
  • LevelFatal
  • LevelPrint

These can be extended or overridden to suit your needs.


Documentation

Index

Constants

View Source
const CtxKeyLogger contextKey = "logos.logger"

CtxKeyLogger is the key used to store a Logger in context.Context.

View Source
const DefaultTimestampFormat = "2006-01-02T15:04:05"

DefaultTimestampFormat defines the layout used for default timestamps.

Variables

View Source
var DefaultConfig = Config{
	Timestamp: DefaultTimestamp,
}

DefaultConfig is the fallback configuration using the DefaultTimestamp function.

View Source
var DefaultLevels = map[string]Level{
	"debug": LevelDebug,
	"info":  LevelInfo,
	"warn":  LevelWarn,
	"error": LevelError,
	"fatal": LevelFatal,
	"print": LevelPrint,
}

DefaultLevels provides a default mapping for logging level strings to their corresponding Level values.

View Source
var FormatNames = map[Format]string{
	FormatJSON:    "JSON",
	FormatText:    "TEXT",
	FormatConsole: "CONSOLE",
}

FormatNames maps Format values to their string identifiers.

Formats is the list of all supported output formats.

View Source
var LevelColors map[Level]Color

LevelColors maps Level values to ANSI color codes for console output. This can be customized. Access is protected by levelMu for thread safety.

View Source
var LevelNames map[Level]string

LevelNames maps Level values to human-readable strings. This can be overridden by the user. Access is protected by levelMu for thread safety.

Functions

func Debug

func Debug(s ...any)

Debug logs a message at the debug level using the default logger.

func Debugf

func Debugf(format string, args ...any)

Debugf logs a formatted message at the debug level using the default logger.

func DefaultTimestamp

func DefaultTimestamp() string

DefaultTimestamp returns the current local time formatted using DefaultTimestampFormat.

func Error

func Error(a ...any)

Error logs a message at the error level using the default logger.

func Errorf

func Errorf(format string, args ...any)

Errorf logs a formatted message at the error level using the default logger.

func Fatal

func Fatal(a ...any)

Fatal logs a message at the fatal level using the default logger and panics.

func Fatalf

func Fatalf(format string, args ...any)

Fatalf logs a formatted message at the fatal level using the default logger and panics.

func GetLevelName added in v0.2.0

func GetLevelName(level Level, cfg *Config) string

GetLevelName returns the name for a level, using the Config's LevelNames if present, otherwise falling back to the global LevelNames map (with thread-safe access).

func Info

func Info(a ...any)

Info logs a message at the info level using the default logger.

func Infof

func Infof(format string, args ...any)

Infof logs a formatted message at the info level using the default logger.

func IsLevelEnabled added in v0.2.0

func IsLevelEnabled(level Level) bool

IsLevelEnabled returns true if the default logger would log at the given level.

func Log

func Log(level Level, a ...any)

Log logs a message at the specified level using the default logger.

func LogFunc

func LogFunc(level Level, msg func() string)

LogFunc logs a lazily-evaluated message using the default logger if the level is enabled.

func LogIf

func LogIf(level Level, log func())

LogIf executes a function if the level is enabled using the default logger.

func Logf

func Logf(level Level, format string, args ...any)

Logf logs a formatted message at the specified level using the default logger.

func Print

func Print(a ...any)

Print logs a message at the print level using the default logger.

func Printf

func Printf(format string, args ...any)

Printf logs a formatted message at the print level using the default logger.

func SetDefaultLogger

func SetDefaultLogger(logger Logger)

SetDefaultLogger overrides the global default logger with a new one. This function is thread-safe.

func SetLevel

func SetLevel(level Level)

SetLevel sets the logging level of the default logger.

func SetLevelColor added in v0.2.0

func SetLevelColor(level Level, color Color)

SetLevelColor sets a custom color for a level in the global LevelColors map. This function is thread-safe.

func SetLevelName added in v0.2.0

func SetLevelName(level Level, name string)

SetLevelName sets a custom name for a level in the global LevelNames map. This function is thread-safe.

func Warn

func Warn(a ...any)

Warn logs a message at the warn level using the default logger.

func Warnf

func Warnf(format string, args ...any)

Warnf logs a formatted message at the warn level using the default logger.

func WithLogger added in v0.2.0

func WithLogger(ctx context.Context, logger Logger) context.Context

WithLogger returns a new context with the provided logger stored in it. The logger can later be retrieved using FromContext.

Types

type Color

type Color string

Color defines an ANSI escape sequence for terminal text and background coloring.

const (
	// ColorReset resets the terminal color to default.
	ColorReset Color = "\033[0m"

	// Foreground text colors.
	ColorTextRed     Color = "\033[31m"
	ColorTextYellow  Color = "\033[33m"
	ColorTextGreen   Color = "\033[32m"
	ColorTextBlue    Color = "\033[34m"
	ColorTextMagenta Color = "\033[35m"
	ColorTextCyan    Color = "\033[36m"
	ColorTextWhite   Color = "\033[37m"
	ColorTextPurple  Color = "\033[35m" // Alias for magenta
	ColorTextBlack   Color = "\033[30m"

	// Background colors.
	ColorBgRed     Color = "\033[41m"
	ColorBgYellow  Color = "\033[43m"
	ColorBgGreen   Color = "\033[42m"
	ColorBgBlue    Color = "\033[44m"
	ColorBgMagenta Color = "\033[45m"
	ColorBgCyan    Color = "\033[46m"
	ColorBgWhite   Color = "\033[47m"
	ColorBgBlack   Color = "\033[40m"
)

func GetLevelColor added in v0.2.0

func GetLevelColor(level Level, cfg *Config) Color

GetLevelColor returns the color for a level, using the Config's LevelColors if present, otherwise falling back to the global LevelColors map (with thread-safe access).

type Config

type Config struct {
	Timestamp   func() string
	LevelNames  map[Level]string // Optional: custom level names. Falls back to global LevelNames if nil.
	LevelColors map[Level]Color  // Optional: custom level colors. Falls back to global LevelColors if nil.
}

Config defines the configuration for a Formatter, such as a custom timestamp function, level names, and colors. If LevelNames or LevelColors are nil, the global defaults will be used.

type Entry

type Entry struct {
	Fields Fields
	Msg    string
	Error  error
}

Entry holds the log data including fields, message, and error.

type Fields

type Fields = map[string]any

Fields represents a key-value pair used to annotate log entries.

type Format

type Format int

Format represents the output format used by the logger.

const (
	// FormatJSON outputs logs in structured JSON format.
	FormatJSON Format = iota
	// FormatText outputs logs as plain, uncolored text.
	FormatText
	// FormatConsole outputs logs as colored text suitable for terminals.
	FormatConsole
)

func (Format) String

func (f Format) String() string

String returns the string representation of a Format.

type Formatter

type Formatter interface {
	Format(level Level, entry Entry) string
}

Formatter is the interface implemented by all formatters in the package. It defines how a log entry is rendered as a string.

func ConsoleFormatter

func ConsoleFormatter() Formatter

ConsoleFormatter returns a new colorized console formatter with the default configuration.

func JSONFormatter

func JSONFormatter() Formatter

JSONFormatter returns a new JSON formatter with the default configuration.

func NewConsoleFormatter

func NewConsoleFormatter(cfg Config) Formatter

NewConsoleFormatter creates a new consoleFormatter using the provided configuration.

func NewFormatter

func NewFormatter(format Format) Formatter

NewFormatter returns a new Formatter based on the provided Format and the default configuration.

func NewFormatterWithConfig

func NewFormatterWithConfig(format Format, cfg Config) Formatter

NewFormatterWithConfig returns a new Formatter based on the provided Format and Config.

func NewJsonFormatter

func NewJsonFormatter(cfg Config) Formatter

NewJsonFormatter creates a new jsonFormatter using the provided configuration.

func NewTextFormatter

func NewTextFormatter(cfg Config) Formatter

NewTextFormatter creates a new textFormatter using the provided configuration.

func TextFormatter

func TextFormatter() Formatter

TextFormatter returns a new plain text formatter with the default configuration.

type Level

type Level int

Level represents the severity of a log message.

const (
	// LevelDebug represents fine-grained debug information.
	LevelDebug Level = iota - 1
	// LevelInfo represents general operational entries about what's going on inside the application.
	LevelInfo
	// LevelWarn represents potentially harmful situations.
	LevelWarn
	// LevelError represents error events that might still allow the application to continue running.
	LevelError
	// LevelFatal represents very severe error events that will presumably lead the application to abort.
	LevelFatal
	// LevelPrint is used for messages that should always be printed regardless of level filtering.
	LevelPrint = math.MaxInt
)

func (Level) String

func (level Level) String() string

String returns the string representation of a logging level.

type Logger

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

Logger is the primary struct for logging messages with optional fields and errors.

func FromContext added in v0.2.0

func FromContext(ctx context.Context) Logger

FromContext retrieves a Logger from the context. If a logger is found in the context, it returns that logger. Otherwise, it returns the default logger.

func NewLogger

func NewLogger(level Level, formatter Formatter, writer io.Writer) Logger

NewLogger creates a new Logger instance with the given level, formatter, and output writer.

func Tee added in v0.2.0

func Tee(loggers ...Logger) Logger

Tee adds one or more loggers as tee destinations to the default logger.

func With

func With(key string, value any) Logger

With returns a copy of the default logger with an additional field.

func WithError

func WithError(err error) Logger

WithError returns a copy of the default logger with an associated error.

func WithFields

func WithFields(fields map[string]any) Logger

WithFields returns a copy of the default logger with additional fields.

func (Logger) Copy

func (logger Logger) Copy() Logger

Copy creates a deep copy of the logger with an independent level. The copied logger shares the same mutex and writer as the original (for thread-safety), but has independent level, fields, and error state.

func (Logger) Debug

func (logger Logger) Debug(a ...any)

Debug logs a message at the debug level.

func (Logger) Debugf

func (logger Logger) Debugf(format string, args ...any)

Debugf logs a formatted message at the debug level.

func (Logger) Error

func (logger Logger) Error(a ...any)

Error logs a message at the error level.

func (Logger) Errorf

func (logger Logger) Errorf(format string, args ...any)

Errorf logs a formatted message at the error level.

func (Logger) Fatal

func (logger Logger) Fatal(a ...any)

Fatal logs a message at the fatal level and then panics.

func (Logger) Fatalf

func (logger Logger) Fatalf(format string, args ...any)

Fatalf logs a formatted message at the fatal level and then panics.

func (Logger) GetError added in v0.2.0

func (logger Logger) GetError() error

GetError returns the logger's associated error.

func (Logger) GetFields added in v0.2.0

func (logger Logger) GetFields() Fields

GetFields returns a copy of the logger's fields. The returned map is independent and modifications won't affect the logger.

func (Logger) GetLevel added in v0.2.0

func (logger Logger) GetLevel() Level

GetLevel returns the current logging level.

func (Logger) GetTeeCount added in v0.2.0

func (logger Logger) GetTeeCount() int

GetTeeCount returns the number of tee loggers attached to this logger.

func (Logger) Info

func (logger Logger) Info(a ...any)

Info logs a message at the info level.

func (Logger) Infof

func (logger Logger) Infof(format string, args ...any)

Infof logs a formatted message at the info level.

func (Logger) IsLevelEnabled added in v0.2.0

func (logger Logger) IsLevelEnabled(level Level) bool

IsLevelEnabled returns true if the logger would log at the given level. This is useful for avoiding expensive computations when the log level is not enabled.

func (Logger) Log

func (logger Logger) Log(level Level, a ...any)

Log logs a message at the specified level.

func (Logger) LogFunc

func (logger Logger) LogFunc(level Level, msg func() string)

LogFunc evaluates the message-producing function only if at least one logger (main or tee) has the level enabled.

func (Logger) LogIf

func (logger Logger) LogIf(level Level, log func())

LogIf calls the provided function if at least one logger (main or tee) has the level enabled.

func (Logger) Logf

func (logger Logger) Logf(level Level, format string, args ...any)

Logf logs a formatted message at the specified level.

func (Logger) Print

func (logger Logger) Print(a ...any)

Print logs a message at the print level.

func (Logger) Printf

func (logger Logger) Printf(format string, args ...any)

Printf logs a formatted message at the print level.

func (Logger) SetLevel

func (logger Logger) SetLevel(level Level)

SetLevel sets the logging level of the logger. Deprecated: Use WithLevel() instead for immutable logger creation.

func (Logger) Tee added in v0.2.0

func (logger Logger) Tee(loggers ...Logger) Logger

Tee adds one or more loggers as tee destinations. Each tee logger will receive all log messages and handle its own level checking and formatting. This allows different destinations to have different levels, formatters, and fields.

func (Logger) Warn

func (logger Logger) Warn(a ...any)

Warn logs a message at the warn level.

func (Logger) Warnf

func (logger Logger) Warnf(format string, args ...any)

Warnf logs a formatted message at the warn level.

func (Logger) With

func (logger Logger) With(key string, value any) Logger

With returns a new Logger with an added single key-value field.

func (Logger) WithError

func (logger Logger) WithError(err error) Logger

WithError returns a new Logger with an associated error. If the logger already has an error, it will be silently replaced.

func (Logger) WithErrorHandler added in v0.2.0

func (logger Logger) WithErrorHandler(handler func(error)) Logger

WithErrorHandler returns a new Logger with the specified error handler. The error handler is called whenever a write error occurs during logging. If handler is nil, write errors will be silently ignored.

func (Logger) WithFields

func (logger Logger) WithFields(fields Fields) Logger

WithFields returns a new Logger with additional key-value pairs.

func (Logger) WithLevel added in v0.2.0

func (logger Logger) WithLevel(level Level) Logger

WithLevel returns a new Logger with the specified logging level. Unlike SetLevel(), this method returns a new logger instance with an independent level, maintaining immutability and preventing shared state issues.

Directories

Path Synopsis
demos
01_basic command
02_fields command
03_levels command
04_formatters command
05_context command
06_tee command
07_errors command
master command

Jump to

Keyboard shortcuts

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