arbor

package module
v1.4.44 Latest Latest
Warning

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

Go to latest
Published: Sep 22, 2025 License: MIT Imports: 13 Imported by: 2

README

arbor

CI/CD Pipeline Go Reference Go Report Card

A comprehensive Go logging system designed for APIs with structured logging, multiple output writers, and advanced correlation tracking.

Installation

go get github.com/ternarybob/arbor@latest

Quick Start

package main

import (
    "github.com/ternarybob/arbor"
    "github.com/ternarybob/arbor/models"
)

func main() {
    // Create logger with console output
    logger := arbor.Logger().
        WithConsoleWriter(models.WriterConfiguration{
            Type:       models.LogWriterTypeConsole,
            TimeFormat: "15:04:05.000",
        }).
        WithCorrelationId("app-startup")

    // Log messages
    logger.Info().Str("version", "1.0.0").Msg("Application started")
    logger.Warn().Msg("This is a warning")
    logger.Error().Str("error", "connection failed").Msg("Database connection error")
}

Features

  • Multi-Writer Architecture: Console, File, and Memory (BoltDB) writers
  • Correlation ID Tracking: Request tracing across application layers
  • Structured Logging: Rich field support with fluent API
  • Log Level Management: String-based and programmatic level configuration
  • Memory Logging: Persistent storage with TTL and automatic cleanup
  • API Integration: Built-in Gin framework support
  • Global Registry: Cross-context logger access
  • Thread-Safe: Concurrent access with proper synchronization
  • Performance Focused: Optimized for high-throughput API scenarios

Basic Usage

Simple Console Logging
import "github.com/ternarybob/arbor"

// Use global logger
arbor.Info().Msg("Simple log message")
arbor.Error().Err(err).Msg("Error occurred")

// With structured fields
arbor.Info().
    Str("user", "john.doe").
    Int("attempts", 3).
    Msg("User login attempt")
Multiple Writers Configuration
logger := arbor.Logger().
    WithConsoleWriter(models.WriterConfiguration{
        Type:       models.LogWriterTypeConsole,
        TimeFormat: "15:04:05.000",
    }).
    WithFileWriter(models.WriterConfiguration{
        Type:       models.LogWriterTypeFile,
        FileName:   "logs/app.log",
        MaxSize:    10 * 1024 * 1024, // 10MB
        MaxBackups: 5,
        TimeFormat: "2006-01-02 15:04:05.000",
        TextOutput: true, // Enable human-readable text format (default: false for JSON)
    }).
    WithMemoryWriter(models.WriterConfiguration{
        Type:       models.LogWriterTypeMemory,
        TimeFormat: "15:04:05.000",
    })

logger.Info().Msg("This goes to console, file, and memory")

File Writer Configuration

The file writer supports both JSON and human-readable text output formats.

JSON Output Format (Default)
logger := arbor.Logger().
    WithFileWriter(models.WriterConfiguration{
        Type:       models.LogWriterTypeFile,
        FileName:   "logs/app.log",
        TimeFormat: "2006-01-02 15:04:05.000",
        TextOutput: false, // JSON format (default)
    })

logger.Info().Str("user", "john").Msg("User logged in")

Output:

{"time":"2025-09-18 15:04:05.123","level":"info","user":"john","message":"User logged in"}
Text Output Format
logger := arbor.Logger().
    WithFileWriter(models.WriterConfiguration{
        Type:       models.LogWriterTypeFile,
        FileName:   "logs/app.log",
        TimeFormat: "15:04:05.000",
        TextOutput: true, // Human-readable text format
    })

logger.Info().Str("user", "john").Msg("User logged in")

Output:

15:04:05.123 INF > User logged in user=john
File Writer Options
  • FileName: Log file path (default: "logs/main.log")
  • MaxSize: Maximum file size in bytes before rotation (default: 10MB)
  • MaxBackups: Number of backup files to keep (default: 5)
  • TextOutput: Enable human-readable format instead of JSON (default: false)
  • TimeFormat: Timestamp format for log entries
  • Level: Minimum log level to write

Log Levels

String-Based Configuration
// Configure from external config
logger := arbor.Logger().WithLevelFromString("debug")

// Supported levels: "trace", "debug", "info", "warn", "error", "fatal", "panic", "disabled"
Programmatic Configuration
logger := arbor.Logger().WithLevel(arbor.DebugLevel)

