mirror of
https://github.com/usual2970/certimate.git
synced 2025-06-08 05:29:51 +00:00
193 lines
3.4 KiB
Go
193 lines
3.4 KiB
Go
package logging
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"sync"
|
|
|
|
types "github.com/pocketbase/pocketbase/tools/types"
|
|
)
|
|
|
|
type HookHandlerOptions struct {
|
|
Level slog.Leveler
|
|
WriteFunc func(ctx context.Context, record *Record) error
|
|
}
|
|
|
|
var _ slog.Handler = (*HookHandler)(nil)
|
|
|
|
type HookHandler struct {
|
|
mutex *sync.Mutex
|
|
parent *HookHandler
|
|
options *HookHandlerOptions
|
|
group string
|
|
attrs []slog.Attr
|
|
}
|
|
|
|
func NewHookHandler(opts *HookHandlerOptions) *HookHandler {
|
|
if opts == nil {
|
|
opts = &HookHandlerOptions{}
|
|
}
|
|
|
|
h := &HookHandler{
|
|
mutex: &sync.Mutex{},
|
|
options: opts,
|
|
}
|
|
|
|
if h.options.WriteFunc == nil {
|
|
panic("`options.WriteFunc` is nil")
|
|
}
|
|
|
|
if h.options.Level == nil {
|
|
h.options.Level = slog.LevelInfo
|
|
}
|
|
|
|
return h
|
|
}
|
|
|
|
func (h *HookHandler) Enabled(ctx context.Context, level slog.Level) bool {
|
|
return level >= h.options.Level.Level()
|
|
}
|
|
|
|
func (h *HookHandler) WithGroup(name string) slog.Handler {
|
|
if name == "" {
|
|
return h
|
|
}
|
|
|
|
return &HookHandler{
|
|
parent: h,
|
|
mutex: h.mutex,
|
|
options: h.options,
|
|
group: name,
|
|
}
|
|
}
|
|
|
|
func (h *HookHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
|
if len(attrs) == 0 {
|
|
return h
|
|
}
|
|
|
|
return &HookHandler{
|
|
parent: h,
|
|
mutex: h.mutex,
|
|
options: h.options,
|
|
attrs: attrs,
|
|
}
|
|
}
|
|
|
|
func (h *HookHandler) Handle(ctx context.Context, r slog.Record) error {
|
|
if h.group != "" {
|
|
h.mutex.Lock()
|
|
attrs := make([]any, 0, len(h.attrs)+r.NumAttrs())
|
|
for _, a := range h.attrs {
|
|
attrs = append(attrs, a)
|
|
}
|
|
h.mutex.Unlock()
|
|
|
|
r.Attrs(func(a slog.Attr) bool {
|
|
attrs = append(attrs, a)
|
|
return true
|
|
})
|
|
|
|
r = slog.NewRecord(r.Time, r.Level, r.Message, r.PC)
|
|
r.AddAttrs(slog.Group(h.group, attrs...))
|
|
} else if len(h.attrs) > 0 {
|
|
r = r.Clone()
|
|
|
|
h.mutex.Lock()
|
|
r.AddAttrs(h.attrs...)
|
|
h.mutex.Unlock()
|
|
}
|
|
|
|
if h.parent != nil {
|
|
return h.parent.Handle(ctx, r)
|
|
}
|
|
|
|
data := make(map[string]any, r.NumAttrs())
|
|
|
|
r.Attrs(func(a slog.Attr) bool {
|
|
if err := h.resolveAttr(data, a); err != nil {
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
|
|
log := &Record{
|
|
Time: r.Time,
|
|
Message: r.Message,
|
|
Data: types.JSONMap[any](data),
|
|
}
|
|
switch r.Level {
|
|
case slog.LevelDebug:
|
|
log.Level = LevelDebug
|
|
case slog.LevelInfo:
|
|
log.Level = LevelInfo
|
|
case slog.LevelWarn:
|
|
log.Level = LevelWarn
|
|
case slog.LevelError:
|
|
log.Level = LevelError
|
|
default:
|
|
log.Level = Level(fmt.Sprintf("LV(%d)", r.Level))
|
|
}
|
|
|
|
if err := h.writeRecord(ctx, log); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *HookHandler) SetLevel(level slog.Level) {
|
|
h.mutex.Lock()
|
|
h.options.Level = level
|
|
h.mutex.Unlock()
|
|
}
|
|
|
|
func (h *HookHandler) writeRecord(ctx context.Context, r *Record) error {
|
|
if h.parent != nil {
|
|
return h.parent.writeRecord(ctx, r)
|
|
}
|
|
|
|
return h.options.WriteFunc(ctx, r)
|
|
}
|
|
|
|
func (h *HookHandler) resolveAttr(data map[string]any, attr slog.Attr) error {
|
|
attr.Value = attr.Value.Resolve()
|
|
|
|
if attr.Equal(slog.Attr{}) {
|
|
return nil
|
|
}
|
|
|
|
switch attr.Value.Kind() {
|
|
case slog.KindGroup:
|
|
{
|
|
attrs := attr.Value.Group()
|
|
if len(attrs) == 0 {
|
|
return nil
|
|
}
|
|
|
|
groupData := make(map[string]any, len(attrs))
|
|
|
|
for _, subAttr := range attrs {
|
|
h.resolveAttr(groupData, subAttr)
|
|
}
|
|
|
|
if len(groupData) > 0 {
|
|
data[attr.Key] = groupData
|
|
}
|
|
}
|
|
|
|
default:
|
|
{
|
|
switch v := attr.Value.Any().(type) {
|
|
case error:
|
|
data[attr.Key] = v.Error()
|
|
default:
|
|
data[attr.Key] = v
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|