-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathlogger.go
159 lines (126 loc) · 3.46 KB
/
logger.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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package logg
import (
"fmt"
stdlog "log"
"time"
"github.com/bep/clocks"
)
// assert interface compliance.
var _ Logger = (*logger)(nil)
// String implements fmt.Stringer and can be used directly in
// the log methods.
type String string
// StringFunc is a function that returns a string.
// It also implements the fmt.Stringer interface and
// can therefore be used as argument to the log methods.
type StringFunc func() string
func (f StringFunc) String() string {
return f()
}
func (s String) String() string {
return string(s)
}
// Fielder is an interface for providing fields to custom types.
type Fielder interface {
Fields() Fields
}
func NewFieldsFunc(fn func() Fields) FieldsFunc {
return FieldsFunc(fn)
}
type FieldsFunc func() Fields
func (f FieldsFunc) Fields() Fields {
return f()
}
// Field holds a named value.
type Field struct {
Name string `json:"name"`
Value any `json:"value"`
}
// Fields represents a slice of entry level data used for structured logging.
type Fields []Field
// Fields implements Fielder.
func (f Fields) Fields() Fields {
return f
}
// Options is the set of options used to configure a logger.
type Options struct {
// Level is the minimum level to log at.
// If not set, defaults to InfoLevel.
Level Level
// Handler is the log handler to use.
Handler Handler
// Clock is the clock to use for timestamps.
// If not set, the system clock is used.
Clock Clock
}
// New returns a new logger.
func New(cfg Options) Logger {
if cfg.Handler == nil {
panic("handler cannot be nil")
}
if cfg.Level <= 0 || cfg.Level > LevelError {
panic("log level is out of range")
}
if cfg.Clock == nil {
cfg.Clock = clocks.System()
}
if cfg.Level == 0 {
cfg.Level = LevelInfo
}
return &logger{
Handler: cfg.Handler,
Level: cfg.Level,
Clock: cfg.Clock,
}
}
// logger represents a logger with configurable Level and Handler.
type logger struct {
Handler Handler
Level Level
Clock Clock
}
// Clock provides the current time.
type Clock interface {
Now() time.Time
}
// WithLevel returns a new entry with `level` set.
func (l *logger) WithLevel(level Level) *Entry {
return NewEntry(l).WithLevel(level)
}
// WithFields returns a new entry with `fields` set.
func (l *logger) WithFields(fields Fielder) *Entry {
return NewEntry(l).WithFields(fields.Fields())
}
// WithField returns a new entry with the `key` and `value` set.
//
// Note that the `key` should not have spaces in it - use camel
// case or underscores
func (l *logger) WithField(key string, value any) *Entry {
return NewEntry(l).WithField(key, value)
}
// WithDuration returns a new entry with the "duration" field set
// to the given duration in milliseconds.
func (l *logger) WithDuration(d time.Duration) *Entry {
return NewEntry(l).WithDuration(d)
}
// WithError returns a new entry with the "error" set to `err`.
func (l *logger) WithError(err error) *Entry {
return NewEntry(l).WithError(err)
}
// ErrStopLogEntry is a sentinel error that can be returned from a
// handler to stop the entry from being passed to the next handler.
var ErrStopLogEntry = fmt.Errorf("stop log entry")
// log the message, invoking the handler.
func (l *logger) log(e *Entry, s fmt.Stringer) {
if e.Level < l.Level {
return
}
finalized := objectPools.GetEntry()
defer objectPools.PutEntry(finalized)
e.finalize(finalized, s.String())
if err := l.Handler.HandleLog(finalized); err != nil {
if err != ErrStopLogEntry {
stdlog.Printf("error logging: %s", err)
}
}
}