FILE / ScuroNeko/SNekLog
formatter.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
207 lines
6.7 KiB
Go
207 lines
6.7 KiB
Go
package sneklog
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Formatter
|
|
// Format: %t - time, %N - name, %l - level, %L - level uppercase, %b - traceback, %B - full traceback, %m - message,
|
|
// %M - method, %f - filename, %n - line number, %s - method signature, %p - full path
|
|
// Example: "%t [%l] %N: %m (%f:%n %s)"
|
|
//
|
|
// TimeStampFormat: %Y - year, %m - month, %d - day, %H - hour, %M - minute, %S - second,
|
|
// %z - timezone(i.e. 0300), %Z - timezone(i.e. 03:00)
|
|
// Example: "%d.%m.%Y %H:%M:%S"
|
|
//
|
|
// TraceBackFormat: %M - method, %f - filename, %n - line number, %s - method signature, %p - full path
|
|
type Formatter struct {
|
|
Format string
|
|
MessageSeparator string
|
|
|
|
// JSONTraceBackPath
|
|
// If true, in traceback will be path to file, otherwise only filename.
|
|
JSONTraceBackPath bool
|
|
TimeStampFormat string
|
|
TraceBackFormat string
|
|
TraceBackSeparator string
|
|
|
|
ColorOutput bool
|
|
ColorOnlyStdout bool
|
|
}
|
|
|
|
var DefaultTextFormatter = &Formatter{
|
|
Format: "%t %l %N: %m",
|
|
MessageSeparator: " ",
|
|
TimeStampFormat: RFC3339,
|
|
TraceBackFormat: "%M (%f:%n %s)",
|
|
TraceBackSeparator: "->",
|
|
ColorOutput: true,
|
|
ColorOnlyStdout: true,
|
|
}
|
|
var DefaultJsonFormatter = &Formatter{
|
|
Format: "%m",
|
|
MessageSeparator: " ",
|
|
TimeStampFormat: RFC3339,
|
|
ColorOutput: false,
|
|
}
|
|
|
|
// NewFormatter returns a copy of the default text formatter settings.
|
|
func NewFormatter() *Formatter {
|
|
return &Formatter{
|
|
Format: DefaultTextFormatter.Format,
|
|
MessageSeparator: DefaultTextFormatter.MessageSeparator,
|
|
TimeStampFormat: DefaultTextFormatter.TimeStampFormat,
|
|
TraceBackFormat: DefaultTextFormatter.TraceBackFormat,
|
|
TraceBackSeparator: DefaultTextFormatter.TraceBackSeparator,
|
|
ColorOutput: DefaultTextFormatter.ColorOutput,
|
|
ColorOnlyStdout: DefaultTextFormatter.ColorOnlyStdout,
|
|
}
|
|
}
|
|
|
|
// SetFormat sets the formatter template used for text rendering.
|
|
func (f *Formatter) SetFormat(format string) *Formatter {
|
|
f.Format = format
|
|
return f
|
|
}
|
|
|
|
// SetMessageSeparator sets the separator used to join message parts.
|
|
func (f *Formatter) SetMessageSeparator(separator string) *Formatter {
|
|
f.MessageSeparator = separator
|
|
return f
|
|
}
|
|
|
|
// SetTimeStampFormat sets the strftime-like format used for timestamps.
|
|
func (f *Formatter) SetTimeStampFormat(format string) *Formatter {
|
|
f.TimeStampFormat = format
|
|
return f
|
|
}
|
|
|
|
// SetTraceBackFormat sets the format used for a single traceback frame.
|
|
func (f *Formatter) SetTraceBackFormat(format string) *Formatter {
|
|
f.TraceBackFormat = format
|
|
return f
|
|
}
|
|
|
|
// SetTraceBackSeparator sets the separator used between traceback frames.
|
|
func (f *Formatter) SetTraceBackSeparator(separator string) *Formatter {
|
|
f.TraceBackSeparator = separator
|
|
return f
|
|
}
|
|
|
|
// SetColorOutput enables or disables colorized output.
|
|
func (f *Formatter) SetColorOutput(color bool) *Formatter {
|
|
f.ColorOutput = color
|
|
return f
|
|
}
|
|
|
|
// SetColorOnlyStdout restricts colorized output to stdout and stderr when enabled.
|
|
func (f *Formatter) SetColorOnlyStdout(only bool) *Formatter {
|
|
f.ColorOnlyStdout = only
|
|
return f
|
|
}
|
|
|
|
// FormatMessage applies the formatter template to the provided log record fields.
|
|
func (f *Formatter) FormatMessage(level LogLevel, prefix string, tb []*MethodTraceback, messages ...any) string {
|
|
if f == nil {
|
|
return fmt.Sprint(messages...)
|
|
}
|
|
output := f.Format
|
|
output = strings.ReplaceAll(output, "%t", f.FormatTime(time.Now()))
|
|
output = strings.ReplaceAll(output, "%N", prefix)
|
|
output = strings.ReplaceAll(output, "%l", level.GetName())
|
|
output = strings.ReplaceAll(output, "%L", strings.ToUpper(level.GetName()))
|
|
|
|
if len(tb) > 0 {
|
|
formattedTraceback := f.FormatTraceback(tb[0])
|
|
output = strings.ReplaceAll(output, "%b", formattedTraceback)
|
|
|
|
formattedTraceback = f.FormatTracebacks(tb)
|
|
output = strings.ReplaceAll(output, "%B", formattedTraceback)
|
|
} else {
|
|
output = strings.ReplaceAll(output, "%b", "")
|
|
output = strings.ReplaceAll(output, "%B", "")
|
|
}
|
|
|
|
if len(tb) > 0 {
|
|
output = strings.ReplaceAll(output, "%M", tb[0].Method)
|
|
output = strings.ReplaceAll(output, "%f", tb[0].Filename)
|
|
output = strings.ReplaceAll(output, "%n", fmt.Sprintf("%d", tb[0].Line))
|
|
output = strings.ReplaceAll(output, "%s", tb[0].Signature)
|
|
} else {
|
|
output = strings.ReplaceAll(output, "%M", "")
|
|
output = strings.ReplaceAll(output, "%f", "")
|
|
output = strings.ReplaceAll(output, "%n", "")
|
|
output = strings.ReplaceAll(output, "%s", "")
|
|
}
|
|
|
|
if f.JSONTraceBackPath && len(tb) > 0 {
|
|
output = strings.ReplaceAll(output, "%p", tb[0].FullPath)
|
|
} else {
|
|
output = strings.ReplaceAll(output, "%p", "")
|
|
}
|
|
|
|
message := Map(messages, func(m any) string { return fmt.Sprint(m) })
|
|
output = strings.ReplaceAll(output, "%m", strings.Join(message, f.MessageSeparator))
|
|
|
|
return output
|
|
}
|
|
|
|
// ColorizeString wraps a string in the ANSI color sequences defined by the level.
|
|
func (f *Formatter) ColorizeString(s string, level LogLevel) string {
|
|
builder := NewColorStringBuilder()
|
|
if level.fgRgb != nil {
|
|
builder.AddForegroundRGB(level.fgRgb)
|
|
} else if level.fg256 > 0 {
|
|
builder.AddForeground256Color(level.fg256)
|
|
} else {
|
|
builder.AddFgColor(level.fg)
|
|
}
|
|
|
|
if level.bgRgb != nil {
|
|
builder.AddBackgroundRGB(level.bgRgb)
|
|
} else if level.bg256 > 0 {
|
|
builder.AddBackground256Color(level.bg256)
|
|
} else {
|
|
builder.AddBgColor(level.bg)
|
|
}
|
|
|
|
for _, attr := range level.attrs {
|
|
builder.AddAttribute(attr)
|
|
}
|
|
return builder.AddText(s).AddReset().String()
|
|
}
|
|
|
|
// FormatTime formats a timestamp using the formatter timestamp layout.
|
|
func (f *Formatter) FormatTime(t time.Time) string {
|
|
if f.TimeStampFormat == "" {
|
|
return t.Format(time.RFC3339)
|
|
}
|
|
return t.Format(StrftimeToGo(f.TimeStampFormat))
|
|
}
|
|
|
|
// FormatTraceback formats a single traceback frame.
|
|
func (f *Formatter) FormatTraceback(traceback *MethodTraceback) string {
|
|
formattedTraceback := f.TraceBackFormat
|
|
if f.JSONTraceBackPath {
|
|
formattedTraceback = strings.ReplaceAll(formattedTraceback, "%p", traceback.FullPath)
|
|
} else {
|
|
formattedTraceback = strings.ReplaceAll(formattedTraceback, "%p", "")
|
|
}
|
|
formattedTraceback = strings.ReplaceAll(formattedTraceback, "%M", traceback.Method)
|
|
formattedTraceback = strings.ReplaceAll(formattedTraceback, "%f", traceback.Filename)
|
|
formattedTraceback = strings.ReplaceAll(formattedTraceback, "%n", fmt.Sprintf("%d", traceback.Line))
|
|
formattedTraceback = strings.ReplaceAll(formattedTraceback, "%s", traceback.Signature)
|
|
return formattedTraceback
|
|
}
|
|
|
|
// FormatTracebacks formats and joins multiple traceback frames.
|
|
func (f *Formatter) FormatTracebacks(traceback []*MethodTraceback) string {
|
|
var formattedTraceback []string
|
|
for _, frame := range traceback {
|
|
formattedTraceback = append(formattedTraceback, f.FormatTraceback(frame))
|
|
}
|
|
return strings.Join(formattedTraceback, f.TraceBackSeparator)
|
|
}
|