-
Notifications
You must be signed in to change notification settings - Fork 415
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
133 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
module github.com/ThreeDotsLabs/watermill | ||
|
||
go 1.20 | ||
go 1.21.0 | ||
|
||
require ( | ||
github.com/cenkalti/backoff/v3 v3.2.2 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package watermill | ||
|
||
import ( | ||
"log/slog" | ||
) | ||
|
||
// LevelTrace must be added, because [slog] package does not have one by default. Generate it by subtracting 4 levels from [slog.Debug] following the example of [slog.LevelWarn] and [slog.LevelError] which are set to 4 and 8. | ||
const LevelTrace = slog.LevelDebug - 4 | ||
|
||
func slogAttrsFromFields(fields LogFields) []any { | ||
result := make([]any, 0, len(fields)*2) | ||
|
||
for key, value := range fields { | ||
// result = append(result, slog.Any(key, value)) | ||
result = append(result, key, value) | ||
} | ||
|
||
return result | ||
} | ||
|
||
// SlogLoggerAdapter wraps [slog.Logger]. | ||
type SlogLoggerAdapter struct { | ||
slog *slog.Logger | ||
} | ||
|
||
// Error logs a message to [slog.LevelError]. | ||
func (s *SlogLoggerAdapter) Error(msg string, err error, fields LogFields) { | ||
s.slog.Error(msg, append(slogAttrsFromFields(fields), "error", err)...) | ||
} | ||
|
||
// Info logs a message to [slog.LevelInfo]. | ||
func (s *SlogLoggerAdapter) Info(msg string, fields LogFields) { | ||
s.slog.Info(msg, slogAttrsFromFields(fields)...) | ||
} | ||
|
||
// Debug logs a message to [slog.LevelDebug]. | ||
func (s *SlogLoggerAdapter) Debug(msg string, fields LogFields) { | ||
s.slog.Debug(msg, slogAttrsFromFields(fields)...) | ||
} | ||
|
||
// Trace logs a message to [LevelTrace]. | ||
func (s *SlogLoggerAdapter) Trace(msg string, fields LogFields) { | ||
s.slog.Log( | ||
// Void context, following the slog example | ||
// as it treats context slighly differently from | ||
// normal usage, minding contextual | ||
// values, but ignoring contextual deadline. | ||
// See the [slog] package documentation | ||
// for more details. | ||
nil, | ||
LevelTrace, | ||
msg, | ||
slogAttrsFromFields(fields)..., | ||
) | ||
} | ||
|
||
// With return a [SlogLoggerAdapter] with a set of fields injected into all consequent logging messages. | ||
func (s *SlogLoggerAdapter) With(fields LogFields) LoggerAdapter { | ||
return &SlogLoggerAdapter{slog: s.slog.With(slogAttrsFromFields(fields)...)} | ||
} | ||
|
||
// NewSlogLogger creates an adapter to the standard library's structured logging package. A `nil` logger is substituted for the result of [slog.Default]. | ||
func NewSlogLogger(logger *slog.Logger) LoggerAdapter { | ||
if logger == nil { | ||
logger = slog.Default() | ||
} | ||
return &SlogLoggerAdapter{ | ||
slog: logger, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package watermill | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"strings" | ||
"testing" | ||
|
||
"log/slog" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestSlogLoggerAdapter(t *testing.T) { | ||
b := &bytes.Buffer{} | ||
|
||
logger := NewSlogLogger(slog.New(slog.NewTextHandler( | ||
b, // output | ||
&slog.HandlerOptions{ | ||
Level: LevelTrace, | ||
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { | ||
if a.Key == "time" && len(groups) == 0 { | ||
// omit time stamp to make the test idempotent | ||
a.Value = slog.StringValue("[omit]") | ||
} | ||
return a | ||
}, | ||
}, | ||
))) | ||
|
||
logger = logger.With(LogFields{ | ||
"common1": "commonvalue", | ||
}) | ||
logger.Trace("test trace", LogFields{ | ||
"field1": "value1", | ||
}) | ||
logger.Error("test error", errors.New("error message"), LogFields{ | ||
"field2": "value2", | ||
}) | ||
logger.Info("test info", LogFields{ | ||
"field3": "value3", | ||
}) | ||
|
||
assert.Equal(t, | ||
strings.TrimSpace(b.String()), | ||
strings.TrimSpace(` | ||
time=[omit] level=DEBUG-4 msg="test trace" common1=commonvalue field1=value1 | ||
time=[omit] level=ERROR msg="test error" common1=commonvalue field2=value2 error="error message" | ||
time=[omit] level=INFO msg="test info" common1=commonvalue field3=value3 | ||
`), | ||
"Logging output does not match saved template.", | ||
) | ||
} |