github.com/richardwilkes/toolbox@v1.121.0/errs/log.go (about) 1 // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved. 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, version 2.0. If a copy of the MPL was not distributed with 5 // this file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 // 7 // This Source Code Form is "Incompatible With Secondary Licenses", as 8 // defined by the Mozilla Public License, version 2.0. 9 10 package errs 11 12 import ( 13 "context" 14 "log/slog" 15 "strings" 16 "time" 17 ) 18 19 // StackTraceKey is the key used for logging the stack trace. 20 const StackTraceKey = "stack_trace" 21 22 // Log an error with a stack trace. 23 func Log(err error, args ...any) { 24 log(context.Background(), slog.LevelError, slog.Default(), WrapTyped(err), args...) 25 } 26 27 // LogContext logs an error with a stack trace. 28 func LogContext(ctx context.Context, err error, args ...any) { 29 log(ctx, slog.LevelError, slog.Default(), WrapTyped(err), args...) 30 } 31 32 // LogTo logs an error with a stack trace. 33 func LogTo(logger *slog.Logger, err error, args ...any) { 34 log(context.Background(), slog.LevelError, logger, WrapTyped(err), args...) 35 } 36 37 // LogContextTo logs an error with a stack trace. 38 func LogContextTo(ctx context.Context, logger *slog.Logger, err error, args ...any) { 39 log(ctx, slog.LevelError, logger, WrapTyped(err), args...) 40 } 41 42 // LogWithLevel logs an error with a stack trace. 43 func LogWithLevel(ctx context.Context, level slog.Level, logger *slog.Logger, err error, args ...any) { 44 log(ctx, level, logger, WrapTyped(err), args...) 45 } 46 47 func log(ctx context.Context, level slog.Level, logger *slog.Logger, err *Error, args ...any) { 48 if logger == nil { 49 logger = slog.Default() 50 } 51 if !logger.Enabled(ctx, level) { 52 return 53 } 54 r := createRecord(level, err) 55 r.Add(args...) 56 if ctx == nil { 57 ctx = context.Background() 58 } 59 _ = logger.Handler().Handle(ctx, r) //nolint:errcheck // Since we are in the logger, nothing we can reasonably do to log this 60 } 61 62 func createRecord(level slog.Level, err *Error) slog.Record { 63 var pc uintptr 64 var msg string 65 if err != nil { 66 msg = err.Message() 67 if len(err.stack) != 0 { 68 pc = err.stack[0] 69 } 70 } 71 r := slog.NewRecord(time.Now(), level, msg, pc) 72 if err != nil { 73 r.AddAttrs(slog.Any(StackTraceKey, &stackValue{err: err})) 74 } 75 return r 76 } 77 78 // LogAttrs logs an error with a stack trace. 79 func LogAttrs(err error, attrs ...slog.Attr) { 80 logAttrs(context.Background(), slog.LevelError, slog.Default(), WrapTyped(err), attrs...) 81 } 82 83 // LogAttrsContext logs an error with a stack trace. 84 func LogAttrsContext(ctx context.Context, err error, attrs ...slog.Attr) { 85 logAttrs(ctx, slog.LevelError, slog.Default(), WrapTyped(err), attrs...) 86 } 87 88 // LogAttrsTo logs an error with a stack trace. 89 func LogAttrsTo(logger *slog.Logger, err error, attrs ...slog.Attr) { 90 logAttrs(context.Background(), slog.LevelError, logger, WrapTyped(err), attrs...) 91 } 92 93 // LogAttrsContextTo logs an error with a stack trace. 94 func LogAttrsContextTo(ctx context.Context, logger *slog.Logger, err error, attrs ...slog.Attr) { 95 logAttrs(ctx, slog.LevelError, logger, WrapTyped(err), attrs...) 96 } 97 98 // LogAttrsWithLevel logs an error with a stack trace. 99 func LogAttrsWithLevel(ctx context.Context, level slog.Level, logger *slog.Logger, err error, attrs ...slog.Attr) { 100 logAttrs(ctx, level, logger, WrapTyped(err), attrs...) 101 } 102 103 func logAttrs(ctx context.Context, level slog.Level, logger *slog.Logger, err *Error, attrs ...slog.Attr) { 104 if logger == nil { 105 logger = slog.Default() 106 } 107 if !logger.Enabled(ctx, level) { 108 return 109 } 110 r := createRecord(level, err) 111 r.AddAttrs(attrs...) 112 if ctx == nil { 113 ctx = context.Background() 114 } 115 _ = logger.Handler().Handle(ctx, r) //nolint:errcheck // Since we are in the logger, nothing we can reasonably do to log this 116 } 117 118 type stackValue struct { 119 err StackError 120 } 121 122 func (v *stackValue) StackError() StackError { 123 return v.err 124 } 125 126 func (v *stackValue) LogValue() slog.Value { 127 stack := strings.Split(v.err.StackTrace(true), "\n") 128 for i := 0; i < len(stack); i++ { 129 stack[i] = strings.TrimSpace(stack[i]) 130 } 131 return slog.AnyValue(stack) 132 }