-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
slog: add an slog backed implementation of Logger
- Loading branch information
1 parent
aab9422
commit d81e157
Showing
2 changed files
with
253 additions
and
0 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 |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package btclog | ||
|
||
import ( | ||
"context" | ||
"encoding/hex" | ||
"log/slog" | ||
) | ||
|
||
// Hex is a convenience function for a hex-encoded log attributes. | ||
func Hex(key string, value []byte) slog.Attr { | ||
h := hex.EncodeToString(value) | ||
|
||
return slog.String(key, h) | ||
} | ||
|
||
type attrsKey struct{} | ||
|
||
// WithCtx returns a copy of the context with which the logging attributes are | ||
// associated. | ||
// | ||
// Usage: | ||
// | ||
// ctx := log.WithCtx(ctx, "height", 1234) | ||
// ... | ||
// log.Info(ctx, "Height processed") // Will contain attribute: height=1234 | ||
func WithCtx(ctx context.Context, attrs ...any) context.Context { | ||
return context.WithValue(ctx, attrsKey{}, mergeAttrs(ctx, attrs)) | ||
} | ||
|
||
// mergeAttrs returns the attributes from the context merged with the provided attributes. | ||
func mergeAttrs(ctx context.Context, attrs []any) []any { | ||
resp, _ := ctx.Value(attrsKey{}).([]any) // We know the type. | ||
resp = append(resp, attrs...) | ||
|
||
return resp | ||
} |
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,217 @@ | ||
package btclog | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log/slog" | ||
) | ||
|
||
// Handler wraps the slog.Handler interface with a few more methods that we | ||
// need in order to satisfy the Logger interface. | ||
type Handler interface { | ||
slog.Handler | ||
|
||
// Level returns the current logging level of the Handler. | ||
Level() Level | ||
|
||
// SetLevel changes the logging level of the Handler to the passed | ||
// level. | ||
SetLevel(level Level) | ||
} | ||
|
||
// sLogger is an implementation of Logger backed by a structured sLogger. | ||
type sLogger struct { | ||
Handler | ||
logger *slog.Logger | ||
} | ||
|
||
// NewSLogger constructs a new structured logger from the given Handler. | ||
func NewSLogger(handler Handler) Logger { | ||
return &sLogger{ | ||
Handler: handler, | ||
logger: slog.New(handler), | ||
} | ||
} | ||
|
||
// Tracef formats message according to format specifier, prepends the prefix as | ||
// necessary, and writes to log with LevelTrace. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) Tracef(format string, params ...any) { | ||
l.toSlogf(LevelTrace, format, params...) | ||
} | ||
|
||
// Debugf formats message according to format specifier, prepends the prefix as | ||
// necessary, and writes to log with LevelDebug. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) Debugf(format string, params ...any) { | ||
l.toSlogf(LevelDebug, format, params...) | ||
} | ||
|
||
// Infof formats message according to format specifier, prepends the prefix as | ||
// necessary, and writes to log with LevelInfo. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) Infof(format string, params ...any) { | ||
l.toSlogf(LevelInfo, format, params...) | ||
} | ||
|
||
// Warnf formats message according to format specifier, prepends the prefix as | ||
// necessary, and writes to log with LevelWarn. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) Warnf(format string, params ...any) { | ||
l.toSlogf(LevelWarn, format, params...) | ||
} | ||
|
||
// Errorf formats message according to format specifier, prepends the prefix as | ||
// necessary, and writes to log with LevelError. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) Errorf(format string, params ...any) { | ||
l.toSlogf(LevelError, format, params...) | ||
} | ||
|
||
// Criticalf formats message according to format specifier, prepends the prefix as | ||
// necessary, and writes to log with LevelCritical. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) Criticalf(format string, params ...any) { | ||
l.toSlogf(LevelCritical, format, params...) | ||
} | ||
|
||
// Trace formats message using the default formats for its operands, prepends | ||
// the prefix as necessary, and writes to log with LevelTrace. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) Trace(v ...any) { | ||
l.toSlog(LevelTrace, v...) | ||
} | ||
|
||
// Debug formats message using the default formats for its operands, prepends | ||
// the prefix as necessary, and writes to log with LevelDebug. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) Debug(v ...any) { | ||
l.toSlog(LevelDebug, v...) | ||
} | ||
|
||
// Info formats message using the default formats for its operands, prepends | ||
// the prefix as necessary, and writes to log with LevelInfo. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) Info(v ...any) { | ||
l.toSlog(LevelInfo, v...) | ||
} | ||
|
||
// Warn formats message using the default formats for its operands, prepends | ||
// the prefix as necessary, and writes to log with LevelWarn. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) Warn(v ...any) { | ||
l.toSlog(LevelWarn, v...) | ||
} | ||
|
||
// Error formats message using the default formats for its operands, prepends | ||
// the prefix as necessary, and writes to log with LevelError. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) Error(v ...any) { | ||
l.toSlog(LevelError, v...) | ||
} | ||
|
||
// Critical formats message using the default formats for its operands, prepends | ||
// the prefix as necessary, and writes to log with LevelCritical. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) Critical(v ...any) { | ||
l.toSlog(LevelCritical, v...) | ||
} | ||
|
||
// TraceS writes a structured log with the given message and key-value pair | ||
// attributes with LevelTrace to the log. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) TraceS(ctx context.Context, msg string, attrs ...any) { | ||
l.logger.Log(ctx, slog.Level(LevelTrace), msg, | ||
mergeAttrs(ctx, attrs)...) | ||
} | ||
|
||
// DebugS writes a structured log with the given message and key-value pair | ||
// attributes with LevelDebug to the log. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) DebugS(ctx context.Context, msg string, attrs ...any) { | ||
l.logger.Log(ctx, slog.Level(LevelDebug), msg, | ||
mergeAttrs(ctx, attrs)...) | ||
} | ||
|
||
// InfoS writes a structured log with the given message and key-value pair | ||
// attributes with LevelInfo to the log. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) InfoS(ctx context.Context, msg string, attrs ...any) { | ||
l.logger.Log(ctx, slog.Level(LevelInfo), msg, | ||
mergeAttrs(ctx, attrs)...) | ||
} | ||
|
||
// WarnS writes a structured log with the given message and key-value pair | ||
// attributes with LevelWarn to the log. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) WarnS(ctx context.Context, msg string, err error, | ||
attrs ...any) { | ||
|
||
if err != nil { | ||
attrs = append([]any{slog.String("err", err.Error())}, attrs...) | ||
} | ||
|
||
l.logger.Log(ctx, slog.Level(LevelWarn), msg, mergeAttrs(ctx, attrs)...) | ||
} | ||
|
||
// ErrorS writes a structured log with the given message and key-value pair | ||
// attributes with LevelError to the log. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) ErrorS(ctx context.Context, msg string, err error, | ||
attrs ...any) { | ||
|
||
if err != nil { | ||
attrs = append([]any{slog.String("err", err.Error())}, attrs...) | ||
} | ||
|
||
l.logger.Log(ctx, slog.Level(LevelError), msg, | ||
mergeAttrs(ctx, attrs)...) | ||
} | ||
|
||
// CriticalS writes a structured log with the given message and key-value pair | ||
// attributes with LevelCritical to the log. | ||
// | ||
// This is part of the Logger interface implementation. | ||
func (l *sLogger) CriticalS(ctx context.Context, msg string, err error, | ||
attrs ...any) { | ||
if err != nil { | ||
attrs = append([]any{slog.String("err", err.Error())}, attrs...) | ||
} | ||
|
||
l.logger.Log(ctx, slog.Level(LevelCritical), msg, | ||
mergeAttrs(ctx, attrs)...) | ||
} | ||
|
||
// toSlogf is a helper method that converts an unstructured log call that | ||
// contains a format string and parameters for the string into the appropriate | ||
// form expected by the structured logger. | ||
func (l *sLogger) toSlogf(level Level, format string, params ...any) { | ||
l.logger.Log(context.Background(), slog.Level(level), | ||
fmt.Sprintf(format, params...)) | ||
} | ||
|
||
// toSlog is a helper method that converts an unstructured log call that | ||
// contains a number of parameters into the appropriate form expected by the | ||
// structured logger. | ||
func (l *sLogger) toSlog(level Level, v ...any) { | ||
l.logger.Log(context.Background(), slog.Level(level), fmt.Sprint(v...)) | ||
} | ||
|
||
var _ Logger = (*sLogger)(nil) |