1
0
mirror of https://github.com/go-micro/go-micro.git synced 2026-04-30 19:15:24 +02:00
Files
go-micro/logger/debug_handler.go
2026-02-04 14:37:40 +00:00

163 lines
3.6 KiB
Go

package logger
import (
"context"
"fmt"
"log/slog"
"runtime"
"strings"
dlog "go-micro.dev/v5/debug/log"
)
// debugLogHandler is a slog handler that writes to the debug/log buffer
type debugLogHandler struct {
level slog.Leveler
attrs []slog.Attr
group string
}
// newDebugLogHandler creates a new handler that writes to debug/log
func newDebugLogHandler(level slog.Leveler) *debugLogHandler {
return &debugLogHandler{
level: level,
attrs: make([]slog.Attr, 0),
}
}
func (h *debugLogHandler) Enabled(_ context.Context, level slog.Level) bool {
return level >= h.level.Level()
}
func (h *debugLogHandler) Handle(_ context.Context, r slog.Record) error {
// Build metadata from attributes
metadata := make(map[string]string)
// Add handler's attributes
for _, attr := range h.attrs {
metadata[attr.Key] = attr.Value.String()
}
// Add record's attributes
r.Attrs(func(a slog.Attr) bool {
metadata[a.Key] = a.Value.String()
return true
})
// Add level to metadata
metadata["level"] = r.Level.String()
// Add source if available
if sourcePath := extractSourceFilePath(r.PC); sourcePath != "" {
metadata["file"] = sourcePath
}
// Create debug log record
rec := dlog.Record{
Timestamp: r.Time,
Message: r.Message,
Metadata: metadata,
}
// Write to debug log
_ = dlog.DefaultLog.Write(rec)
return nil
}
func (h *debugLogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
newAttrs := make([]slog.Attr, len(h.attrs)+len(attrs))
copy(newAttrs, h.attrs)
copy(newAttrs[len(h.attrs):], attrs)
return &debugLogHandler{
level: h.level,
attrs: newAttrs,
group: h.group,
}
}
func (h *debugLogHandler) WithGroup(name string) slog.Handler {
// For simplicity, we'll just track the group name
// A full implementation would nest attributes properly
return &debugLogHandler{
level: h.level,
attrs: h.attrs,
group: name,
}
}
// multiHandler sends records to multiple handlers
type multiHandler struct {
handlers []slog.Handler
}
func newMultiHandler(handlers ...slog.Handler) *multiHandler {
return &multiHandler{
handlers: handlers,
}
}
func (h *multiHandler) Enabled(ctx context.Context, level slog.Level) bool {
// Enabled if any handler is enabled
for _, handler := range h.handlers {
if handler.Enabled(ctx, level) {
return true
}
}
return false
}
func (h *multiHandler) Handle(ctx context.Context, r slog.Record) error {
for _, handler := range h.handlers {
// Clone the record for each handler to avoid issues
if err := handler.Handle(ctx, r.Clone()); err != nil {
return err
}
}
return nil
}
func (h *multiHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
newHandlers := make([]slog.Handler, len(h.handlers))
for i, handler := range h.handlers {
newHandlers[i] = handler.WithAttrs(attrs)
}
return &multiHandler{handlers: newHandlers}
}
func (h *multiHandler) WithGroup(name string) slog.Handler {
newHandlers := make([]slog.Handler, len(h.handlers))
for i, handler := range h.handlers {
newHandlers[i] = handler.WithGroup(name)
}
return &multiHandler{handlers: newHandlers}
}
// extractSourceFilePath extracts the package/file:line from a PC
func extractSourceFilePath(pc uintptr) string {
if pc == 0 {
return ""
}
fs := runtime.CallersFrames([]uintptr{pc})
f, _ := fs.Next()
if f.File == "" {
return ""
}
// Extract just filename, not full path
idx := strings.LastIndexByte(f.File, '/')
if idx == -1 {
return fmt.Sprintf("%s:%d", f.File, f.Line)
}
// Get package/file:line
idx2 := strings.LastIndexByte(f.File[:idx], '/')
if idx2 == -1 {
return fmt.Sprintf("%s:%d", f.File[idx+1:], f.Line)
}
return fmt.Sprintf("%s:%d", f.File[idx2+1:], f.Line)
}