mirror of
https://git.sr.ht/~ashkeel/strimertul
synced 2024-09-30 02:40:33 +00:00
117 lines
2.4 KiB
Go
117 lines
2.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"math/rand"
|
|
"os"
|
|
"time"
|
|
|
|
"git.sr.ht/~ashkeel/containers/sync"
|
|
slogmulti "github.com/samber/slog-multi"
|
|
"gopkg.in/natefinch/lumberjack.v2"
|
|
)
|
|
|
|
const LogHistory = 50
|
|
|
|
var (
|
|
lastLogs *sync.Slice[LogEntry]
|
|
incomingLogs chan LogEntry
|
|
)
|
|
|
|
func initLogger(level slog.Level) {
|
|
lastLogs = sync.NewSlice[LogEntry]()
|
|
incomingLogs = make(chan LogEntry, 100)
|
|
logStorage := NewLogStorage(level)
|
|
fileLogger := &lumberjack.Logger{
|
|
Filename: logFilename,
|
|
MaxSize: 20,
|
|
MaxBackups: 3,
|
|
MaxAge: 28,
|
|
}
|
|
consoleHandler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
|
|
Level: level,
|
|
})
|
|
fileHandler := slog.NewJSONHandler(fileLogger, &slog.HandlerOptions{
|
|
AddSource: true,
|
|
Level: level,
|
|
})
|
|
logger := slog.New(slogmulti.Fanout(consoleHandler, fileHandler, logStorage))
|
|
|
|
slog.SetDefault(logger)
|
|
}
|
|
|
|
type LogEntry struct {
|
|
ID string `json:"id"`
|
|
Time string `json:"time"`
|
|
Level string `json:"level"`
|
|
Message string `json:"message"`
|
|
Data string `json:"data"`
|
|
}
|
|
|
|
type LogStorage struct {
|
|
minLevel slog.Level
|
|
attrs []slog.Attr
|
|
group string
|
|
}
|
|
|
|
func (core *LogStorage) Enabled(_ context.Context, level slog.Level) bool {
|
|
return level >= core.minLevel
|
|
}
|
|
|
|
func (core *LogStorage) Handle(_ context.Context, record slog.Record) error {
|
|
attributes := map[string]any{}
|
|
record.Attrs(func(attrs slog.Attr) bool {
|
|
attributes[attrs.Key] = attrs.Value.Any()
|
|
return true
|
|
})
|
|
attrJSON, _ := json.MarshalToString(attributes)
|
|
|
|
// Generate unique log ID
|
|
id := fmt.Sprintf("%d-%d", time.Now().UnixNano(), rand.Int31())
|
|
|
|
logEntry := LogEntry{
|
|
ID: id,
|
|
Time: record.Time.Format(time.RFC3339),
|
|
Level: record.Level.String(),
|
|
Message: record.Message,
|
|
Data: attrJSON,
|
|
}
|
|
lastLogs.Push(logEntry)
|
|
if lastLogs.Size() > LogHistory {
|
|
lastLogs.Splice(0, 1)
|
|
}
|
|
incomingLogs <- logEntry
|
|
return nil
|
|
}
|
|
|
|
func (core *LogStorage) WithAttrs(attrs []slog.Attr) slog.Handler {
|
|
return &LogStorage{
|
|
minLevel: core.minLevel,
|
|
attrs: append(core.attrs, attrs...),
|
|
group: core.group,
|
|
}
|
|
}
|
|
|
|
func (core *LogStorage) WithGroup(name string) slog.Handler {
|
|
return &LogStorage{
|
|
minLevel: core.minLevel,
|
|
attrs: core.attrs,
|
|
group: name,
|
|
}
|
|
}
|
|
|
|
func NewLogStorage(level slog.Level) *LogStorage {
|
|
return &LogStorage{
|
|
minLevel: level,
|
|
}
|
|
}
|
|
|
|
func parseLogFields(data map[string]any) []any {
|
|
fields := []any{}
|
|
for k, v := range data {
|
|
fields = append(fields, k, v)
|
|
}
|
|
return fields
|
|
}
|