Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Added `riverlog.LoggerSafely` which provides a non-panic variant of `riverlog.Logger` for use when code may or may not have a context logger available. [PR #1093](https://github.com/riverqueue/river/pull/1093).

## [0.27.1] - 2025-11-21

### Fixed
Expand Down
21 changes: 20 additions & 1 deletion riverlog/river_log.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,27 @@ type contextKey struct{}
// Logger extracts a logger from context from within the Work body of a worker.
// Middleware must be installed on either the worker or client for this function
// to be usable.
//
// This variant panics if no logger was available in context.
func Logger(ctx context.Context) *slog.Logger {
logger, ok := ctx.Value(contextKey{}).(*slog.Logger)
logger, ok := LoggerSafely(ctx)
if !ok {
panic("no logger in context; do you have riverlog.Middleware configured?")
}
return logger
}

// LoggerSafely extracts a logger from context from within the Work body of a
// worker. Middleware must be installed on either the worker or client for this
// function to be usable.
//
// This variant returns a boolean that's true if a logger was available in
// context and false otherwise.
func LoggerSafely(ctx context.Context) (*slog.Logger, bool) {
logger, ok := ctx.Value(contextKey{}).(*slog.Logger)
return logger, ok
}

// Middleware injects a context logger into the Work function of workers it's
// installed on (or workers of the client it's installed on) which is accessible
// with Logger, and which collates all log output to store it to metadata after
Expand Down Expand Up @@ -79,6 +92,12 @@ type MiddlewareConfig struct {
// riverlog.NewMiddleware(func(w io.Writer) slog.Handler {
// return slog.NewJSONHandler(w, nil)
// }, nil)
//
// With the middleware in place, the logger is available in a work function's
// context:
//
// func (w *MyWorker) Work(ctx context.Context, job *river.Job[MyArgs]) error {
// Logger(ctx).InfoContext(ctx, "Hello from work")
func NewMiddleware(newSlogHandler func(w io.Writer) slog.Handler, config *MiddlewareConfig) *Middleware {
return &Middleware{
config: defaultConfig(config),
Expand Down
46 changes: 46 additions & 0 deletions riverlog/river_log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,57 @@ import (
"github.com/riverqueue/river/riverdbtest"
"github.com/riverqueue/river/riverdriver"
"github.com/riverqueue/river/riverdriver/riverpgxv5"
"github.com/riverqueue/river/rivershared/riversharedtest"
"github.com/riverqueue/river/rivershared/util/slogutil"
"github.com/riverqueue/river/rivertest"
"github.com/riverqueue/river/rivertype"
)

func TestLogger(t *testing.T) {
t.Parallel()

ctx := context.Background()

t.Run("Success", func(t *testing.T) {
t.Parallel()

ctx := context.WithValue(ctx, contextKey{}, riversharedtest.Logger(t))

Logger(ctx).InfoContext(ctx, "Hello from logger")
})

t.Run("PanicIfNotSet", func(t *testing.T) {
t.Parallel()

require.PanicsWithValue(t, "no logger in context; do you have riverlog.Middleware configured?", func() {
Logger(ctx).InfoContext(ctx, "This will panic")
})
})
}

func TestLoggerSafely(t *testing.T) {
t.Parallel()

ctx := context.Background()

t.Run("Success", func(t *testing.T) {
t.Parallel()

ctx := context.WithValue(ctx, contextKey{}, riversharedtest.Logger(t))

logger, ok := LoggerSafely(ctx)
require.True(t, ok)
logger.InfoContext(ctx, "Hello from logger")
})

t.Run("PanicIfNotSet", func(t *testing.T) {
t.Parallel()

_, ok := LoggerSafely(ctx)
require.False(t, ok)
})
}

var _ rivertype.WorkerMiddleware = &Middleware{}

type loggingArgs struct {
Expand Down
Loading