Applies AX principle 2 (Comments as Usage Examples) — removes prose
descriptions that restate the function signature ("returns", "retrieves",
"creates", "wraps", etc.) and keeps or replaces with concrete usage
examples showing real calls with realistic values.
Co-Authored-By: Charon <charon@lethean.io>
289 lines
7.7 KiB
Go
289 lines
7.7 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, msg 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(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(logger.output, builder.String())
|
|
}
|
|
|
|
// logger.Debug("hashrate collected", logging.Fields{"rate": 1234})
|
|
func (logger *Logger) Debug(msg string, fields ...Fields) {
|
|
logger.log(LevelDebug, msg, mergeFields(fields))
|
|
}
|
|
|
|
// logger.Info("miner started", logging.Fields{"miner": "xmrig"})
|
|
func (logger *Logger) Info(msg string, fields ...Fields) {
|
|
logger.log(LevelInfo, msg, mergeFields(fields))
|
|
}
|
|
|
|
// logger.Warn("hashrate drop", logging.Fields{"current": 500, "min": 1000})
|
|
func (logger *Logger) Warn(msg string, fields ...Fields) {
|
|
logger.log(LevelWarn, msg, mergeFields(fields))
|
|
}
|
|
|
|
// logger.Error("miner crashed", logging.Fields{"code": -1, "miner": "xmrig"})
|
|
func (logger *Logger) Error(msg string, fields ...Fields) {
|
|
logger.log(LevelError, msg, 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 _, f := range fields {
|
|
for k, v := range f {
|
|
result[k] = v
|
|
}
|
|
}
|
|
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(msg string, fields ...Fields) {
|
|
GetGlobal().Debug(msg, fields...)
|
|
}
|
|
|
|
// logging.Info("miner started", logging.Fields{"miner": "xmrig", "pool": "pool.lthn.io"})
|
|
func Info(msg string, fields ...Fields) {
|
|
GetGlobal().Info(msg, fields...)
|
|
}
|
|
|
|
// logging.Warn("hashrate dropped below threshold", logging.Fields{"current": 500, "min": 1000})
|
|
func Warn(msg string, fields ...Fields) {
|
|
GetGlobal().Warn(msg, fields...)
|
|
}
|
|
|
|
// logging.Error("miner process exited unexpectedly", logging.Fields{"code": -1})
|
|
func Error(msg string, fields ...Fields) {
|
|
GetGlobal().Error(msg, 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)
|
|
}
|
|
}
|