File size: 2,128 Bytes
fea99b3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package log

import (
	"fmt"
	"log/slog"
	"os"
	"runtime/debug"
)

// OTelCodeStackTrace a stacktrace as a string in the natural representation for the language runtime.
// The representation is to be determined and documented by each language SIG.
// See: https://github.com/open-telemetry/semantic-conventions/blob/v1.29.0/docs/attributes-registry/code.md
const OTelCodeStackTrace = "code.stacktrace"

type propagationStrategy int8

const (
	// propagationStrategyPanic will propagate the panic after logging it
	propagationStrategyRePanic propagationStrategy = iota

	// propagationStrategyExit will exit the program after logging the panic
	propagationStrategyExit

	// propagationStrategyContinue will continue the program after logging the panic
	propagationStrategyContinue
)

type panicLoggerOptions struct {
	propagationStrategy propagationStrategy
}

// WithRePanic is an option to set the propagation strategy to propagate the panic after logging it
func WithRePanic(o *panicLoggerOptions) {
	o.propagationStrategy = propagationStrategyRePanic
}

// WithExit is an option to set the propagation strategy to exit the program after logging the panic
func WithExit(o *panicLoggerOptions) {
	o.propagationStrategy = propagationStrategyExit
}

// WithContinue is an option to set the propagation strategy to continue the program after logging the panic
func WithContinue(o *panicLoggerOptions) {
	o.propagationStrategy = propagationStrategyContinue
}

// PanicLogger is a function that logs panics and then propagates the failure based on the propagationStrategy setting
// Usage (in main):
//
//	defer log.PanicLogger(log.WithExit)
func PanicLogger(options ...func(*panicLoggerOptions)) {
	opts := &panicLoggerOptions{}

	for _, o := range options {
		o(opts)
	}

	if r := recover(); r != nil {
		description := fmt.Sprintf("panic: %s", r)

		slog.Error(description, OTelCodeStackTrace, string(debug.Stack()))

		switch opts.propagationStrategy {
		case propagationStrategyExit:
			os.Exit(1)
		case propagationStrategyContinue:
			return
		case propagationStrategyRePanic:
			fallthrough
		default:
			panic(r)
		}
	}
}