189 lines
4.2 KiB
Go
189 lines
4.2 KiB
Go
package logger
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"log/slog"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"arimelody.space/melody-vod-manager/colour"
|
|
"github.com/mattn/go-isatty"
|
|
)
|
|
|
|
type (
|
|
Options struct {
|
|
Handlers []slog.Handler
|
|
Level slog.Leveler
|
|
IsTerminal bool
|
|
}
|
|
|
|
SlogHandler struct {
|
|
attrs []slog.Attr
|
|
group string
|
|
mutex *sync.Mutex
|
|
options Options
|
|
writer io.Writer
|
|
}
|
|
)
|
|
|
|
const groupPadding = 11
|
|
const levelPadding = 5
|
|
|
|
func NewSlogHandler(writer io.Writer, options Options) *SlogHandler {
|
|
writerFile, writerIsFile := writer.(*os.File)
|
|
if writerIsFile {
|
|
options.IsTerminal = isatty.IsTerminal(writerFile.Fd())
|
|
}
|
|
|
|
return &SlogHandler{
|
|
options: options,
|
|
attrs: []slog.Attr{},
|
|
group: "OTHER",
|
|
mutex: &sync.Mutex{},
|
|
writer: writer,
|
|
}
|
|
}
|
|
|
|
func (log *SlogHandler) appendRecord(buf *strings.Builder, record slog.Record) {
|
|
log.appendTime(buf, record.Time)
|
|
log.appendGroup(buf, log.group)
|
|
log.appendLevel(buf, record.Level)
|
|
log.appendMessage(buf, record.Message)
|
|
|
|
for _, attr := range log.attrs {
|
|
log.appendAttr(buf, attr)
|
|
}
|
|
|
|
record.Attrs(func(attr slog.Attr) bool {
|
|
log.appendAttr(buf, attr)
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (log *SlogHandler) appendTime(buf *strings.Builder, t time.Time) {
|
|
if !t.IsZero() {
|
|
if log.options.IsTerminal {
|
|
buf.WriteString(colour.Dim + colour.Gray)
|
|
}
|
|
buf.WriteRune('[')
|
|
buf.WriteString(t.Format(time.UnixDate))
|
|
buf.WriteRune(']')
|
|
if log.options.IsTerminal {
|
|
buf.WriteString(colour.Reset)
|
|
}
|
|
buf.WriteRune(' ')
|
|
}
|
|
}
|
|
|
|
func (log *SlogHandler) appendGroup(buf *strings.Builder, group string) {
|
|
if log.options.IsTerminal {
|
|
buf.WriteString(colour.Dim + colour.Gray)
|
|
}
|
|
log.appendPadLeft(buf, groupPadding, group)
|
|
if log.options.IsTerminal {
|
|
buf.WriteString(colour.Reset)
|
|
}
|
|
buf.WriteRune(' ')
|
|
}
|
|
|
|
func (log *SlogHandler) appendPadLeft(buf *strings.Builder, padding int, content string) {
|
|
padding -= len(content)
|
|
for i := padding; i > 0; i-- {
|
|
buf.WriteRune(' ')
|
|
}
|
|
buf.WriteString(content)
|
|
}
|
|
|
|
func (log *SlogHandler) appendPadRight(buf *strings.Builder, padding int, content string) {
|
|
buf.WriteString(content)
|
|
padding -= len(content)
|
|
for i := padding; i > 0; i-- {
|
|
buf.WriteRune(' ')
|
|
}
|
|
}
|
|
|
|
func (log *SlogHandler) appendLevel(buf *strings.Builder, level slog.Level) {
|
|
if log.options.IsTerminal {
|
|
levelColour := colour.Reset
|
|
switch level {
|
|
case slog.LevelDebug:
|
|
levelColour = colour.Purple
|
|
case slog.LevelError:
|
|
levelColour = colour.Red
|
|
case slog.LevelWarn:
|
|
levelColour = colour.Yellow
|
|
case slog.LevelInfo:
|
|
levelColour = colour.Cyan
|
|
}
|
|
buf.WriteString(levelColour)
|
|
}
|
|
log.appendPadRight(buf, levelPadding, level.String())
|
|
if log.options.IsTerminal {
|
|
buf.WriteString(colour.Reset)
|
|
}
|
|
buf.WriteRune(' ')
|
|
}
|
|
|
|
func (log *SlogHandler) appendMessage(buf *strings.Builder, message string) {
|
|
if message != "" {
|
|
buf.WriteString(message)
|
|
buf.WriteRune(' ')
|
|
}
|
|
}
|
|
|
|
func (log *SlogHandler) appendAttr(buf *strings.Builder, attr slog.Attr) {
|
|
buf.WriteString(attr.String())
|
|
buf.WriteRune(' ')
|
|
}
|
|
|
|
func (log *SlogHandler) Enabled(ctx context.Context, level slog.Level) bool {
|
|
return level >= log.options.Level.Level()
|
|
}
|
|
|
|
func (log *SlogHandler) Handle(ctx context.Context, record slog.Record) error {
|
|
buf := &strings.Builder{}
|
|
|
|
log.appendRecord(buf, record)
|
|
|
|
buf.WriteRune('\n')
|
|
|
|
log.mutex.Lock()
|
|
defer log.mutex.Unlock()
|
|
|
|
if _, err := log.writer.Write([]byte(buf.String())); err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, handler := range log.options.Handlers {
|
|
if err := handler.Handle(ctx, record); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (log *SlogHandler) WithGroup(group string) slog.Handler {
|
|
log2 := *log
|
|
log2.group = group
|
|
log2.options.Handlers = make([]slog.Handler, len(log.options.Handlers))
|
|
for i, handler := range log.options.Handlers {
|
|
log.options.Handlers[i] = handler.WithGroup(group)
|
|
}
|
|
return &log2
|
|
}
|
|
|
|
func (log *SlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
|
log2 := *log
|
|
log2.attrs = make([]slog.Attr, 0, len(log.attrs)+len(attrs))
|
|
log2.attrs = append(log2.attrs, log.attrs...)
|
|
log2.attrs = append(log2.attrs, attrs...)
|
|
log2.options.Handlers = make([]slog.Handler, len(log.options.Handlers))
|
|
for i, handler := range log.options.Handlers {
|
|
log.options.Handlers[i] = handler.WithAttrs(attrs)
|
|
}
|
|
return &log2
|
|
}
|