Files
PrivateCaptcha/pkg/common/log.go
2025-09-12 14:43:54 +03:00

153 lines
3.2 KiB
Go

package common
import (
"context"
"fmt"
"log/slog"
"os"
"strings"
)
const (
LevelTrace = slog.Level(-8)
)
type contextHandler struct {
slog.Handler
}
func (h *contextHandler) Handle(ctx context.Context, r slog.Record) error {
if ctx != nil {
if tid, ok := ctx.Value(TraceIDContextKey).(string); ok && (len(tid) > 0) {
r.AddAttrs(TraceIDAttr(tid))
}
if sid, ok := ctx.Value(SessionIDContextKey).(string); ok && (len(sid) > 0) {
r.AddAttrs(SessionIDAttr(sid))
}
if svc, ok := ctx.Value(ServiceContextKey).(string); ok && (len(svc) > 0) {
r.AddAttrs(ServiceAttr(svc))
}
}
return h.Handler.Handle(ctx, r)
}
func (h *contextHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
return &contextHandler{h.Handler.WithAttrs(attrs)}
}
func (h *contextHandler) Enabled(ctx context.Context, level slog.Level) bool {
return h.Handler.Enabled(ctx, level)
}
func (h *contextHandler) WithGroup(name string) slog.Handler {
return &contextHandler{h.Handler.WithGroup(name)}
}
func TraceContextFunc(ctx context.Context, traceID func() string) (context.Context, string) {
tid, ok := ctx.Value(TraceIDContextKey).(string)
if !ok || (len(tid) == 0) {
tid = traceID()
}
return context.WithValue(ctx, TraceIDContextKey, tid), tid
}
func TraceContext(ctx context.Context, traceID string) context.Context {
if tid, ok := ctx.Value(TraceIDContextKey).(string); !ok || (len(tid) == 0) {
ctx = context.WithValue(ctx, TraceIDContextKey, traceID)
}
return ctx
}
func CopyTraceID(from context.Context, to context.Context) context.Context {
if tid, ok := from.Value(TraceIDContextKey).(string); ok && (len(tid) > 0) {
return context.WithValue(to, TraceIDContextKey, tid)
}
return to
}
func SetLogLevel(levelVar *slog.LevelVar, verbose bool) {
level := slog.LevelDebug
if verbose {
level = LevelTrace
}
levelVar.Set(level)
}
func SetupLogs(stage string, verbose bool) *slog.LevelVar {
levelVar := &slog.LevelVar{}
SetLogLevel(levelVar, verbose)
opts := &slog.HandlerOptions{
Level: levelVar,
}
handler := slog.NewJSONHandler(os.Stdout, opts)
ctxHandler := &contextHandler{handler}
logger := slog.New(ctxHandler)
slog.SetDefault(logger)
return levelVar
}
func SetupTraceLogs() {
opts := &slog.HandlerOptions{
Level: LevelTrace,
}
logger := slog.New(slog.NewJSONHandler(os.Stdout, opts))
slog.SetDefault(logger)
}
func FindHeader(headers map[string]string, header string) (string, bool) {
requestID, ok := headers[header]
if ok {
return requestID, ok
}
requestID, ok = headers[strings.ToLower(header)]
if ok {
return requestID, ok
}
return "", false
}
func ErrAttr(err error) slog.Attr {
return slog.Any("error", err)
}
func TraceIDAttr(tid string) slog.Attr {
return slog.Attr{
Key: "traceID",
Value: slog.StringValue(tid),
}
}
func SessionIDAttr(sid string) slog.Attr {
return slog.Attr{
Key: "sessID",
Value: slog.StringValue(sid),
}
}
func ServiceAttr(svc string) slog.Attr {
return slog.Attr{
Key: "service",
Value: slog.StringValue(svc),
}
}
type FmtLogger struct {
Ctx context.Context
Level slog.Level
}
func (l *FmtLogger) Printf(s string, args ...interface{}) {
msg := fmt.Sprintf(s, args...)
slog.Log(l.Ctx, l.Level, msg)
}