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