slogsink.go 2.9 KB

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