log: fix syncer causing errors
This commit is contained in:
parent
97341ec698
commit
2486702215
|
@ -1,7 +1,7 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/mattn/go-isatty"
|
||||
slogmulti "github.com/samber/slog-multi"
|
||||
"github.com/tigorlazuardi/bluemage/go/config"
|
||||
"github.com/tigorlazuardi/bluemage/go/pkg/errs"
|
||||
"github.com/tigorlazuardi/bluemage/go/pkg/telemetry"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
@ -20,8 +21,10 @@ func NewHandler(cfg *config.Config) (slog.Handler, func() error) {
|
|||
cleanup := func() error { return nil }
|
||||
|
||||
if cfg.Bool("log.enable") {
|
||||
log, sync := createStandardLogger(cfg)
|
||||
cleanup = sync
|
||||
log := createStandardLogger(cfg)
|
||||
if closer, ok := log.(io.Closer); ok {
|
||||
cleanup = closer.Close
|
||||
}
|
||||
handlers = append(handlers, log)
|
||||
}
|
||||
|
||||
|
@ -29,7 +32,7 @@ func NewHandler(cfg *config.Config) (slog.Handler, func() error) {
|
|||
log, clean := createFileLogger(cfg)
|
||||
cl := cleanup
|
||||
cleanup = func() error {
|
||||
return errors.Join(cl(), clean())
|
||||
return errs.Join(cl(), clean())
|
||||
}
|
||||
handlers = append(handlers, log)
|
||||
}
|
||||
|
@ -66,19 +69,16 @@ func createFileLogger(cfg *config.Config) (slog.Handler, func() error) {
|
|||
AddSource: cfg.Bool("log.source"),
|
||||
Level: lvl,
|
||||
}
|
||||
return slog.NewJSONHandler(Lock(AddSync(output)), opts), output.Close
|
||||
return slog.NewJSONHandler(Lock(output), opts), output.Close
|
||||
}
|
||||
|
||||
func createStandardLogger(cfg *config.Config) (slog.Handler, func() error) {
|
||||
var output WriteSyncer
|
||||
var cleanup func() error
|
||||
func createStandardLogger(cfg *config.Config) slog.Handler {
|
||||
var output WriteLocker
|
||||
|
||||
if strings.ToLower(cfg.String("log.output")) == "stdout" {
|
||||
output = Lock(AddSync(colorable.NewColorableStdout()))
|
||||
cleanup = output.Sync
|
||||
output = Lock(colorable.NewColorableStdout())
|
||||
} else {
|
||||
output = Lock(AddSync(colorable.NewColorableStderr()))
|
||||
cleanup = output.Sync
|
||||
output = Lock(colorable.NewColorableStderr())
|
||||
}
|
||||
|
||||
var lvl slog.Level
|
||||
|
@ -89,9 +89,9 @@ func createStandardLogger(cfg *config.Config) (slog.Handler, func() error) {
|
|||
}
|
||||
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||
return NewPrettyHandler(output, opts), cleanup
|
||||
return NewPrettyHandler(output, opts)
|
||||
} else {
|
||||
return slog.NewJSONHandler(output, opts), cleanup
|
||||
return slog.NewJSONHandler(output, opts)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
|
||||
type PrettyHandler struct {
|
||||
opts *slog.HandlerOptions
|
||||
output WriteSyncer
|
||||
output WriteLocker
|
||||
replaceAttr func(groups []string, attr slog.Attr) slog.Attr
|
||||
withAttrs []slog.Attr
|
||||
withGroup []string
|
||||
|
@ -27,7 +27,7 @@ type PrettyHandler struct {
|
|||
}
|
||||
|
||||
// NewPrettyHandler creates a human friendly readable logs.
|
||||
func NewPrettyHandler(writer WriteSyncer, opts *slog.HandlerOptions) *PrettyHandler {
|
||||
func NewPrettyHandler(writer WriteLocker, opts *slog.HandlerOptions) *PrettyHandler {
|
||||
if opts == nil {
|
||||
opts = &slog.HandlerOptions{Level: slog.LevelDebug}
|
||||
}
|
||||
|
@ -56,24 +56,24 @@ func NewPrettyHandler(writer WriteSyncer, opts *slog.HandlerOptions) *PrettyHand
|
|||
// and then flush the buffer.
|
||||
//
|
||||
// Flush blocks other log writes until it's done.
|
||||
func (pr *PrettyHandler) Flush() error {
|
||||
func (pr *PrettyHandler) Flush() {
|
||||
pr.isFlushing.Store(true)
|
||||
defer func() { pr.isFlushing.Store(false) }()
|
||||
pr.wg.Wait()
|
||||
err := pr.output.Sync()
|
||||
for {
|
||||
select {
|
||||
// signal to handlers that flushing is done.
|
||||
case pr.flushingChan <- struct{}{}:
|
||||
default:
|
||||
return err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sync attemps to flush the buffer of the underlying writer.
|
||||
func (pr *PrettyHandler) Sync() error {
|
||||
return pr.output.Sync()
|
||||
func (pr *PrettyHandler) Close() error {
|
||||
pr.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Enabled implements slog.Handler interface.
|
||||
|
|
|
@ -2,68 +2,27 @@ package log
|
|||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type WriteSyncer interface {
|
||||
type WriteLocker interface {
|
||||
io.Writer
|
||||
Sync() error
|
||||
sync.Locker
|
||||
}
|
||||
|
||||
// WrapOsFile wraps an *os.File in a WriteSyncer.
|
||||
//
|
||||
// To support multithreaded logging and to support
|
||||
// flushing the buffer before the program exits.
|
||||
func WrapOsFile(f *os.File) WriteSyncer {
|
||||
return Lock(f)
|
||||
}
|
||||
|
||||
// AddSync converts an io.Writer to a WriteSyncer. It attempts to be
|
||||
// intelligent: if the concrete type of the io.Writer implements WriteSyncer,
|
||||
// we'll use the existing Sync method. If it doesn't, we'll add a no-op Sync.
|
||||
func AddSync(w io.Writer) WriteSyncer {
|
||||
switch w := w.(type) {
|
||||
case WriteSyncer:
|
||||
return w
|
||||
default:
|
||||
return writerWrapper{w}
|
||||
}
|
||||
}
|
||||
|
||||
type lockedWriteSyncer struct {
|
||||
type writeLock struct {
|
||||
sync.Mutex
|
||||
ws WriteSyncer
|
||||
io.Writer
|
||||
}
|
||||
|
||||
// Lock wraps a WriteSyncer in a mutex to make it safe for concurrent use. In
|
||||
// particular, *os.Files must be locked before use.
|
||||
func Lock(ws WriteSyncer) WriteSyncer {
|
||||
if _, ok := ws.(*lockedWriteSyncer); ok {
|
||||
//
|
||||
// If w is already a WriteLocker, it will be returned as is.
|
||||
func Lock(w io.Writer) WriteLocker {
|
||||
if wl, ok := w.(WriteLocker); ok {
|
||||
// no need to layer on another lock
|
||||
return ws
|
||||
return wl
|
||||
}
|
||||
return &lockedWriteSyncer{ws: ws}
|
||||
}
|
||||
|
||||
func (s *lockedWriteSyncer) Write(bs []byte) (int, error) {
|
||||
s.Lock()
|
||||
n, err := s.ws.Write(bs)
|
||||
s.Unlock()
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (s *lockedWriteSyncer) Sync() error {
|
||||
s.Lock()
|
||||
err := s.ws.Sync()
|
||||
s.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
type writerWrapper struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (w writerWrapper) Sync() error {
|
||||
return nil
|
||||
return &writeLock{Writer: w}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue