Mining/pkg/logging/logger.go
Claude 7d6d00a0e6
ax(logging): replace prose comment on New with usage example
AX Principle 2: comments must show concrete usage, not restate the
function name. "New creates a new Logger with the given configuration"
restated the signature — replaced with a callable example.

Co-Authored-By: Charon <charon@lethean.io>
2026-04-02 14:13:04 +01:00

285 lines
6.5 KiB
Go

// Package logging provides structured logging with log levels and fields.
package logging
import (
"fmt"
"io"
"os"
"strings"
"sync"
"time"
)
// Level represents the severity of a log message.
type Level int
const (
// LevelDebug is the most verbose log level.
LevelDebug Level = iota
// LevelInfo is for general informational messages.
LevelInfo
// LevelWarn is for warning messages.
LevelWarn
// LevelError is for error messages.
LevelError
)
// String returns the string representation of the log level.
func (l Level) String() string {
switch l {
case LevelDebug:
return "DEBUG"
case LevelInfo:
return "INFO"
case LevelWarn:
return "WARN"
case LevelError:
return "ERROR"
default:
return "UNKNOWN"
}
}
// Logger provides structured logging with configurable output and level.
type Logger struct {
mu sync.Mutex
output io.Writer
level Level
component string
}
// Config holds configuration for creating a new Logger.
type Config struct {
Output io.Writer
Level Level
Component string
}
// DefaultConfig returns the default logger configuration.
func DefaultConfig() Config {
return Config{
Output: os.Stderr,
Level: LevelInfo,
Component: "",
}
}
// New creates a Logger with the given configuration.
// logger := logging.New(logging.Config{Output: os.Stderr, Level: logging.LevelInfo, Component: "mining"})
func New(cfg Config) *Logger {
if cfg.Output == nil {
cfg.Output = os.Stderr
}
return &Logger{
output: cfg.Output,
level: cfg.Level,
component: cfg.Component,
}
}
// WithComponent returns a new Logger with the specified component name.
func (l *Logger) WithComponent(component string) *Logger {
return &Logger{
output: l.output,
level: l.level,
component: component,
}
}
// SetLevel sets the minimum log level.
func (l *Logger) SetLevel(level Level) {
l.mu.Lock()
defer l.mu.Unlock()
l.level = level
}
// GetLevel returns the current log level.
func (l *Logger) GetLevel() Level {
l.mu.Lock()
defer l.mu.Unlock()
return l.level
}
// Fields represents key-value pairs for structured logging.
type Fields map[string]interface{}
// log writes a log message at the specified level.
func (l *Logger) log(level Level, msg string, fields Fields) {
l.mu.Lock()
defer l.mu.Unlock()
if level < l.level {
return
}
// Build the log line
var builder strings.Builder
timestamp := time.Now().Format("2006/01/02 15:04:05")
builder.WriteString(timestamp)
builder.WriteString(" [")
builder.WriteString(level.String())
builder.WriteString("]")
if l.component != "" {
builder.WriteString(" [")
builder.WriteString(l.component)
builder.WriteString("]")
}
builder.WriteString(" ")
builder.WriteString(msg)
// Add fields if present
if len(fields) > 0 {
builder.WriteString(" |")
for k, v := range fields {
builder.WriteString(" ")
builder.WriteString(k)
builder.WriteString("=")
builder.WriteString(fmt.Sprintf("%v", v))
}
}
builder.WriteString("\n")
fmt.Fprint(l.output, builder.String())
}
// Debug logs a debug message.
func (l *Logger) Debug(msg string, fields ...Fields) {
l.log(LevelDebug, msg, mergeFields(fields))
}
// Info logs an informational message.
func (l *Logger) Info(msg string, fields ...Fields) {
l.log(LevelInfo, msg, mergeFields(fields))
}
// Warn logs a warning message.
func (l *Logger) Warn(msg string, fields ...Fields) {
l.log(LevelWarn, msg, mergeFields(fields))
}
// Error logs an error message.
func (l *Logger) Error(msg string, fields ...Fields) {
l.log(LevelError, msg, mergeFields(fields))
}
// Debugf logs a formatted debug message.
func (l *Logger) Debugf(format string, args ...interface{}) {
l.log(LevelDebug, fmt.Sprintf(format, args...), nil)
}
// Infof logs a formatted informational message.
func (l *Logger) Infof(format string, args ...interface{}) {
l.log(LevelInfo, fmt.Sprintf(format, args...), nil)
}
// Warnf logs a formatted warning message.
func (l *Logger) Warnf(format string, args ...interface{}) {
l.log(LevelWarn, fmt.Sprintf(format, args...), nil)
}
// Errorf logs a formatted error message.
func (l *Logger) Errorf(format string, args ...interface{}) {
l.log(LevelError, fmt.Sprintf(format, args...), nil)
}
// mergeFields combines multiple Fields maps into one.
func mergeFields(fields []Fields) Fields {
if len(fields) == 0 {
return nil
}
result := make(Fields)
for _, f := range fields {
for k, v := range f {
result[k] = v
}
}
return result
}
// --- Global logger for convenience ---
var (
globalLogger = New(DefaultConfig())
globalMutex sync.RWMutex
)
// SetGlobal sets the global logger instance.
func SetGlobal(l *Logger) {
globalMutex.Lock()
defer globalMutex.Unlock()
globalLogger = l
}
// GetGlobal returns the global logger instance.
func GetGlobal() *Logger {
globalMutex.RLock()
defer globalMutex.RUnlock()
return globalLogger
}
// SetGlobalLevel sets the log level of the global logger.
func SetGlobalLevel(level Level) {
globalMutex.RLock()
defer globalMutex.RUnlock()
globalLogger.SetLevel(level)
}
// Global convenience functions that use the global logger
// Debug logs a debug message using the global logger.
func Debug(msg string, fields ...Fields) {
GetGlobal().Debug(msg, fields...)
}
// Info logs an informational message using the global logger.
func Info(msg string, fields ...Fields) {
GetGlobal().Info(msg, fields...)
}
// Warn logs a warning message using the global logger.
func Warn(msg string, fields ...Fields) {
GetGlobal().Warn(msg, fields...)
}
// Error logs an error message using the global logger.
func Error(msg string, fields ...Fields) {
GetGlobal().Error(msg, fields...)
}
// Debugf logs a formatted debug message using the global logger.
func Debugf(format string, args ...interface{}) {
GetGlobal().Debugf(format, args...)
}
// Infof logs a formatted informational message using the global logger.
func Infof(format string, args ...interface{}) {
GetGlobal().Infof(format, args...)
}
// Warnf logs a formatted warning message using the global logger.
func Warnf(format string, args ...interface{}) {
GetGlobal().Warnf(format, args...)
}
// Errorf logs a formatted error message using the global logger.
func Errorf(format string, args ...interface{}) {
GetGlobal().Errorf(format, args...)
}
// ParseLevel parses a string into a log level.
func ParseLevel(s string) (Level, error) {
switch strings.ToUpper(s) {
case "DEBUG":
return LevelDebug, nil
case "INFO":
return LevelInfo, nil
case "WARN", "WARNING":
return LevelWarn, nil
case "ERROR":
return LevelError, nil
default:
return LevelInfo, fmt.Errorf("unknown log level: %s", s)
}
}