// Available levels: TraceLevel, DebugLevel, InfoLevel, WarnLevel, ErrorLevel, FatalLevel, PanicLevel

Correlation ID Tracking

Correlation IDs enable request tracing across your application layers:

// Set correlation ID for request tracking
logger := arbor.Logger().
    WithConsoleWriter(config).
    WithCorrelationId("req-12345")

logger.Info().Msg("Processing request")
logger.Debug().Str("step", "validation").Msg("Validating input")
logger.Info().Str("result", "success").Msg("Request completed")

// Auto-generate correlation ID
logger.WithCorrelationId("") // Generates UUID automatically

// Clear correlation ID
logger.ClearCorrelationId()

Memory Logging & Retrieval

Store logs in BoltDB for later retrieval and debugging:

// Configure memory writer
logger := arbor.Logger().
    WithMemoryWriter(models.WriterConfiguration{
        Type:       models.LogWriterTypeMemory,
        TimeFormat: "15:04:05.000",
    }).
    WithCorrelationId("debug-session")

// Log some messages
logger.Info().Msg("Starting process")
logger.Debug().Str("step", "initialization").Msg("Initializing components")
logger.Error().Str("error", "timeout").Msg("Operation failed")

// Retrieve logs by correlation ID
logs, err := logger.GetMemoryLogs("debug-session", arbor.DebugLevel)
if err != nil {
    log.Fatal(err)
}

// Display retrieved logs
for index, message := range logs {
    fmt.Printf("[%s]: %s\n", index, message)
}
Memory Log Retrieval Options
// Get all logs for correlation ID
logs, _ := logger.GetMemoryLogsForCorrelation("correlation-id")

// Get logs with minimum level filter
logs, _ := logger.GetMemoryLogs("correlation-id", arbor.WarnLevel)

// Get most recent N entries
logs, _ := logger.GetMemoryLogsWithLimit(100)

API Integration

Gin Framework Integration
import (
    "github.com/gin-gonic/gin"
    "github.com/ternarybob/arbor"
    "github.com/ternarybob/arbor/models"
)

func main() {
    // Configure logger with memory writer for log retrieval
    logger := arbor.Logger().
        WithConsoleWriter(models.WriterConfiguration{
            Type:       models.LogWriterTypeConsole,
            TimeFormat: "15:04:05.000",
        }).
        WithMemoryWriter(models.WriterConfiguration{
            Type:       models.LogWriterTypeMemory,
            TimeFormat: "15:04:05.000",
        })

    // Create Gin engine with arbor integration
    r := gin.New()
    
    // Use arbor writer for Gin logs
    ginWriter := logger.GinWriter(models.WriterConfiguration{
        Type:       models.LogWriterTypeConsole,
        TimeFormat: "15:04:05.000",
    })
    
    r.Use(gin.LoggerWithWriter(ginWriter.(io.Writer)))
    
    // Your routes here
    r.GET("/health", func(c *gin.Context) {
        correlationID := c.GetHeader("X-Correlation-ID")
        requestLogger := logger.WithCorrelationId(correlationID)
        
        requestLogger.Info().Str("endpoint", "/health").Msg("Health check requested")
        c.JSON(200, gin.H{"status": "ok"})
    })
    
    r.Run(":8080")
}
Request Correlation Middleware
func CorrelationMiddleware(logger arbor.ILogger) gin.HandlerFunc {
    return gin.HandlerFunc(func(c *gin.Context) {
        // Extract or generate correlation ID
        correlationID := c.GetHeader("X-Correlation-ID")
        if correlationID == "" {
            correlationID = generateUUID() // Your UUID generation
        }
        
        // Create request-scoped logger
        requestLogger := logger.WithCorrelationId(correlationID)
        
        // Store in context for handler access
        c.Set("logger", requestLogger)
        c.Header("X-Correlation-ID", correlationID)
        
        requestLogger.Info().
            Str("method", c.Request.Method).
            Str("path", c.Request.URL.Path).
            Msg("Request started")
        
        c.Next()
        
        requestLogger.Info().
            Int("status", c.Writer.Status()).
            Msg("Request completed")
    })
}

Global Registry Pattern

Access loggers across different application contexts:

// Register logger during application startup
func initLogging() {
    mainLogger := arbor.Logger().
        WithConsoleWriter(consoleConfig).
        WithMemoryWriter(memoryConfig).
        WithCorrelationId("main-app")
    
    // Register for global access
    arbor.RegisterLogger("main", mainLogger)
    arbor.RegisterLogger("api", apiLogger)
}

