network/pkg/logging/logger.go
2025-08-03 16:24:04 +03:00

281 lines
7.0 KiB
Go

package logging
import (
"fmt"
"os"
"strings"
"time"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// ANSI color codes
const (
Reset = "\033[0m"
Bold = "\033[1m"
Dim = "\033[2m"
// Standard colors
Red = "\033[31m"
Green = "\033[32m"
Yellow = "\033[33m"
Blue = "\033[34m"
Magenta = "\033[35m"
Cyan = "\033[36m"
White = "\033[37m"
Gray = "\033[90m"
// Bright colors
BrightRed = "\033[91m"
BrightGreen = "\033[92m"
BrightYellow = "\033[93m"
BrightBlue = "\033[94m"
BrightMagenta = "\033[95m"
BrightCyan = "\033[96m"
BrightWhite = "\033[97m"
)
// ColoredLogger wraps zap.Logger with colored output
type ColoredLogger struct {
*zap.Logger
enableColors bool
}
// Component represents different parts of the system for color coding
type Component string
const (
ComponentBootstrap Component = "BOOTSTRAP"
ComponentNode Component = "NODE"
ComponentRQLite Component = "RQLITE"
ComponentLibP2P Component = "LIBP2P"
ComponentStorage Component = "STORAGE"
ComponentDatabase Component = "DATABASE"
ComponentClient Component = "CLIENT"
ComponentDHT Component = "DHT"
ComponentGeneral Component = "GENERAL"
)
// getComponentColor returns the color for a specific component
func getComponentColor(component Component) string {
switch component {
case ComponentBootstrap:
return BrightGreen
case ComponentNode:
return BrightBlue
case ComponentRQLite:
return BrightMagenta
case ComponentLibP2P:
return BrightCyan
case ComponentStorage:
return BrightYellow
case ComponentDatabase:
return Green
case ComponentClient:
return Blue
case ComponentDHT:
return Cyan
default:
return White
}
}
// getLevelColor returns the color for a log level
func getLevelColor(level zapcore.Level) string {
switch level {
case zapcore.DebugLevel:
return Gray
case zapcore.InfoLevel:
return BrightWhite
case zapcore.WarnLevel:
return BrightYellow
case zapcore.ErrorLevel:
return BrightRed
case zapcore.DPanicLevel, zapcore.PanicLevel, zapcore.FatalLevel:
return Red
default:
return White
}
}
// coloredConsoleEncoder creates a custom encoder with colors
func coloredConsoleEncoder(enableColors bool) zapcore.Encoder {
config := zap.NewDevelopmentEncoderConfig()
config.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
timeStr := t.Format("2006-01-02T15:04:05.000Z0700")
if enableColors {
enc.AppendString(fmt.Sprintf("%s%s%s", Dim, timeStr, Reset))
} else {
enc.AppendString(timeStr)
}
}
config.EncodeLevel = func(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
levelStr := strings.ToUpper(level.String())
if enableColors {
color := getLevelColor(level)
enc.AppendString(fmt.Sprintf("%s%s%-5s%s", color, Bold, levelStr, Reset))
} else {
enc.AppendString(fmt.Sprintf("%-5s", levelStr))
}
}
config.EncodeCaller = func(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) {
if enableColors {
enc.AppendString(fmt.Sprintf("%s%s%s", Dim, caller.TrimmedPath(), Reset))
} else {
enc.AppendString(caller.TrimmedPath())
}
}
return zapcore.NewConsoleEncoder(config)
}
// NewColoredLogger creates a new colored logger
func NewColoredLogger(component Component, enableColors bool) (*ColoredLogger, error) {
// Auto-detect color support if not explicitly disabled
if enableColors {
enableColors = supportsColor()
}
// Create encoder
encoder := coloredConsoleEncoder(enableColors)
// Create core
core := zapcore.NewCore(
encoder,
zapcore.AddSync(os.Stdout),
zapcore.DebugLevel,
)
// Create logger with caller information
logger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
return &ColoredLogger{
Logger: logger,
enableColors: enableColors,
}, nil
}
// NewDefaultLogger creates a logger with default settings and color auto-detection
func NewDefaultLogger(component Component) (*ColoredLogger, error) {
return NewColoredLogger(component, true)
}
// Component-specific logging methods
func (l *ColoredLogger) ComponentInfo(component Component, msg string, fields ...zap.Field) {
if l.enableColors {
color := getComponentColor(component)
msg = fmt.Sprintf("%s[%s]%s %s", color, component, Reset, msg)
} else {
msg = fmt.Sprintf("[%s] %s", component, msg)
}
l.Info(msg, fields...)
}
func (l *ColoredLogger) ComponentWarn(component Component, msg string, fields ...zap.Field) {
if l.enableColors {
color := getComponentColor(component)
msg = fmt.Sprintf("%s[%s]%s %s", color, component, Reset, msg)
} else {
msg = fmt.Sprintf("[%s] %s", component, msg)
}
l.Warn(msg, fields...)
}
func (l *ColoredLogger) ComponentError(component Component, msg string, fields ...zap.Field) {
if l.enableColors {
color := getComponentColor(component)
msg = fmt.Sprintf("%s[%s]%s %s", color, component, Reset, msg)
} else {
msg = fmt.Sprintf("[%s] %s", component, msg)
}
l.Error(msg, fields...)
}
func (l *ColoredLogger) ComponentDebug(component Component, msg string, fields ...zap.Field) {
if l.enableColors {
color := getComponentColor(component)
msg = fmt.Sprintf("%s[%s]%s %s", color, component, Reset, msg)
} else {
msg = fmt.Sprintf("[%s] %s", component, msg)
}
l.Debug(msg, fields...)
}
// supportsColor detects if the terminal supports color
func supportsColor() bool {
// Check environment variables
term := os.Getenv("TERM")
colorTerm := os.Getenv("COLORTERM")
// Common indicators of color support
if colorTerm != "" {
return true
}
if term != "" {
colorTerms := []string{
"xterm", "xterm-color", "xterm-256color",
"screen", "screen-256color",
"tmux", "tmux-256color",
"ansi", "color",
}
for _, ct := range colorTerms {
if strings.Contains(term, ct) {
return true
}
}
}
// Check if we're not in a pipe/redirect
if fileInfo, _ := os.Stdout.Stat(); fileInfo != nil {
return (fileInfo.Mode() & os.ModeCharDevice) == os.ModeCharDevice
}
return false
}
// StandardLogger provides colored standard library compatible logging
type StandardLogger struct {
logger *ColoredLogger
component Component
}
// NewStandardLogger creates a standard library compatible colored logger
func NewStandardLogger(component Component) (*StandardLogger, error) {
coloredLogger, err := NewDefaultLogger(component)
if err != nil {
return nil, err
}
return &StandardLogger{
logger: coloredLogger,
component: component,
}, nil
}
// Printf implements the standard library log interface with colors
func (s *StandardLogger) Printf(format string, v ...interface{}) {
msg := fmt.Sprintf(format, v...)
// Remove trailing newline if present (zap adds its own)
msg = strings.TrimSuffix(msg, "\n")
s.logger.ComponentInfo(s.component, msg)
}
// Print implements the standard library log interface with colors
func (s *StandardLogger) Print(v ...interface{}) {
msg := fmt.Sprint(v...)
msg = strings.TrimSuffix(msg, "\n")
s.logger.ComponentInfo(s.component, msg)
}
// Println implements the standard library log interface with colors
func (s *StandardLogger) Println(v ...interface{}) {
msg := fmt.Sprintln(v...)
msg = strings.TrimSuffix(msg, "\n")
s.logger.ComponentInfo(s.component, msg)
}