FILE / ScuroNeko/SNekLog
logger.go
Исходный файл и его история в репозитории.
Golang lint / lint (push) Successful in 24s
- add custom LogLevel constructors and richer color configuration - support ANSI, 256-color, RGB, and text attributes on LogLevel - introduce clearer preferred APIs such as NewLogger and SetName - preserve backward compatibility with deprecated wrappers for v2.0.1 APIs - restore legacy ColorStringBuilder signatures as compatibility wrappers - fix newline separator handling in Println-style output - add tests for color precedence, deprecated aliases, and LogLevel attributes - update GoDoc and bilingual README documentation
334 lines
9.7 KiB
Go
334 lines
9.7 KiB
Go
package sneklog
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
)
|
|
|
|
// LogLevel describes a logging severity.
|
|
type LogLevel struct {
|
|
n uint8
|
|
t string
|
|
|
|
fg FgColor
|
|
fg256 FgColor256
|
|
fgRgb FgColorRGB
|
|
|
|
bg BgColor
|
|
bg256 BgColor256
|
|
bgRgb BgColorRGB
|
|
|
|
attrs []Attribute
|
|
}
|
|
|
|
// NewLogLevel creates a log level without predefined colors.
|
|
func NewLogLevel(index uint8, name string) LogLevel {
|
|
return LogLevel{n: index, t: name, attrs: []Attribute{}}
|
|
}
|
|
|
|
// NewLogLevelWithColors creates a log level with ANSI foreground and background colors.
|
|
func NewLogLevelWithColors(index uint8, name string, fg FgColor, bg BgColor) LogLevel {
|
|
return LogLevel{n: index, t: name, fg: fg, bg: bg, attrs: []Attribute{}}
|
|
}
|
|
|
|
// GetName returns the lowercase textual representation of the level.
|
|
func (l *LogLevel) GetName() string { return l.t }
|
|
|
|
// SetFgColor sets the ANSI foreground color and clears other foreground color modes.
|
|
// Deprecated: use SetForegroundColor. This method will be removed in v3.
|
|
func (l *LogLevel) SetFgColor(color FgColor) *LogLevel {
|
|
return l.SetForegroundColor(color)
|
|
}
|
|
|
|
// GetFgColor returns the ANSI foreground color for the level.
|
|
// Deprecated: use GetForegroundColor. This method will be removed in v3.
|
|
func (l *LogLevel) GetFgColor() FgColor { return l.GetForegroundColor() }
|
|
|
|
// SetForegroundColor sets the ANSI foreground color and clears other foreground color modes.
|
|
func (l *LogLevel) SetForegroundColor(color FgColor) *LogLevel {
|
|
l.fg = color
|
|
l.fg256 = 0
|
|
l.fgRgb = nil
|
|
return l
|
|
}
|
|
|
|
// GetForegroundColor returns the ANSI foreground color for the level.
|
|
func (l *LogLevel) GetForegroundColor() FgColor { return l.fg }
|
|
|
|
// SetForeground256Color sets the 256-color foreground and clears other foreground color modes.
|
|
func (l *LogLevel) SetForeground256Color(color FgColor256) *LogLevel {
|
|
l.fg = 0
|
|
l.fg256 = color
|
|
l.fgRgb = nil
|
|
return l
|
|
}
|
|
|
|
// GetForeground256Color returns the configured 256-color foreground value.
|
|
func (l *LogLevel) GetForeground256Color() FgColor256 { return l.fg256 }
|
|
|
|
// SetForegroundRGB sets the RGB foreground color and clears other foreground color modes.
|
|
func (l *LogLevel) SetForegroundRGB(r, g, b uint8) *LogLevel {
|
|
l.fg = 0
|
|
l.fg256 = 0
|
|
l.fgRgb = FgColorRGB{r, g, b}
|
|
return l
|
|
}
|
|
|
|
// GetForegroundRGB returns the configured RGB foreground value.
|
|
func (l *LogLevel) GetForegroundRGB() FgColorRGB { return l.fgRgb }
|
|
|
|
// SetBgColor sets the ANSI background color and clears other background color modes.
|
|
// Deprecated: use SetBackgroundColor. This method will be removed in v3.
|
|
func (l *LogLevel) SetBgColor(color BgColor) *LogLevel {
|
|
return l.SetBackgroundColor(color)
|
|
}
|
|
|
|
// GetBgColor returns the ANSI background color for the level.
|
|
// Deprecated: use GetBackgroundColor. This method will be removed in v3.
|
|
func (l *LogLevel) GetBgColor() BgColor { return l.GetBackgroundColor() }
|
|
|
|
// SetBackgroundColor sets the ANSI background color and clears other background color modes.
|
|
func (l *LogLevel) SetBackgroundColor(color BgColor) *LogLevel {
|
|
l.bg = color
|
|
l.bg256 = 0
|
|
l.bgRgb = nil
|
|
return l
|
|
}
|
|
|
|
// GetBackgroundColor returns the ANSI background color for the level.
|
|
func (l *LogLevel) GetBackgroundColor() BgColor { return l.bg }
|
|
|
|
// SetBackground256Color sets the 256-color background and clears other background color modes.
|
|
func (l *LogLevel) SetBackground256Color(color BgColor256) *LogLevel {
|
|
l.bg = 0
|
|
l.bg256 = color
|
|
l.bgRgb = nil
|
|
return l
|
|
}
|
|
|
|
// GetBackground256Color returns the configured 256-color background value.
|
|
func (l *LogLevel) GetBackground256Color() BgColor256 { return l.bg256 }
|
|
|
|
// SetBackgroundRGB sets the RGB background color and clears other background color modes.
|
|
func (l *LogLevel) SetBackgroundRGB(r, g, b uint8) *LogLevel {
|
|
l.bg = 0
|
|
l.bg256 = 0
|
|
l.bgRgb = BgColorRGB{r, g, b}
|
|
return l
|
|
}
|
|
|
|
// GetBackgroundRGB returns the configured RGB background value.
|
|
func (l *LogLevel) GetBackgroundRGB() BgColorRGB { return l.bgRgb }
|
|
|
|
// AddAttribute appends an ANSI text attribute to the level.
|
|
func (l *LogLevel) AddAttribute(a Attribute) *LogLevel {
|
|
l.attrs = append(l.attrs, a)
|
|
return l
|
|
}
|
|
|
|
// RemoveAttribute removes all matching ANSI text attributes from the level.
|
|
func (l *LogLevel) RemoveAttribute(a Attribute) *LogLevel {
|
|
attrs := make([]Attribute, 0)
|
|
for _, attr := range l.attrs {
|
|
if attr != a {
|
|
attrs = append(attrs, attr)
|
|
}
|
|
}
|
|
l.attrs = attrs
|
|
return l
|
|
}
|
|
|
|
// SetAttributes replaces the level attributes with a copy of the provided slice.
|
|
func (l *LogLevel) SetAttributes(a []Attribute) *LogLevel {
|
|
attrs := make([]Attribute, len(a))
|
|
copy(attrs, a)
|
|
l.attrs = attrs
|
|
return l
|
|
}
|
|
|
|
// GetAttributes returns a copy of the configured ANSI text attributes.
|
|
func (l *LogLevel) GetAttributes() []Attribute {
|
|
attrs := make([]Attribute, len(l.attrs))
|
|
copy(attrs, l.attrs)
|
|
return attrs
|
|
}
|
|
|
|
// MethodTraceback describes a single stack frame attached to a log entry.
|
|
type MethodTraceback struct {
|
|
Method string `json:"method"`
|
|
Filename string `json:"filename"`
|
|
Line int `json:"line"`
|
|
Signature string `json:"signature"`
|
|
FullPath string `json:"fullPath"`
|
|
}
|
|
|
|
// Predefined log levels.
|
|
var (
|
|
INFO = NewLogLevelWithColors(0, "info", FgWhite, BgNone)
|
|
WARN = NewLogLevelWithColors(1, "warn", FgHiYellow, BgNone)
|
|
ERROR = NewLogLevelWithColors(2, "error", FgHiRed, BgNone)
|
|
FATAL = NewLogLevelWithColors(3, "fatal", FgRed, BgNone)
|
|
DEBUG = NewLogLevelWithColors(4, "debug", FgGreen, BgNone)
|
|
)
|
|
|
|
// Logger routes log records to one or more configured writers.
|
|
type Logger struct {
|
|
prefix string
|
|
level LogLevel
|
|
writers []LoggerWriter
|
|
replacers []replacer
|
|
|
|
jsonPretty bool
|
|
}
|
|
|
|
// CreateLogger creates a logger with default settings.
|
|
// Deprecated: use NewLogger. This method will be removed in v3.
|
|
func CreateLogger() *Logger {
|
|
return &Logger{
|
|
prefix: "LOG",
|
|
level: FATAL,
|
|
}
|
|
}
|
|
|
|
// NewLogger creates a logger with default settings.
|
|
func NewLogger() *Logger {
|
|
return &Logger{
|
|
prefix: "LOG",
|
|
level: FATAL,
|
|
}
|
|
}
|
|
|
|
// Prefix sets the record prefix and returns the logger for chaining.
|
|
// Deprecated: use SetName. This method will be removed in v3.
|
|
func (l *Logger) Prefix(prefix string) *Logger {
|
|
l.prefix = prefix
|
|
return l
|
|
}
|
|
|
|
// SetName sets the record prefix and returns the logger for chaining.
|
|
func (l *Logger) SetName(name string) *Logger {
|
|
l.prefix = name
|
|
return l
|
|
}
|
|
|
|
// Level sets the maximum enabled level and returns the logger for chaining.
|
|
// Deprecated: use SetLevel. This method will be removed in v3.
|
|
func (l *Logger) Level(level LogLevel) *Logger {
|
|
l.level = level
|
|
return l
|
|
}
|
|
|
|
// SetLevel sets the maximum enabled level and returns the logger for chaining.
|
|
func (l *Logger) SetLevel(level LogLevel) *Logger {
|
|
l.level = level
|
|
return l
|
|
}
|
|
|
|
// JsonPretty enables or disables indented JSON output for JSON writers created by the logger.
|
|
// Deprecated: use SetJSONPretty. This method will be removed in v3.
|
|
func (l *Logger) JsonPretty(b bool) *Logger {
|
|
l.jsonPretty = b
|
|
return l
|
|
}
|
|
|
|
// SetJSONPretty enables or disables indented output for JSON writers created by the logger.
|
|
func (l *Logger) SetJSONPretty(b bool) *Logger {
|
|
l.jsonPretty = b
|
|
return l
|
|
}
|
|
|
|
// AddWriters appends multiple writers to the logger.
|
|
func (l *Logger) AddWriters(writers ...LoggerWriter) *Logger {
|
|
l.writers = append(l.writers, writers...)
|
|
return l
|
|
}
|
|
|
|
// AddWriter appends a single writer to the logger.
|
|
// Deprecated: use AddWriters. This method will be removed in v3.
|
|
func (l *Logger) AddWriter(writer LoggerWriter) *Logger {
|
|
l.writers = append(l.writers, writer)
|
|
return l
|
|
}
|
|
|
|
// AddReplacer appends a message replacer and returns the logger for chaining.
|
|
//
|
|
// Replacement rules are applied before records are passed to writers. Empty old
|
|
// values are ignored because replacing an empty string would insert the
|
|
// replacement between every UTF-8 sequence.
|
|
func (l *Logger) AddReplacer(old, new string) *Logger {
|
|
if old == "" {
|
|
return l
|
|
}
|
|
r := replacer{old: old, new: new}
|
|
l.replacers = append(l.replacers, r)
|
|
return l
|
|
}
|
|
|
|
// Close closes all owned writers and returns a joined error, if any.
|
|
func (l *Logger) Close() error {
|
|
var errs []error
|
|
for _, writer := range l.writers {
|
|
if writer == nil {
|
|
continue
|
|
}
|
|
err := writer.Close()
|
|
if err != nil {
|
|
errs = append(errs, err)
|
|
}
|
|
}
|
|
l.writers = nil
|
|
return errors.Join(errs...)
|
|
}
|
|
|
|
// CreateTextWriter wraps an external writer with the logger text settings.
|
|
func (l *Logger) CreateTextWriter(w io.Writer) *LoggerTextWriter { return CreateTextWriter(w) }
|
|
|
|
// CreateTextStdoutWriter creates a non-owning text writer for os.Stdout.
|
|
func (l *Logger) CreateTextStdoutWriter() *LoggerTextWriter { return CreateTextStdoutWriter() }
|
|
|
|
// CreateTextFileWriter creates an owning text writer for a file.
|
|
func (l *Logger) CreateTextFileWriter(filename string) (*LoggerTextWriter, error) {
|
|
return CreateTextFileWriter(filename)
|
|
}
|
|
|
|
// CreateJsonWriter wraps an external writer with the logger JSON settings.
|
|
func (l *Logger) CreateJsonWriter(w io.Writer) *LoggerJsonWriter {
|
|
return CreateJsonWriter(w, l.jsonPretty)
|
|
}
|
|
|
|
// CreateJsonStdoutWriter creates a non-owning JSON writer for os.Stdout.
|
|
func (l *Logger) CreateJsonStdoutWriter() *LoggerJsonWriter {
|
|
return CreateJsonStdoutWriter(l.jsonPretty)
|
|
}
|
|
|
|
// CreateJsonFileWriter creates an owning JSON writer for a file.
|
|
func (l *Logger) CreateJsonFileWriter(filename string) (*LoggerJsonWriter, error) {
|
|
return CreateJsonFileWriter(filename, l.jsonPretty)
|
|
}
|
|
|
|
type replacer struct {
|
|
old string
|
|
new string
|
|
}
|
|
|
|
func (r replacer) replace(s string) string { return strings.ReplaceAll(s, r.old, r.new) }
|
|
func (l *Logger) replace(s string) string {
|
|
out := s
|
|
for _, repl := range l.replacers {
|
|
out = repl.replace(out)
|
|
}
|
|
return out
|
|
}
|
|
func (l *Logger) replaceAll(messages ...any) []any {
|
|
if len(l.replacers) == 0 {
|
|
return messages
|
|
}
|
|
out := make([]any, len(messages))
|
|
for i, msg := range messages {
|
|
out[i] = l.replace(fmt.Sprint(msg))
|
|
}
|
|
return out
|
|
}
|