github.com/shogo82148/goa-v1@v1.6.2/logging/slog/adapter.go (about)

     1  //go:build go1.21
     2  // +build go1.21
     3  
     4  /*
     5  Package goaslog contains an adapter that makes it possible to configure goa so it uses [log/slog]
     6  as logger backend.
     7  Usage:
     8  
     9  	handler := slog.NewJSONHandler(os.Stderr, nil)
    10  	// Initialize logger handler using [log/slog] package
    11  	service.WithLogger(goaslog.New(handler))
    12  	// ... Proceed with configuring and starting the goa service
    13  
    14  	// In handlers:
    15  	goaslog.Entry(ctx).Info("foo", "bar")
    16  */
    17  package goaslog
    18  
    19  import (
    20  	"context"
    21  	"log/slog"
    22  	"runtime"
    23  	"time"
    24  
    25  	"github.com/shogo82148/goa-v1"
    26  )
    27  
    28  var _ goa.LogAdapter = (*adapter)(nil)
    29  var _ goa.ContextLogAdapter = (*adapter)(nil)
    30  
    31  // adapter is the slog goa logger adapter.
    32  type adapter struct {
    33  	handler slog.Handler
    34  }
    35  
    36  // New wraps a [log/slog.Handler] into a goa logger.
    37  func New(handler slog.Handler) goa.LogAdapter {
    38  	return &adapter{handler: handler}
    39  }
    40  
    41  // Info logs messages using [log/slog].
    42  func (a *adapter) Info(msg string, data ...any) {
    43  	a.log(context.Background(), slog.LevelInfo, msg, data...)
    44  }
    45  
    46  // InfoContext logs messages using [log/slog].
    47  func (a *adapter) InfoContext(ctx context.Context, msg string, data ...any) {
    48  	a.log(ctx, slog.LevelInfo, msg, data...)
    49  }
    50  
    51  // Warn logs message using [log/slog].
    52  func (a *adapter) Warn(msg string, data ...any) {
    53  	a.log(context.Background(), slog.LevelWarn, msg, data...)
    54  }
    55  
    56  // WarnContext logs message using [log/slog].
    57  func (a *adapter) WarnContext(ctx context.Context, msg string, data ...any) {
    58  	a.log(ctx, slog.LevelWarn, msg, data...)
    59  }
    60  
    61  // Error logs errors using [log/slog].
    62  func (a *adapter) Error(msg string, data ...any) {
    63  	a.log(context.Background(), slog.LevelError, msg, data...)
    64  }
    65  
    66  // ErrorContext logs errors using [log/slog].
    67  func (a *adapter) ErrorContext(ctx context.Context, msg string, data ...any) {
    68  	a.log(ctx, slog.LevelError, msg, data...)
    69  }
    70  
    71  // New creates a new logger given a context.
    72  func (a *adapter) New(data ...any) goa.LogAdapter {
    73  	r := slog.NewRecord(time.Now(), slog.LevelInfo, "", 0)
    74  	r.Add(data...)
    75  
    76  	attrs := make([]slog.Attr, 0, r.NumAttrs())
    77  	r.Attrs(func(a slog.Attr) bool {
    78  		attrs = append(attrs, a)
    79  		return true
    80  	})
    81  	h := a.handler.WithAttrs(attrs)
    82  	return &adapter{handler: h}
    83  }
    84  
    85  func (a *adapter) log(ctx context.Context, level slog.Level, msg string, data ...any) {
    86  	if !a.handler.Enabled(ctx, level) {
    87  		return
    88  	}
    89  
    90  	var pc uintptr
    91  	var pcs [1]uintptr
    92  	// skip [runtime.Callers, this functions, this functions caller, the caller of the adapter]
    93  	runtime.Callers(4, pcs[:])
    94  	pc = pcs[0]
    95  	r := slog.NewRecord(time.Now(), level, msg, pc)
    96  	r.Add(data...)
    97  	a.handler.Handle(ctx, r)
    98  }