-
Notifications
You must be signed in to change notification settings - Fork 0
/
core.go
144 lines (121 loc) · 3.11 KB
/
core.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package zapappender
import (
"sync"
"go.uber.org/zap/zapcore"
)
var _ zapcore.Core = &AppenderCore{}
// Appender is the interface for composable appenders.
//
// The Write method receives the zapcore.Entry in addition to the text buffer.
// This allows appenders access to the fields like Time.
//
// Several variants of the interface were analyzed.
// 1. Write with p, ent, fields
// 2. Write with p, ent
// 3. Write with p and a subset of ent
// A. Append with enc, ent, fields
//
// Decision: variant 2 - thus variant 3 would also be an option.
// - we cannot keep the fields in an async process
// - they might hold references that might be already mutated or hinder GC
// - without fields, we cannot use the Encoder to encode the message
type Appender interface {
// Write
// must not retain p
Write(p []byte, ent zapcore.Entry) (n int, err error)
// Sync flushes buffered logs (if any).
Sync() error
}
type SynchronizationAware interface {
Synchronized() bool
}
type SynchronizationAwareAppender interface {
Appender
SynchronizationAware
}
func Synchronized(s interface{}) bool {
if s, ok := s.(SynchronizationAware); ok && s.Synchronized() {
return true
}
return false
}
var _ SynchronizationAwareAppender = &Synchronizing{}
type Synchronizing struct {
primary Appender
mutex sync.Mutex
}
func NewSynchronizing(inner Appender) Appender {
if inner == nil {
return nil
}
if Synchronized(inner) {
// already synchronizing
return inner
}
return &Synchronizing{
primary: inner,
}
}
func (s *Synchronizing) Write(p []byte, ent zapcore.Entry) (n int, err error) {
s.mutex.Lock()
defer s.mutex.Unlock()
return s.primary.Write(p, ent)
}
func (s *Synchronizing) Sync() error {
//TODO: should we lock Sync?
return s.primary.Sync()
}
func (s *Synchronizing) Synchronized() bool {
return true
}
var _ zapcore.Core = &AppenderCore{}
// AppenderCore bridges between zapcore and zapappender.
type AppenderCore struct {
zapcore.LevelEnabler
enc zapcore.Encoder
appender Appender
}
func NewAppenderCore(enc zapcore.Encoder, appender Appender, enab zapcore.LevelEnabler) *AppenderCore {
return &AppenderCore{
LevelEnabler: enab,
enc: enc,
appender: NewSynchronizing(appender),
}
}
func (c *AppenderCore) With(fields []zapcore.Field) zapcore.Core {
enc := c.enc.Clone()
for i := range fields {
fields[i].AddTo(enc)
}
return &AppenderCore{
LevelEnabler: c.LevelEnabler,
appender: c.appender,
enc: enc,
}
}
func (c *AppenderCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
if c.Enabled(ent.Level) {
return ce.AddCore(ent, c)
}
return ce
}
func (c *AppenderCore) Write(ent zapcore.Entry, fields []zapcore.Field) error {
buf, err := c.enc.EncodeEntry(ent, fields)
if err != nil {
return err
}
_, err = c.appender.Write(buf.Bytes(), ent)
buf.Free()
if err != nil {
return err
}
if ent.Level > zapcore.ErrorLevel {
// Since we may be crashing the program, sync the output. Ignore Sync
// errors, pending a clean solution to issue #370.
_ = c.Sync()
}
return nil
}
func (c *AppenderCore) Sync() error {
return c.appender.Sync()
}