diff --git a/README.md b/README.md index 9469d46..e6c4749 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,4 @@ Collection of Hertz logger extension | [logrus](./logrus) | This is a logger library that uses [logrus](https://github.com/sirupsen/logrus) to implement the [Hertz logger interface](https://www.cloudwego.io/docs/hertz/tutorials/framework-exten/log/) | [readme](./logrus/README.md) | [CoderPoet](https://github.com/CoderPoet) | | [zap](./zap) | This is a logger library that uses [zap](https://github.com/uber-go/zap) to implement the [Hertz logger interface](https://www.cloudwego.io/docs/hertz/tutorials/framework-exten/log/) | [readme](./zap/README.md) | [rogerogers](https://github.com/rogerogers) | | [zerolog](./zerolog) | This is a logger library that uses [zerolog](https://github.com/rs/zerolog) to implement the [Hertz logger interface](https://www.cloudwego.io/docs/hertz/tutorials/framework-exten/log/) | [readme](./zerolog/README.md) | [sillen102](https://github.com/sillen102) | -| [slog](./slog) | This is a logger library that uses [slog](https://pkg.go.dev/golang.org/x/exp/slog) to implement the [Hertz logger interface](https://www.cloudwego.io/docs/hertz/tutorials/framework-exten/log/) | [readme](./slog/README.md) | [rogerogers](https://github.com/rogerogers) | +| [slog](./slog) | This is a logger library that uses [slog](https://pkg.go.dev/log/slog) to implement the [Hertz logger interface](https://www.cloudwego.io/docs/hertz/tutorials/framework-exten/log/) | [readme](./slog/README.md) | [rogerogers](https://github.com/rogerogers) | diff --git a/licenses/LICENSE-go.txt b/licenses/LICENSE-go.txt new file mode 100644 index 0000000..ea5ea89 --- /dev/null +++ b/licenses/LICENSE-go.txt @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/slog/logger.go b/slog/logger.go index cb69ed5..be3c6f8 100644 --- a/slog/logger.go +++ b/slog/logger.go @@ -23,6 +23,12 @@ import ( "github.com/cloudwego/hertz/pkg/common/hlog" ) +const ( + LevelTrace = slog.Level(-8) + LevelNotice = slog.Level(2) + LevelFatal = slog.Level(12) +) + var _ hlog.FullLogger = (*Logger)(nil) func NewLogger(opts ...Option) *Logger { @@ -32,16 +38,52 @@ func NewLogger(opts ...Option) *Logger { for _, opt := range opts { opt.apply(config) } - if config.withLevel { - config.handlerOptions.Level = config.level - } - if config.withHandlerOptions { + if !config.withLevel && config.withHandlerOptions && config.handlerOptions.Level != nil { lvl := &slog.LevelVar{} lvl.Set(config.handlerOptions.Level.Level()) - config.handlerOptions.Level = lvl config.level = lvl } + config.handlerOptions.Level = config.level + + var replaceAttrDefined bool + if config.handlerOptions.ReplaceAttr == nil { + replaceAttrDefined = false + } else { + replaceAttrDefined = true + } + + replaceFun := config.handlerOptions.ReplaceAttr + + replaceAttr := func(groups []string, a slog.Attr) slog.Attr { + if a.Key == slog.LevelKey { + level := a.Value.Any().(slog.Level) + switch level { + case LevelTrace: + a.Value = slog.StringValue("Trace") + case slog.LevelDebug: + a.Value = slog.StringValue("Debug") + case slog.LevelInfo: + a.Value = slog.StringValue("Info") + case LevelNotice: + a.Value = slog.StringValue("Notice") + case slog.LevelWarn: + a.Value = slog.StringValue("Warn") + case slog.LevelError: + a.Value = slog.StringValue("Error") + case LevelFatal: + a.Value = slog.StringValue("Fatal") + default: + a.Value = slog.StringValue("Warn") + } + } + if replaceAttrDefined { + return replaceFun(groups, a) + } else { + return a + } + } + config.handlerOptions.ReplaceAttr = replaceAttr return &Logger{ l: slog.New(slog.NewJSONHandler(config.output, config.handlerOptions)), diff --git a/slog/logger_test.go b/slog/logger_test.go index 3e5007a..dc3d819 100644 --- a/slog/logger_test.go +++ b/slog/logger_test.go @@ -17,6 +17,7 @@ package slog import ( "bufio" "bytes" + "context" "log/slog" "os" "testing" @@ -100,9 +101,12 @@ func TestWithLevel(t *testing.T) { func TestWithHandlerOptions(t *testing.T) { buf := new(bytes.Buffer) - lvl := &slog.LevelVar{} - lvl.Set(slog.LevelError) - logger := NewLogger(WithHandlerOptions(&slog.HandlerOptions{Level: lvl})) + logger := NewLogger(WithHandlerOptions(&slog.HandlerOptions{Level: slog.LevelError, ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { + if a.Key == slog.MessageKey { + a.Key = "content" + } + return a + }})) hlog.SetLogger(logger) hlog.SetOutput(buf) @@ -117,4 +121,77 @@ func TestWithHandlerOptions(t *testing.T) { hlog.Info(infoMsg) assert.Contains(t, buf.String(), infoMsg) + assert.Contains(t, buf.String(), "content") + + buf.Reset() + hlog.SetLevel(hlog.LevelTrace) + + testCase := []struct { + levelName string + method func(...any) + msg string + }{ + { + "Trace", + hlog.Trace, + traceMsg, + }, + { + "Debug", + hlog.Debug, + debugMsg, + }, + { + "Info", + hlog.Info, + infoMsg, + }, + { + "Notice", + hlog.Notice, + noticeMsg, + }, + { + "Warn", + hlog.Warn, + warnMsg, + }, + { + "Error", + hlog.Error, + errorMsg, + }, + { + "Fatal", + hlog.Fatal, + fatalMsg, + }, + } + + for _, tc := range testCase { + tc.method(tc.msg) + assert.Contains(t, buf.String(), tc.levelName) + assert.Contains(t, buf.String(), tc.msg) + buf.Reset() + } +} + +func TestWithoutLevel(t *testing.T) { + buf := new(bytes.Buffer) + logger := NewLogger(WithHandlerOptions(&slog.HandlerOptions{AddSource: true})) + + hlog.SetLogger(logger) + hlog.SetOutput(buf) + + hlog.CtxInfof(context.TODO(), "hello %s", "hertz") + assert.Contains(t, buf.String(), "source") +} + +func TestWithOutput(t *testing.T) { + buf := new(bytes.Buffer) + logger := NewLogger(WithOutput(buf)) + hlog.SetLogger(logger) + + hlog.CtxErrorf(context.TODO(), errorMsg) + assert.Contains(t, buf.String(), errorMsg) } diff --git a/slog/utils.go b/slog/utils.go index 166b70d..b543c48 100644 --- a/slog/utils.go +++ b/slog/utils.go @@ -22,14 +22,20 @@ import ( func hLevelToSLevel(level hlog.Level) (lvl slog.Level) { switch level { - case hlog.LevelTrace, hlog.LevelDebug: + case hlog.LevelTrace: + lvl = LevelTrace + case hlog.LevelDebug: lvl = slog.LevelDebug case hlog.LevelInfo: lvl = slog.LevelInfo - case hlog.LevelWarn, hlog.LevelNotice: + case hlog.LevelWarn: lvl = slog.LevelWarn - case hlog.LevelError, hlog.LevelFatal: + case hlog.LevelNotice: + lvl = LevelNotice + case hlog.LevelError: lvl = slog.LevelError + case hlog.LevelFatal: + lvl = LevelFatal default: lvl = slog.LevelWarn }