From 72234b60178f4c5d98684e26015dec8c3a1af029 Mon Sep 17 00:00:00 2001 From: Dimitry Kolyshev Date: Tue, 1 Oct 2024 15:58:27 +0300 Subject: [PATCH] Pull request: AGDNS-2441-trace-log Squashed commit of the following: commit 444cf5ef077269dd073708c719dcefb509fbcfbc Author: Dimitry Kolyshev Date: Tue Oct 1 15:49:52 2024 +0300 slogutil: imp code commit e1e6a7cc0a12e713e4e85272ea766b09067aa816 Author: Dimitry Kolyshev Date: Tue Oct 1 15:45:49 2024 +0300 slogutil: imp code commit e9e5ef05f5e2285d8ff567741e73e135e553f477 Author: Dimitry Kolyshev Date: Tue Oct 1 15:21:42 2024 +0300 slogutil: imp code commit f3def15cde54fcf388c8a85a8c4ad357b26089ba Author: Dimitry Kolyshev Date: Tue Oct 1 15:15:20 2024 +0300 slogutil: default commit a79518ef162443452b500c953e6d04511fa1b81d Author: Dimitry Kolyshev Date: Tue Oct 1 15:09:52 2024 +0300 all: legacy trace log commit 86e7acf000b06ed74fe55a5009a9d05d3fedd902 Author: Dimitry Kolyshev Date: Mon Sep 30 10:58:08 2024 +0300 all: legacy trace log commit 954d7d2fc89c516c72e12c578ad6d34dcb22f499 Author: Dimitry Kolyshev Date: Mon Sep 30 10:09:29 2024 +0300 slogutil: imp code commit e9b9a24e924e4d3f8985559fcbc52fb0211f98fa Author: Dimitry Kolyshev Date: Fri Sep 27 11:32:24 2024 +0700 slogutil: replace level name commit 1aa7a3a06eb1517b4ddcc654a9676f2a2dedf26e Author: Dimitry Kolyshev Date: Fri Sep 27 09:54:03 2024 +0700 slogutil: imp code commit 8e07d8a08160c31678061ed31b7f46a6dc2a77fc Author: Dimitry Kolyshev Date: Fri Sep 27 08:16:27 2024 +0700 slogutil: imp code commit c9ee8271d557d5936530cef13b38efa030f9bb54 Author: Dimitry Kolyshev Date: Thu Sep 26 15:09:44 2024 +0700 slogutil: trace --- logutil/slogutil/defer_example_test.go | 2 +- logutil/slogutil/legacy.go | 2 + logutil/slogutil/slogutil.go | 51 +++++++++++++++++------ logutil/slogutil/slogutil_example_test.go | 26 +++++++++--- logutil/slogutil/verbosity.go | 31 ++++++++++++++ 5 files changed, 94 insertions(+), 18 deletions(-) create mode 100644 logutil/slogutil/verbosity.go diff --git a/logutil/slogutil/defer_example_test.go b/logutil/slogutil/defer_example_test.go index 8801de6..e1a5723 100644 --- a/logutil/slogutil/defer_example_test.go +++ b/logutil/slogutil/defer_example_test.go @@ -13,7 +13,7 @@ import ( func ExampleCloseAndLog() { ctx := context.Background() l := slogutil.New(&slogutil.Config{ - Verbose: true, + Level: slog.LevelDebug, }) func() { diff --git a/logutil/slogutil/legacy.go b/logutil/slogutil/legacy.go index 9e56152..796ea89 100644 --- a/logutil/slogutil/legacy.go +++ b/logutil/slogutil/legacy.go @@ -157,6 +157,8 @@ type logFunction func(format string, args ...any) // returned. func logFuncForLevel(lvl slog.Level) (logFunc logFunction, warnStr string, err error) { switch lvl { + case LevelTrace: + return aglog.Debug, "trace: ", nil case slog.LevelDebug: return aglog.Debug, "", nil case slog.LevelInfo: diff --git a/logutil/slogutil/slogutil.go b/logutil/slogutil/slogutil.go index 576aa96..4b52008 100644 --- a/logutil/slogutil/slogutil.go +++ b/logutil/slogutil/slogutil.go @@ -29,11 +29,11 @@ type Config struct { // If set, it must be valid. Format Format + // Level is the minimum record level that will be logged. + Level slog.Level + // AddTimestamp, if true, adds a timestamp to every record. AddTimestamp bool - - // Verbose, if true, enables verbose logging. - Verbose bool } // New creates a slog logger with the given parameters. If c is nil, the @@ -49,11 +49,7 @@ func New(c *Config) (l *slog.Logger) { } } - lvl := slog.LevelInfo - if c.Verbose { - lvl = slog.LevelDebug - } - + lvl := c.Level format := cmp.Or(c.Format, FormatDefault) output := cmp.Or[io.Writer](c.Output, os.Stdout) if format == FormatDefault { @@ -61,10 +57,7 @@ func New(c *Config) (l *slog.Logger) { return newDefault(output, lvl, c.AddTimestamp) } - var replaceAttr func(groups []string, a slog.Attr) (res slog.Attr) - if !c.AddTimestamp { - replaceAttr = RemoveTime - } + replaceAttr := newReplaceAttr(!c.AddTimestamp) var h slog.Handler switch format { @@ -95,6 +88,8 @@ func New(c *Config) (l *slog.Logger) { } // newDefault returns a new default slog logger set up with the given options. +// +// TODO(d.kolyshev): Replace log level name for [LevelTrace]. func newDefault(output io.Writer, lvl slog.Level, addTimestamp bool) (l *slog.Logger) { h := NewLevelHandler(lvl, slog.Default().Handler()) log.SetOutput(output) @@ -107,6 +102,38 @@ func newDefault(output io.Writer, lvl slog.Level, addTimestamp bool) (l *slog.Lo return slog.New(h) } +// newReplaceAttr is a function that returns [slog.HandlerOptions.ReplaceAttr] +// function for provided parameters. +func newReplaceAttr(removeTime bool) func(groups []string, a slog.Attr) (res slog.Attr) { + if !removeTime { + return ReplaceLevel + } + + return func(groups []string, a slog.Attr) (res slog.Attr) { + return ReplaceLevel(groups, RemoveTime(groups, a)) + } +} + +// traceAttrValue is a [LevelTrace] value under the [slog.LevelKey] key. +var traceAttrValue = slog.StringValue("TRACE") + +// ReplaceLevel is a function for [slog.HandlerOptions.ReplaceAttr] that adds +// [LevelTrace] custom name for level attribute. +func ReplaceLevel(groups []string, a slog.Attr) (res slog.Attr) { + if len(groups) > 0 { + return a + } + + if a.Key == slog.LevelKey { + lvl := a.Value.Any().(slog.Level) + if lvl == LevelTrace { + a.Value = traceAttrValue + } + } + + return a +} + // RemoveTime is a function for [slog.HandlerOptions.ReplaceAttr] that removes // the "time" attribute. func RemoveTime(groups []string, a slog.Attr) (res slog.Attr) { diff --git a/logutil/slogutil/slogutil_example_test.go b/logutil/slogutil/slogutil_example_test.go index a46f754..4f710f9 100644 --- a/logutil/slogutil/slogutil_example_test.go +++ b/logutil/slogutil/slogutil_example_test.go @@ -9,7 +9,7 @@ import ( func ExampleNew_default() { l := slogutil.New(&slogutil.Config{ - Verbose: true, + Level: slog.LevelDebug, }) l.Info("test info") @@ -22,8 +22,8 @@ func ExampleNew_default() { func ExampleNew_json() { l := slogutil.New(&slogutil.Config{ - Format: slogutil.FormatJSON, - Verbose: true, + Format: slogutil.FormatJSON, + Level: slog.LevelDebug, }) l.Info("test info") @@ -41,8 +41,8 @@ func ExampleNew_json() { func ExampleNew_text() { l := slogutil.New(&slogutil.Config{ - Format: slogutil.FormatText, - Verbose: true, + Format: slogutil.FormatText, + Level: slog.LevelDebug, }) l.Info("test info") @@ -72,3 +72,19 @@ This is a very long text with many lines.` // INFO my text line_num=2 line="" // INFO my text line_num=3 line="This is a very long text with many lines." } + +func ExampleNew_trace() { + l := slogutil.New(&slogutil.Config{ + Format: slogutil.FormatText, + Level: slogutil.LevelTrace, + }) + + l.Log(context.Background(), slogutil.LevelTrace, "test trace") + l.Info("test info") + l.Debug("test debug") + + // Output: + // level=TRACE msg="test trace" + // level=INFO msg="test info" + // level=DEBUG msg="test debug" +} diff --git a/logutil/slogutil/verbosity.go b/logutil/slogutil/verbosity.go new file mode 100644 index 0000000..05ba4e2 --- /dev/null +++ b/logutil/slogutil/verbosity.go @@ -0,0 +1,31 @@ +package slogutil + +import ( + "fmt" + "log/slog" + + "github.com/AdguardTeam/golibs/errors" +) + +// Acceptable [slog.Level] levels. +const ( + LevelTrace = slog.Level(-8) + LevelDebug = slog.LevelDebug + LevelInfo = slog.LevelInfo + LevelWarn = slog.LevelWarn + LevelError = slog.LevelError +) + +// VerbosityToLevel returns log level for given verbosity. +func VerbosityToLevel(l uint8) (lvl slog.Level, err error) { + switch l { + case 0: + return LevelInfo, nil + case 1: + return LevelDebug, nil + case 2: + return LevelTrace, nil + default: + return lvl, fmt.Errorf("%w: %d", errors.ErrBadEnumValue, l) + } +}