mirror of
https://github.com/tarampampam/error-pages.git
synced 2024-08-30 18:22:40 +00:00
127 lines
3.5 KiB
Go
127 lines
3.5 KiB
Go
package logger
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"log/slog"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// internalAttrKeyLoggerName is used to store the logger name in the logger context (attributes).
|
|
const internalAttrKeyLoggerName = "named_logger"
|
|
|
|
var (
|
|
// consoleFormatAttrReplacer is a replacer for console format. It replaces some attributes with more
|
|
// human-readable ones.
|
|
consoleFormatAttrReplacer = func(_ []string, a slog.Attr) slog.Attr { //nolint:gochecknoglobals
|
|
switch a.Key {
|
|
case internalAttrKeyLoggerName:
|
|
return slog.String("logger", a.Value.String())
|
|
case "level":
|
|
return slog.String(a.Key, strings.ToLower(a.Value.String()))
|
|
default:
|
|
if ts, ok := a.Value.Any().(time.Time); ok && a.Key == "time" {
|
|
return slog.String(a.Key, ts.Format("15:04:05"))
|
|
}
|
|
}
|
|
|
|
return a
|
|
}
|
|
|
|
// jsonFormatAttrReplacer is a replacer for JSON format. It replaces some attributes with more
|
|
// machine-readable ones.
|
|
jsonFormatAttrReplacer = func(_ []string, a slog.Attr) slog.Attr { //nolint:gochecknoglobals
|
|
switch a.Key {
|
|
case internalAttrKeyLoggerName:
|
|
return slog.String("logger", a.Value.String())
|
|
case "level":
|
|
return slog.String(a.Key, strings.ToLower(a.Value.String()))
|
|
default:
|
|
if ts, ok := a.Value.Any().(time.Time); ok && a.Key == "time" {
|
|
return slog.Float64("ts", float64(ts.Unix())+float64(ts.Nanosecond())/1e9)
|
|
}
|
|
}
|
|
|
|
return a
|
|
}
|
|
)
|
|
|
|
// Logger is a simple logger that wraps [slog.Logger]. It provides a more convenient API for logging and
|
|
// formatting messages.
|
|
type Logger struct {
|
|
ctx context.Context
|
|
slog *slog.Logger
|
|
lvl Level
|
|
}
|
|
|
|
// New creates a new logger with the given level and format. Optionally, you can specify the writer to write logs to.
|
|
func New(l Level, f Format, writer ...io.Writer) (*Logger, error) {
|
|
var options slog.HandlerOptions
|
|
|
|
switch l {
|
|
case DebugLevel:
|
|
options.Level = slog.LevelDebug
|
|
case InfoLevel:
|
|
options.Level = slog.LevelInfo
|
|
case WarnLevel:
|
|
options.Level = slog.LevelWarn
|
|
case ErrorLevel:
|
|
options.Level = slog.LevelError
|
|
default:
|
|
return nil, errors.New("unsupported logging level")
|
|
}
|
|
|
|
var (
|
|
handler slog.Handler
|
|
target io.Writer
|
|
)
|
|
|
|
if len(writer) > 0 && writer[0] != nil {
|
|
target = writer[0]
|
|
} else {
|
|
target = os.Stderr
|
|
}
|
|
|
|
switch f {
|
|
case ConsoleFormat:
|
|
options.ReplaceAttr = consoleFormatAttrReplacer
|
|
|
|
handler = slog.NewTextHandler(target, &options)
|
|
case JSONFormat:
|
|
options.ReplaceAttr = jsonFormatAttrReplacer
|
|
|
|
handler = slog.NewJSONHandler(target, &options)
|
|
default:
|
|
return nil, errors.New("unsupported logging format")
|
|
}
|
|
|
|
return &Logger{ctx: context.Background(), slog: slog.New(handler), lvl: l}, nil
|
|
}
|
|
|
|
// Level returns the logger level.
|
|
func (l *Logger) Level() Level { return l.lvl }
|
|
|
|
// Named creates a new logger with the same properties as the original logger and the given name.
|
|
func (l *Logger) Named(name string) *Logger {
|
|
return &Logger{
|
|
ctx: l.ctx,
|
|
slog: l.slog.With(slog.String(internalAttrKeyLoggerName, name)),
|
|
lvl: l.lvl,
|
|
}
|
|
}
|
|
|
|
// Debug logs a message at DebugLevel.
|
|
func (l *Logger) Debug(msg string, f ...Attr) { l.slog.LogAttrs(l.ctx, slog.LevelDebug, msg, f...) }
|
|
|
|
// Info logs a message at InfoLevel.
|
|
func (l *Logger) Info(msg string, f ...Attr) { l.slog.LogAttrs(l.ctx, slog.LevelInfo, msg, f...) }
|
|
|
|
// Warn logs a message at WarnLevel.
|
|
func (l *Logger) Warn(msg string, f ...Attr) { l.slog.LogAttrs(l.ctx, slog.LevelWarn, msg, f...) }
|
|
|
|
// Error logs a message at ErrorLevel.
|
|
func (l *Logger) Error(msg string, f ...Attr) { l.slog.LogAttrs(l.ctx, slog.LevelError, msg, f...) }
|