| Abhay Kumar | a61c522 | 2025-11-10 07:32:50 +0000 | [diff] [blame^] | 1 | //go:build go1.21 |
| 2 | // +build go1.21 |
| 3 | |
| 4 | /* |
| 5 | Copyright 2023 The logr Authors. |
| 6 | |
| 7 | Licensed under the Apache License, Version 2.0 (the "License"); |
| 8 | you may not use this file except in compliance with the License. |
| 9 | You may obtain a copy of the License at |
| 10 | |
| 11 | http://www.apache.org/licenses/LICENSE-2.0 |
| 12 | |
| 13 | Unless required by applicable law or agreed to in writing, software |
| 14 | distributed under the License is distributed on an "AS IS" BASIS, |
| 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 16 | See the License for the specific language governing permissions and |
| 17 | limitations under the License. |
| 18 | */ |
| 19 | |
| 20 | package funcr |
| 21 | |
| 22 | import ( |
| 23 | "context" |
| 24 | "log/slog" |
| 25 | |
| 26 | "github.com/go-logr/logr" |
| 27 | ) |
| 28 | |
| 29 | var _ logr.SlogSink = &fnlogger{} |
| 30 | |
| 31 | const extraSlogSinkDepth = 3 // 2 for slog, 1 for SlogSink |
| 32 | |
| 33 | func (l fnlogger) Handle(_ context.Context, record slog.Record) error { |
| 34 | kvList := make([]any, 0, 2*record.NumAttrs()) |
| 35 | record.Attrs(func(attr slog.Attr) bool { |
| 36 | kvList = attrToKVs(attr, kvList) |
| 37 | return true |
| 38 | }) |
| 39 | |
| 40 | if record.Level >= slog.LevelError { |
| 41 | l.WithCallDepth(extraSlogSinkDepth).Error(nil, record.Message, kvList...) |
| 42 | } else { |
| 43 | level := l.levelFromSlog(record.Level) |
| 44 | l.WithCallDepth(extraSlogSinkDepth).Info(level, record.Message, kvList...) |
| 45 | } |
| 46 | return nil |
| 47 | } |
| 48 | |
| 49 | func (l fnlogger) WithAttrs(attrs []slog.Attr) logr.SlogSink { |
| 50 | kvList := make([]any, 0, 2*len(attrs)) |
| 51 | for _, attr := range attrs { |
| 52 | kvList = attrToKVs(attr, kvList) |
| 53 | } |
| 54 | l.AddValues(kvList) |
| 55 | return &l |
| 56 | } |
| 57 | |
| 58 | func (l fnlogger) WithGroup(name string) logr.SlogSink { |
| 59 | l.startGroup(name) |
| 60 | return &l |
| 61 | } |
| 62 | |
| 63 | // attrToKVs appends a slog.Attr to a logr-style kvList. It handle slog Groups |
| 64 | // and other details of slog. |
| 65 | func attrToKVs(attr slog.Attr, kvList []any) []any { |
| 66 | attrVal := attr.Value.Resolve() |
| 67 | if attrVal.Kind() == slog.KindGroup { |
| 68 | groupVal := attrVal.Group() |
| 69 | grpKVs := make([]any, 0, 2*len(groupVal)) |
| 70 | for _, attr := range groupVal { |
| 71 | grpKVs = attrToKVs(attr, grpKVs) |
| 72 | } |
| 73 | if attr.Key == "" { |
| 74 | // slog says we have to inline these |
| 75 | kvList = append(kvList, grpKVs...) |
| 76 | } else { |
| 77 | kvList = append(kvList, attr.Key, PseudoStruct(grpKVs)) |
| 78 | } |
| 79 | } else if attr.Key != "" { |
| 80 | kvList = append(kvList, attr.Key, attrVal.Any()) |
| 81 | } |
| 82 | |
| 83 | return kvList |
| 84 | } |
| 85 | |
| 86 | // levelFromSlog adjusts the level by the logger's verbosity and negates it. |
| 87 | // It ensures that the result is >= 0. This is necessary because the result is |
| 88 | // passed to a LogSink and that API did not historically document whether |
| 89 | // levels could be negative or what that meant. |
| 90 | // |
| 91 | // Some example usage: |
| 92 | // |
| 93 | // logrV0 := getMyLogger() |
| 94 | // logrV2 := logrV0.V(2) |
| 95 | // slogV2 := slog.New(logr.ToSlogHandler(logrV2)) |
| 96 | // slogV2.Debug("msg") // =~ logrV2.V(4) =~ logrV0.V(6) |
| 97 | // slogV2.Info("msg") // =~ logrV2.V(0) =~ logrV0.V(2) |
| 98 | // slogv2.Warn("msg") // =~ logrV2.V(-4) =~ logrV0.V(0) |
| 99 | func (l fnlogger) levelFromSlog(level slog.Level) int { |
| 100 | result := -level |
| 101 | if result < 0 { |
| 102 | result = 0 // because LogSink doesn't expect negative V levels |
| 103 | } |
| 104 | return int(result) |
| 105 | } |