// Access from any context (e.g., service layer)
func ProcessRequest(correlationID string) error {
    logger := arbor.GetRegisteredLogger("main")
    if logger == nil {
        logger = arbor.GetRegisteredLogger("default") // fallback
    }
    
    requestLogger := logger.WithCorrelationId(correlationID)
    requestLogger.Info().Msg("Processing business logic")
    
    return nil
}

// Retrieve logs in API handlers
func GetLogsHandler(c *gin.Context) {
    correlationID := c.Query("correlation_id")
    
    logger := arbor.GetRegisteredLogger("main")
    logs, err := logger.GetMemoryLogs(correlationID, arbor.DebugLevel)
    if err != nil {
        c.JSON(500, gin.H{"error": err.Error()})
        return
    }
    
    c.JSON(200, gin.H{"logs": logs})
}

Advanced Features

Context Management
// Add structured context
logger := arbor.Logger().
    WithContext("service", "user-management").
    WithContext("version", "1.2.0").
    WithPrefix("UserSvc")

// Copy logger with fresh context
cleanLogger := logger.Copy() // Same writers, no context data
Broadcast Logging
// Register multiple loggers
arbor.RegisterLogger("console", consoleLogger)
arbor.RegisterLogger("file", fileLogger) 
arbor.RegisterLogger("memory", memoryLogger)

// Broadcast to all registered loggers
arbor.BroadcastInfo().Msg("Application started")
arbor.BroadcastError().Err(err).Msg("Critical system error")

Configuration Examples

From Environment Variables
logLevel := os.Getenv("LOG_LEVEL")
if logLevel == "" {
    logLevel = "info"
}

logger := arbor.Logger().
    WithConsoleWriter(models.WriterConfiguration{
        Type:       models.LogWriterTypeConsole,
        TimeFormat: "15:04:05.000",
    }).
    WithLevelFromString(logLevel)
Configuration Struct
type LogConfig struct {
    Level      string `json:"level"`
    Console    bool   `json:"console"`
    File       string `json:"file"`
    Memory     bool   `json:"memory"`
    TimeFormat string `json:"time_format"`
    TextOutput bool   `json:"text_output"`
}

func ConfigureLogger(config LogConfig) arbor.ILogger {
    logger := arbor.NewLogger()

    if config.Console {
        logger.WithConsoleWriter(models.WriterConfiguration{
            Type:       models.LogWriterTypeConsole,
            TimeFormat: config.TimeFormat,
        })
    }

    if config.File != "" {
        logger.WithFileWriter(models.WriterConfiguration{
            Type:       models.LogWriterTypeFile,
            FileName:   config.File,
            TimeFormat: config.TimeFormat,
            TextOutput: config.TextOutput, // Enable text format for files
        })
    }

    if config.Memory {
        logger.WithMemoryWriter(models.WriterConfiguration{
            Type:       models.LogWriterTypeMemory,
            TimeFormat: config.TimeFormat,
        })
    }

    return logger.WithLevelFromString(config.Level)
}

Performance Considerations

  • Memory Writer: Uses BoltDB for persistence with configurable TTL
  • Cleanup: Automatic cleanup of expired entries every minute
  • Thread Safety: All operations are thread-safe with minimal lock contention
  • Buffer Limits: Memory writer maintains buffer limits per correlation ID
  • Log Levels: Level filtering occurs at writer level for efficiency

CI/CD

This project uses GitHub Actions for continuous integration and deployment:

  • Automated Testing: Runs tests on every push and pull request
  • Code Quality Checks: Enforces go fmt, go vet, and build validation
  • Auto-Release: Automatically creates releases on main branch pushes
  • Tagged Releases: Manual version control via git tags

Documentation

Full documentation is available at pkg.go.dev.

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.

This library is part of the T3B ecosystem:

  • funktion - Core utility functions
  • satus - Configuration and status management
  • arbor - Structured logging system
  • omnis - Web framework integrations

Documentation

Index

Constants

View Source
const (
	LOGGER_CONTEXT_KEY string = "logger"
	LEVEL_KEY          string = "level"
	CORRELATION_ID_KEY string = "correlationid"
	PREFIX_KEY         string = "prefix"
	GIN_LOG_KEY        string = "gin"
)
View Source
const (
	WRITER_CONSOLE = "console"
	WRITER_FILE    = "file"
	WRITER_MEMORY  = "memory"
)

