forked from fritzpay/paymentd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
handler.go
116 lines (103 loc) · 2.99 KB
/
handler.go
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package ext
import (
"sync"
"sync/atomic"
"unsafe"
log "gopkg.in/inconshreveable/log15.v2"
)
// EscalateErrHandler wraps another handler and passes all records through
// unchanged except if the logged context contains a non-nil error
// value in its context. In that case, the record's level is raised
// to LvlError unless it was already more serious (LvlCrit).
//
// This allows you to log the result of all functions for debugging
// and still capture error conditions when in production with a single
// log line. As an example, the following the log record will be written
// out only if there was an error writing a value to redis:
//
// logger := logext.EscalateErrHandler(
// log.LvlFilterHandler(log.LvlInfo, log.StdoutHandler))
//
// reply, err := redisConn.Do("SET", "foo", "bar")
// logger.Debug("Wrote value to redis", "reply", reply, "err", err)
// if err != nil {
// return err
// }
//
func EscalateErrHandler(h log.Handler) log.Handler {
return log.FuncHandler(func(r *log.Record) error {
if r.Lvl > log.LvlError {
for i := 1; i < len(r.Ctx); i++ {
if v, ok := r.Ctx[i].(error); ok && v != nil {
r.Lvl = log.LvlError
break
}
}
}
return h.Log(r)
})
}
// SpeculativeHandler is a handler for speculative logging. It
// keeps a ring buffer of the given size full of the last events
// logged into it. When Flush is called, all buffered log records
// are written to the wrapped handler. This is extremely for
// continuosly capturing debug level output, but only flushing those
// log records if an exceptional condition is encountered.
func SpeculativeHandler(size int, h log.Handler) *Speculative {
return &Speculative{
handler: h,
recs: make([]*log.Record, size),
}
}
type Speculative struct {
mu sync.Mutex
idx int
recs []*log.Record
handler log.Handler
full bool
}
func (h *Speculative) Log(r *log.Record) error {
h.mu.Lock()
defer h.mu.Unlock()
h.recs[h.idx] = r
h.idx = (h.idx + 1) % len(h.recs)
h.full = h.full || h.idx == 0
return nil
}
func (h *Speculative) Flush() {
recs := make([]*log.Record, 0)
func() {
h.mu.Lock()
defer h.mu.Unlock()
if h.full {
recs = append(recs, h.recs[h.idx:]...)
}
recs = append(recs, h.recs[:h.idx]...)
// reset state
h.full = false
h.idx = 0
}()
// don't hold the lock while we flush to the wrapped handler
for _, r := range recs {
h.handler.Log(r)
}
}
// HotSwapHandler wraps another handler that may swapped out
// dynamically at runtime in a thread-safe fashion.
// HotSwapHandler is the same functionality
// used to implement the SetHandler method for the default
// implementation of Logger.
func HotSwapHandler(h log.Handler) *HotSwap {
hs := new(HotSwap)
hs.Swap(h)
return hs
}
type HotSwap struct {
handler unsafe.Pointer
}
func (h *HotSwap) Log(r *log.Record) error {
return (*(*log.Handler)(atomic.LoadPointer(&h.handler))).Log(r)
}
func (h *HotSwap) Swap(newHandler log.Handler) {
atomic.StorePointer(&h.handler, unsafe.Pointer(&newHandler))
}