diff --git a/README.md b/README.md index 9f6ee9f..64c8ee3 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,10 @@ func main() { ```text Processors Logger --{ - Handlers --{ With Formatter + Handlers --|- Handler0 With Formatter0 + |- Handler1 With Formatter1 + |- Handler2 (can also without Formatter) + |- ... more ``` > Note: Be sure to remember to add `Handler`, `Processor` to the logger instance and log records will be processed by `Handler`. diff --git a/README.zh-CN.md b/README.zh-CN.md index 9ac3248..147c952 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -202,7 +202,10 @@ func main() { ```text Processors Logger --{ - Handlers --{ With Formatter + Handlers --|- Handler0 With Formatter0 + |- Handler1 With Formatter1 + |- Handler2 (can also without Formatter) + |- ... more ``` > 注意:一定要记得将 `Handler`, `Processor` 添加注册到 logger 实例上,日志记录才会经过 `Handler` 处理。 diff --git a/formatter.go b/formatter.go index ea8e183..0b135fe 100644 --- a/formatter.go +++ b/formatter.go @@ -28,13 +28,17 @@ type Formattable interface { SetFormatter(Formatter) } -// FormattableTrait definition -type FormattableTrait struct { +// FormattableTrait alias of FormatterWrapper +type FormattableTrait = FormatterWrapper + +// FormatterWrapper use for format log record. +type FormatterWrapper struct { + // if not set, default use the TextFormatter formatter Formatter } // Formatter get formatter. if not set, will return TextFormatter -func (f *FormattableTrait) Formatter() Formatter { +func (f *FormatterWrapper) Formatter() Formatter { if f.formatter == nil { f.formatter = NewTextFormatter() } @@ -42,12 +46,12 @@ func (f *FormattableTrait) Formatter() Formatter { } // SetFormatter to handler -func (f *FormattableTrait) SetFormatter(formatter Formatter) { +func (f *FormatterWrapper) SetFormatter(formatter Formatter) { f.formatter = formatter } // Format log record to bytes -func (f *FormattableTrait) Format(record *Record) ([]byte, error) { +func (f *FormatterWrapper) Format(record *Record) ([]byte, error) { return f.Formatter().Format(record) } diff --git a/formatter_test.go b/formatter_test.go index 62f055f..56c1249 100644 --- a/formatter_test.go +++ b/formatter_test.go @@ -1,9 +1,12 @@ package slog_test import ( + "fmt" + "runtime" "strings" "testing" + "github.com/gookit/goutil/byteutil" "github.com/gookit/goutil/dump" "github.com/gookit/goutil/testutil/assert" "github.com/gookit/slog" @@ -59,6 +62,23 @@ func TestNewTextFormatter(t *testing.T) { dump.Println(f.Fields()) assert.Contains(t, f.Fields(), "datetime") assert.Len(t, f.Fields(), strings.Count(slog.NamedTemplate, "{{")) + + f.WithEnableColor(true) + assert.True(t, f.EnableColor) + + t.Run("CallerFormatFunc", func(t *testing.T) { + buf := byteutil.NewBuffer() + h := handler.IOWriterWithMaxLevel(buf, slog.DebugLevel) + h.SetFormatter(slog.NewTextFormatter().Configure(func(f *slog.TextFormatter) { + f.CallerFormatFunc = func(rf *runtime.Frame) string { + return "custom_caller" + } + })) + + l := slog.NewWithHandlers(h) + l.Debug("test message") + assert.Contains(t, buf.String(), "custom_caller") + }) } func TestTextFormatter_Format(t *testing.T) { @@ -67,7 +87,7 @@ func TestTextFormatter_Format(t *testing.T) { bs, err := f.Format(r) logTxt := string(bs) - dump.Println(f.Template(), logTxt) + fmt.Println(f.Template(), logTxt) assert.NoErr(t, err) assert.NotEmpty(t, logTxt) @@ -76,39 +96,49 @@ func TestTextFormatter_Format(t *testing.T) { } func TestJSONFormatter(t *testing.T) { - l := slog.New() - f := slog.NewJSONFormatter() f.AddField(slog.FieldKeyTimestamp) - h := handler.NewConsoleHandler(slog.AllLevels) + h := handler.ConsoleWithLevels(slog.AllLevels) h.SetFormatter(f) - l.AddHandler(h) + l := slog.NewWithHandlers(h) fields := slog.M{ - "field1": 123, - "field2": "abc", + "field1": 123, + "field2": "abc", + "message": "field name is same of message", // will be as fields.message } l.WithFields(fields).Info("info", "message") - // PrettyPrint=true - - l = slog.New() - h = handler.NewConsoleHandler(slog.AllLevels) - f = slog.NewJSONFormatter(func(f *slog.JSONFormatter) { - f.Aliases = slog.StringMap{ - "level": "levelName", - } - f.PrettyPrint = true + t.Run("CallerFormatFunc", func(t *testing.T) { + h.SetFormatter(slog.NewJSONFormatter(func(f *slog.JSONFormatter) { + f.CallerFormatFunc = func(rf *runtime.Frame) string { + return rf.Function + } + })) + l.WithFields(fields).Info("info", "message") }) - h.SetFormatter(f) + // PrettyPrint=true + t.Run("PrettyPrint", func(t *testing.T) { + l = slog.New() + h = handler.ConsoleWithMaxLevel(slog.DebugLevel) + f = slog.NewJSONFormatter(func(f *slog.JSONFormatter) { + f.Aliases = slog.StringMap{ + "level": "levelName", + } + f.PrettyPrint = true + }) + + h.SetFormatter(f) + + l.AddHandler(h) + l.WithFields(fields). + SetData(slog.M{"key1": "val1"}). + SetExtra(slog.M{"ext1": "val1"}). + Info("info message and PrettyPrint is TRUE") - l.AddHandler(h) - l.WithFields(fields). - SetData(slog.M{"key1": "val1"}). - SetExtra(slog.M{"ext1": "val1"}). - Info("info message and PrettyPrint is TRUE") + }) } diff --git a/formatter_text.go b/formatter_text.go index 41ecb5e..7194936 100644 --- a/formatter_text.go +++ b/formatter_text.go @@ -71,6 +71,12 @@ func NewTextFormatter(template ...string) *TextFormatter { return f } +// Configure the formatter +func (f *TextFormatter) Configure(fn func(*TextFormatter)) *TextFormatter { + fn(f) + return f +} + // SetTemplate set the log format template and update field-map func (f *TextFormatter) SetTemplate(fmtTpl string) { f.template = fmtTpl @@ -82,6 +88,12 @@ func (f *TextFormatter) Template() string { return f.template } +// WithEnableColor enable color on print log to terminal +func (f *TextFormatter) WithEnableColor(enable bool) *TextFormatter { + f.EnableColor = enable + return f +} + // Fields get export field list func (f *TextFormatter) Fields() []string { ss := make([]string, 0, len(f.fields)/2) diff --git a/handler.go b/handler.go index cda51f7..e8d2ac5 100644 --- a/handler.go +++ b/handler.go @@ -155,7 +155,7 @@ func (h *LevelHandling) IsHandling(level Level) bool { // LevelFormatting wrap level handling and log formatter type LevelFormatting struct { LevelHandling - RecordFormatter + FormatterWrapper } // NewMaxLevelFormatting create new instance with max level diff --git a/logger.go b/logger.go index 52bd0b4..5cb7fe9 100644 --- a/logger.go +++ b/logger.go @@ -114,9 +114,7 @@ func (l *Logger) Config(fns ...LoggerFn) *Logger { return l } -// Configure current logger. -// -// Deprecated: use Config() +// Configure current logger. alias of Config() func (l *Logger) Configure(fn LoggerFn) *Logger { return l.Config(fn) } // RegisterExitHandler register an exit-handler on global exitHandlers diff --git a/logger_test.go b/logger_test.go index d58df3a..5b80dbb 100644 --- a/logger_test.go +++ b/logger_test.go @@ -23,7 +23,7 @@ func TestLoggerBasic(t *testing.T) { } func TestLogger_PushHandler(t *testing.T) { - l := slog.New().Config(func(l *slog.Logger) { + l := slog.New().Configure(func(l *slog.Logger) { l.DoNothingOnPanicFatal() }) @@ -38,7 +38,7 @@ func TestLogger_PushHandler(t *testing.T) { l.Warning(slog.WarnLevel, "message") l.Logf(slog.TraceLevel, "%s message", slog.TraceLevel) - assert.Contains(t, w1.String(), "WARNING message") + assert.Contains(t, w1.String(), "WARN message") assert.Contains(t, w2.String(), "TRACE message") assert.Contains(t, w2.String(), "TestLogger_PushHandler")