Variables

This section is empty.

Functions

func GetAllRegisteredWriters added in v1.4.31

func GetAllRegisteredWriters() map[string]writers.IWriter

GetAllRegisteredWriters returns a copy of all registered writers

func GetRegisteredMemoryWriter added in v1.4.31

func GetRegisteredMemoryWriter(name string) writers.IMemoryWriter

GetRegisteredMemoryWriter retrieves a memory writer by name from the global registry Returns nil if the writer is not found or is not a memory writer

func GetRegisteredWriter added in v1.4.31

func GetRegisteredWriter(name string) writers.IWriter

GetRegisteredWriter retrieves a writer by name from the global registry Returns nil if the writer is not found

func GetRegisteredWriterNames added in v1.4.31

func GetRegisteredWriterNames() []string

GetRegisteredWriterNames returns a list of all registered writer names

func GetWriterCount added in v1.4.31

func GetWriterCount() int

GetWriterCount returns the number of registered writers

func LevelToString added in v1.4.14

func LevelToString(level log.Level) string

LevelToString converts log level to string representation (exported for writers)

func ParseLevelString added in v1.4.14

func ParseLevelString(levelStr string) (log.Level, error)

Re-export convenience functions from levels subpackage

func ParseLogLevel added in v1.4.14

func ParseLogLevel(level int) log.Level

func RegisterWriter added in v1.4.31

func RegisterWriter(name string, writer writers.IWriter)

RegisterWriter registers a writer with the given name in the global registry

func UnregisterWriter added in v1.4.31

func UnregisterWriter(name string)

UnregisterWriter removes a writer from the global registry

Types

type ILogEvent added in v1.4.14

type ILogEvent interface {
	// String slice field method
	Strs(key string, values []string) ILogEvent

	// String field methods
	Str(key, value string) ILogEvent

	// Error field method
	Err(err error) ILogEvent

	// Message methods
	Msg(message string)
	Msgf(format string, args ...interface{})

	// Integer field method
	Int(key string, value int) ILogEvent

	// Int32 field method
	Int32(key string, value int32) ILogEvent

	// Int64 field method
	Int64(key string, value int64) ILogEvent

	// Float32 field method
	Float32(key string, value float32) ILogEvent

	// Duration field method
	Dur(key string, value time.Duration) ILogEvent

	// Float64 field method
	Float64(key string, value float64) ILogEvent
}

ILogEvent represents a fluent interface for building log events

func Debug added in v1.4.14

func Debug() ILogEvent

func Error added in v1.4.14

func Error() ILogEvent

func Fatal added in v1.4.14

func Fatal() ILogEvent

func Info added in v1.4.14

func Info() ILogEvent

func Panic added in v1.4.14

func Panic() ILogEvent

func Trace added in v1.4.14

func Trace() ILogEvent

Global convenience functions for direct logging

func Warn added in v1.4.14

func Warn() ILogEvent

type ILogger added in v1.4.14

type ILogger interface {
	WithConsoleWriter(config models.WriterConfiguration) ILogger

	WithFileWriter(config models.WriterConfiguration) ILogger

	WithMemoryWriter(config models.WriterConfiguration) ILogger

	WithPrefix(value string) ILogger

	WithCorrelationId(value string) ILogger

	ClearCorrelationId() ILogger

	// ClearContext removes all context data from the logger
	ClearContext() ILogger

	WithLevel(lvl LogLevel) ILogger

	// WithLevelFromString applies a log level from a string configuration
	WithLevelFromString(levelStr string) ILogger

	WithContext(key string, value string) ILogger

	// Copy creates a copy of the logger with the same configuration but clean/empty context
	// This is useful when you want a fresh logger that shares the same writers but has no correlation ID, prefix, or other context
	Copy() ILogger

	// Fluent logging methods
	Trace() ILogEvent
	Debug() ILogEvent
	Info() ILogEvent
	Warn() ILogEvent
	Error() ILogEvent
	Fatal() ILogEvent
	Panic() ILogEvent

	GetMemoryLogs(correlationid string, minLevel LogLevel) (map[string]string, error)

	// GetMemoryLogsForCorrelation retrieves all log entries for a specific correlation ID
	GetMemoryLogsForCorrelation(correlationid string) (map[string]string, error)

	// GetMemoryLogsWithLimit retrieves the most recent log entries up to the specified limit
	GetMemoryLogsWithLimit(limit int) (map[string]string, error)

	// GinWriter returns an io.Writer that integrates Gin logs with arbor's registered writers
	GinWriter(config models.WriterConfiguration) interface{}

	// GetLogFilePath returns the configured log file path if a file writer is registered
	GetLogFilePath() string
}

