From 98c4692bcdd6506d46c81866d9bebeef7d462b96 Mon Sep 17 00:00:00 2001 From: junya koyama Date: Wed, 7 Feb 2024 23:58:20 +0900 Subject: [PATCH] zapslog: Support for a custom mapping of slog.Level to zap.Level Signed-off-by: junya koyama --- exp/zapslog/handler.go | 22 +++----------- exp/zapslog/leveler.go | 65 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 18 deletions(-) create mode 100644 exp/zapslog/leveler.go diff --git a/exp/zapslog/handler.go b/exp/zapslog/handler.go index 7951d456a..617c41879 100644 --- a/exp/zapslog/handler.go +++ b/exp/zapslog/handler.go @@ -39,6 +39,7 @@ type Handler struct { addCaller bool addStackAt slog.Level callerSkip int + leveler ConvertLeveler // List of unapplied groups. // @@ -54,6 +55,7 @@ func NewHandler(core zapcore.Core, opts ...HandlerOption) *Handler { h := &Handler{ core: core, addStackAt: slog.LevelError, + leveler: &DefaultConvertLeveler{}, } for _, v := range opts { v.apply(h) @@ -113,31 +115,15 @@ func convertAttrToField(attr slog.Attr) zapcore.Field { } } -// convertSlogLevel maps slog Levels to zap Levels. -// Note that there is some room between slog levels while zap levels are continuous, so we can't 1:1 map them. -// See also https://go.googlesource.com/proposal/+/master/design/56345-structured-logging.md?pli=1#levels -func convertSlogLevel(l slog.Level) zapcore.Level { - switch { - case l >= slog.LevelError: - return zapcore.ErrorLevel - case l >= slog.LevelWarn: - return zapcore.WarnLevel - case l >= slog.LevelInfo: - return zapcore.InfoLevel - default: - return zapcore.DebugLevel - } -} - // Enabled reports whether the handler handles records at the given level. func (h *Handler) Enabled(ctx context.Context, level slog.Level) bool { - return h.core.Enabled(convertSlogLevel(level)) + return h.core.Enabled(h.leveler.ConvertLevel(level)) } // Handle handles the Record. func (h *Handler) Handle(ctx context.Context, record slog.Record) error { ent := zapcore.Entry{ - Level: convertSlogLevel(record.Level), + Level: h.leveler.ConvertLevel(record.Level), Time: record.Time, Message: record.Message, LoggerName: h.name, diff --git a/exp/zapslog/leveler.go b/exp/zapslog/leveler.go new file mode 100644 index 000000000..de61651f5 --- /dev/null +++ b/exp/zapslog/leveler.go @@ -0,0 +1,65 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//go:build go1.21 + +package zapslog + +import ( + "log/slog" + + "go.uber.org/zap/zapcore" +) + +// ConvertLeveler maps from [log/slog.Level] to [go.uber.org/zap/zapcore.Level]. +// Note that there is some room between slog levels while zap levels are continuous, so we can't 1:1 map them. +// See also [structured logging proposal] +// +// [structured logging proposal]: https://go.googlesource.com/proposal/+/master/design/56345-structured-logging.md?pli=1#levels +type ConvertLeveler interface { + ConvertLevel(l slog.Level) zapcore.Level +} + +// DefaultConvertLeveler maps. +// implements: [go.uber.org/zap/exp/zapslog.ConvertLeveler] +type DefaultConvertLeveler struct{} + +// ConvertLevel static maps from [log/slog.Level] to [go.uber.org/zap/zapcore.Level]. +// - [log/slog.LevelError] to [go.uber.org/zap/zapcore.ErrorLevel] +// - [log/slog.LevelWarn] to [go.uber.org/zap/zapcore.WarnLevel] +// - [log/slog.LevelInfo] to [go.uber.org/zap/zapcore.InfoLevel] +// - [log/slog.LevelDebug] or default to [go.uber.org/zap/zapcore.DebugLevel] +// +// Note that there is some room between slog levels while zap levels are continuous, so we can't 1:1 map them. +// See also [structured logging proposal] +// +// [structured logging proposal]: https://go.googlesource.com/proposal/+/master/design/56345-structured-logging.md?pli=1#levels +func (c *DefaultConvertLeveler) ConvertLevel(l slog.Level) zapcore.Level { + switch { + case l >= slog.LevelError: + return zapcore.ErrorLevel + case l >= slog.LevelWarn: + return zapcore.WarnLevel + case l >= slog.LevelInfo: + return zapcore.InfoLevel + default: + return zapcore.DebugLevel + } +}