Mining/pkg/logging/logger.go
Claude 1f9624279a
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run
ax(batch): rename abbreviated variables to predictable names
resp→response, db→database pattern across mining, logging packages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 04:47:58 +01:00

289 lines
7.8 KiB
Go

// logger := logging.New(logging.Config{Level: logging.LevelDebug, Component: "mining"})
// logger.Info("started", logging.Fields{"miner": "xmrig"})
// logging.SetGlobal(logger)
// logging.Info("global log", logging.Fields{"key": "value"})
package logging
import (
"fmt"
"io"
"os"
"strings"
"sync"
"time"
)
// if level >= logging.LevelWarn { alertOps() }
type Level int
const (
LevelDebug Level = iota // logger.SetLevel(logging.LevelDebug)
LevelInfo // logger.SetLevel(logging.LevelInfo)
LevelWarn // logger.SetLevel(logging.LevelWarn)
LevelError // logger.SetLevel(logging.LevelError)
)
// logging.LevelDebug.String() // "DEBUG"
// logging.LevelError.String() // "ERROR"
func (level Level) String() string {
switch level {
case LevelDebug:
return "DEBUG"
case LevelInfo:
return "INFO"
case LevelWarn:
return "WARN"
case LevelError:
return "ERROR"
default:
return "UNKNOWN"
}
}
// logger := logging.New(logging.Config{Level: logging.LevelDebug, Component: "mining"})
// logger.Info("started", logging.Fields{"miner": "xmrig"})
type Logger struct {
mutex sync.Mutex
output io.Writer
level Level
component string
}
// logging.Config{Output: os.Stderr, Level: logging.LevelInfo, Component: "mining"}
type Config struct {
Output io.Writer
Level Level
Component string
}
// config := logging.DefaultConfig() // Output: os.Stderr, Level: LevelInfo
func DefaultConfig() Config {
return Config{
Output: os.Stderr,
Level: LevelInfo,
Component: "",
}
}
// logger := logging.New(logging.Config{Output: os.Stderr, Level: logging.LevelInfo, Component: "mining"})
func New(config Config) *Logger {
if config.Output == nil {
config.Output = os.Stderr
}
return &Logger{
output: config.Output,
level: config.Level,
component: config.Component,
}
}
// child := logger.WithComponent("xmrig")
// child.Info("miner started")
func (logger *Logger) WithComponent(component string) *Logger {
return &Logger{
output: logger.output,
level: logger.level,
component: component,
}
}
// logger.SetLevel(logging.LevelDebug)
func (logger *Logger) SetLevel(level Level) {
logger.mutex.Lock()
defer logger.mutex.Unlock()
logger.level = level
}
// current := logger.GetLevel()
// if current == logging.LevelDebug { logger.SetLevel(logging.LevelInfo) }
func (logger *Logger) GetLevel() Level {
logger.mutex.Lock()
defer logger.mutex.Unlock()
return logger.level
}
// logger.Info("started", logging.Fields{"miner": "xmrig", "pool": "pool.lthn.io"})
type Fields map[string]interface{}
// logger.log(LevelInfo, "started", Fields{"miner": "xmrig"})
func (logger *Logger) log(level Level, message string, fields Fields) {
logger.mutex.Lock()
defer logger.mutex.Unlock()
if level < logger.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 logger.component != "" {
builder.WriteString(" [")
builder.WriteString(logger.component)
builder.WriteString("]")
}
builder.WriteString(" ")
builder.WriteString(message)
// Add fields if present
if len(fields) > 0 {
builder.WriteString(" |")
for key, value := range fields {
builder.WriteString(" ")
builder.WriteString(key)
builder.WriteString("=")
builder.WriteString(fmt.Sprintf("%v", value))
}
}
builder.WriteString("\n")
fmt.Fprint(logger.output, builder.String())
}
// logger.Debug("hashrate collected", logging.Fields{"rate": 1234})
func (logger *Logger) Debug(message string, fields ...Fields) {
logger.log(LevelDebug, message, mergeFields(fields))
}
// logger.Info("miner started", logging.Fields{"miner": "xmrig"})
func (logger *Logger) Info(message string, fields ...Fields) {
logger.log(LevelInfo, message, mergeFields(fields))
}
// logger.Warn("hashrate drop", logging.Fields{"current": 500, "min": 1000})
func (logger *Logger) Warn(message string, fields ...Fields) {
logger.log(LevelWarn, message, mergeFields(fields))
}
// logger.Error("miner crashed", logging.Fields{"code": -1, "miner": "xmrig"})
func (logger *Logger) Error(message string, fields ...Fields) {
logger.log(LevelError, message, mergeFields(fields))
}
// logger.Debugf("collected %d hashrate points for %s", len(points), minerName)
func (logger *Logger) Debugf(format string, args ...interface{}) {
logger.log(LevelDebug, fmt.Sprintf(format, args...), nil)
}
// logger.Infof("miner %s started on pool %s", minerName, poolURL)
func (logger *Logger) Infof(format string, args ...interface{}) {
logger.log(LevelInfo, fmt.Sprintf(format, args...), nil)
}
// logger.Warnf("hashrate %d H/s below minimum %d H/s", current, minimum)
func (logger *Logger) Warnf(format string, args ...interface{}) {
logger.log(LevelWarn, fmt.Sprintf(format, args...), nil)
}
// logger.Errorf("failed to connect to pool %s: %v", poolURL, err)
func (logger *Logger) Errorf(format string, args ...interface{}) {
logger.log(LevelError, fmt.Sprintf(format, args...), nil)
}
// combined := mergeFields([]Fields{{"a": 1}, {"b": 2}}) // Fields{"a": 1, "b": 2}
func mergeFields(fields []Fields) Fields {
if len(fields) == 0 {
return nil
}
result := make(Fields)
for _, fieldSet := range fields {
for key, value := range fieldSet {
result[key] = value
}
}
return result
}
// --- Global logger for convenience ---
var (
globalLogger = New(DefaultConfig())
globalMutex sync.RWMutex
)
// logging.SetGlobal(logging.New(logging.Config{Level: logging.LevelDebug}))
func SetGlobal(logger *Logger) {
globalMutex.Lock()
defer globalMutex.Unlock()
globalLogger = logger
}
// logger := logging.GetGlobal()
// logger.Info("using global logger")
func GetGlobal() *Logger {
globalMutex.RLock()
defer globalMutex.RUnlock()
return globalLogger
}
// logging.SetGlobalLevel(logging.LevelDebug) // enable debug logging globally
func SetGlobalLevel(level Level) {
globalMutex.RLock()
defer globalMutex.RUnlock()
globalLogger.SetLevel(level)
}
// Global convenience functions that use the global logger
// logging.Debug("hashrate collected", logging.Fields{"rate": 1234, "miner": "xmrig"})
func Debug(message string, fields ...Fields) {
GetGlobal().Debug(message, fields...)
}
// logging.Info("miner started", logging.Fields{"miner": "xmrig", "pool": "pool.lthn.io"})
func Info(message string, fields ...Fields) {
GetGlobal().Info(message, fields...)
}
// logging.Warn("hashrate dropped below threshold", logging.Fields{"current": 500, "min": 1000})
func Warn(message string, fields ...Fields) {
GetGlobal().Warn(message, fields...)
}
// logging.Error("miner process exited unexpectedly", logging.Fields{"code": -1})
func Error(message string, fields ...Fields) {
GetGlobal().Error(message, fields...)
}
// logging.Debugf("collected %d hashrate points for %s", len(points), minerName)
func Debugf(format string, args ...interface{}) {
GetGlobal().Debugf(format, args...)
}
// logging.Infof("miner %s started on pool %s", minerName, poolURL)
func Infof(format string, args ...interface{}) {
GetGlobal().Infof(format, args...)
}
// logging.Warnf("hashrate %d H/s below minimum %d H/s", current, minimum)
func Warnf(format string, args ...interface{}) {
GetGlobal().Warnf(format, args...)
}
// logging.Errorf("failed to connect to pool %s: %v", poolURL, err)
func Errorf(format string, args ...interface{}) {
GetGlobal().Errorf(format, args...)
}
// level, err := logging.ParseLevel("DEBUG") // LevelDebug, nil
// level, err := logging.ParseLevel("nope") // LevelInfo, error
func ParseLevel(input string) (Level, error) {
switch strings.ToUpper(input) {
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", input)
}
}