func GetLogger added in v1.4.14

func GetLogger() ILogger

GetLogger returns the default logger instance from the registry

func Logger added in v1.4.14

func Logger() ILogger

Logger returns the default logger instance, creating it if it doesn't exist

func NewLogger added in v1.4.31

func NewLogger() ILogger

NewLogger creates a new logger instance This is useful for testing or when you need isolated logger instances

type IWriterRegistry added in v1.4.31

type IWriterRegistry interface {
	// RegisterWriter registers a writer with the given name in the registry
	RegisterWriter(name string, writer writers.IWriter)

	// GetRegisteredWriter retrieves a writer by name from the registry
	// Returns nil if the writer is not found
	GetRegisteredWriter(name string) writers.IWriter

	// GetRegisteredMemoryWriter retrieves a memory writer by name from the registry
	// Returns nil if the writer is not found or is not a memory writer
	GetRegisteredMemoryWriter(name string) writers.IMemoryWriter

	// GetRegisteredWriterNames returns a list of all registered writer names
	GetRegisteredWriterNames() []string

	// UnregisterWriter removes a writer from the registry
	UnregisterWriter(name string)

	// GetWriterCount returns the number of registered writers
	GetWriterCount() int

	// GetAllRegisteredWriters returns a copy of all registered writers
	GetAllRegisteredWriters() map[string]writers.IWriter
}

IWriterRegistry defines the interface for managing a collection of named writers with thread-safe access operations

func NewWriterRegistry added in v1.4.31

func NewWriterRegistry() IWriterRegistry

NewWriterRegistry creates a new instance of WriterRegistry

type LogLevel added in v1.4.14

type LogLevel = levels.LogLevel
const (
	TraceLevel LogLevel = levels.TraceLevel
	DebugLevel LogLevel = levels.DebugLevel
	InfoLevel  LogLevel = levels.InfoLevel
	WarnLevel  LogLevel = levels.WarnLevel
	ErrorLevel LogLevel = levels.ErrorLevel
	FatalLevel LogLevel = levels.FatalLevel
	PanicLevel LogLevel = levels.PanicLevel
	Disabled   LogLevel = levels.Disabled
)

type WriterRegistry added in v1.4.31

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

WriterRegistry manages a collection of named writers with thread-safe access and implements the IWriterRegistry interface

func (*WriterRegistry) GetAllRegisteredWriters added in v1.4.31

func (wr *WriterRegistry) GetAllRegisteredWriters() map[string]writers.IWriter

GetAllRegisteredWriters returns a copy of all registered writers

func (*WriterRegistry) GetRegisteredMemoryWriter added in v1.4.31

func (wr *WriterRegistry) GetRegisteredMemoryWriter(name string) writers.IMemoryWriter

GetRegisteredMemoryWriter retrieves a memory writer by name from the registry Returns nil if the writer is not found or is not a memory writer

func (*WriterRegistry) GetRegisteredWriter added in v1.4.31

func (wr *WriterRegistry) GetRegisteredWriter(name string) writers.IWriter

GetRegisteredWriter retrieves a writer by name from the registry Returns nil if the writer is not found

func (*WriterRegistry) GetRegisteredWriterNames added in v1.4.31

func (wr *WriterRegistry) GetRegisteredWriterNames() []string

GetRegisteredWriterNames returns a list of all registered writer names

func (*WriterRegistry) GetWriterCount added in v1.4.31

func (wr *WriterRegistry) GetWriterCount() int

GetWriterCount returns the number of registered writers

func (*WriterRegistry) RegisterWriter added in v1.4.31

func (wr *WriterRegistry) RegisterWriter(name string, writer writers.IWriter)

RegisterWriter registers a writer with the given name in the registry

func (*WriterRegistry) UnregisterWriter added in v1.4.31

func (wr *WriterRegistry) UnregisterWriter(name string)

UnregisterWriter removes a writer from the registry

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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