forked from influxdata/telegraf
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlogger.go
305 lines (259 loc) · 7.76 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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
package logger
import (
"errors"
"fmt"
"io"
"log"
"strings"
"sync"
"time"
"github.com/influxdata/telegraf"
)
// Central handler for the logs used by the logger to actually output the logs.
// This is necessary to be able to dynamically switch the sink even though
// plugins already instantiated a logger _before_ the final sink is set up.
var (
instance *handler // handler for the actual output
once sync.Once // once token to initialize the handler only once
)
// sink interface that has to be implemented by a logging sink
type sink interface {
Print(telegraf.LogLevel, time.Time, string, map[string]interface{}, ...interface{})
}
// logger is the actual implementation of the telegraf logger interface
type logger struct {
level *telegraf.LogLevel
category string
name string
alias string
prefix string
onError []func()
attributes map[string]interface{}
}
// New creates a new logging instance to be used in models
func New(category, name, alias string) *logger {
l := &logger{
category: category,
name: name,
alias: alias,
attributes: map[string]interface{}{"category": category, "plugin": name},
}
if alias != "" {
l.attributes["alias"] = alias
}
// Format the prefix
l.prefix = l.category
if l.prefix != "" && l.name != "" {
l.prefix += "."
}
l.prefix += l.name
if l.prefix != "" && l.alias != "" {
l.prefix += "::"
}
l.prefix += l.alias
if l.prefix != "" {
l.prefix = "[" + l.prefix + "] "
}
return l
}
// Level returns the current log-level of the logger
func (l *logger) Level() telegraf.LogLevel {
if l.level != nil {
return *l.level
}
return instance.level
}
// AddAttribute allows to add a key-value attribute to the logging output
func (l *logger) AddAttribute(key string, value interface{}) {
// Do not allow to overwrite general keys
switch key {
case "category", "plugin", "alias":
default:
l.attributes[key] = value
}
}
// Error logging including callbacks
func (l *logger) Errorf(format string, args ...interface{}) {
l.Error(fmt.Sprintf(format, args...))
}
func (l *logger) Error(args ...interface{}) {
l.Print(telegraf.Error, time.Now(), args...)
for _, f := range l.onError {
f()
}
}
// Warning logging
func (l *logger) Warnf(format string, args ...interface{}) {
l.Warn(fmt.Sprintf(format, args...))
}
func (l *logger) Warn(args ...interface{}) {
l.Print(telegraf.Warn, time.Now(), args...)
}
// Info logging
func (l *logger) Infof(format string, args ...interface{}) {
l.Info(fmt.Sprintf(format, args...))
}
func (l *logger) Info(args ...interface{}) {
l.Print(telegraf.Info, time.Now(), args...)
}
// Debug logging, this is suppressed on console
func (l *logger) Debugf(format string, args ...interface{}) {
l.Debug(fmt.Sprintf(format, args...))
}
func (l *logger) Debug(args ...interface{}) {
l.Print(telegraf.Debug, time.Now(), args...)
}
// Trace logging, this is suppressed on console
func (l *logger) Tracef(format string, args ...interface{}) {
l.Trace(fmt.Sprintf(format, args...))
}
func (l *logger) Trace(args ...interface{}) {
l.Print(telegraf.Trace, time.Now(), args...)
}
func (l *logger) Print(level telegraf.LogLevel, ts time.Time, args ...interface{}) {
// Check if we are in early logging state and store the message in this case
if instance.impl == nil {
instance.add(level, ts, l.prefix, l.attributes, args...)
}
// Skip all messages with insufficient log-levels
if l.level != nil && !l.level.Includes(level) || l.level == nil && !instance.level.Includes(level) {
return
}
if instance.impl != nil {
instance.impl.Print(level, ts.In(instance.timezone), l.prefix, l.attributes, args...)
} else {
msg := append([]interface{}{ts.In(instance.timezone).Format(time.RFC3339), " ", level.Indicator(), " ", l.prefix}, args...)
instance.earlysink.Print(msg...)
}
}
// SetLevel overrides the current log-level of the logger
func (l *logger) SetLevel(level telegraf.LogLevel) {
l.level = &level
}
// SetLevel changes the log-level to the given one
func (l *logger) SetLogLevel(name string) error {
if name == "" {
return nil
}
level := telegraf.LogLevelFromString(name)
if level == telegraf.None {
return fmt.Errorf("invalid log-level %q", name)
}
l.SetLevel(level)
return nil
}
// Register a callback triggered when errors are about to be written to the log
func (l *logger) RegisterErrorCallback(f func()) {
l.onError = append(l.onError, f)
}
type Config struct {
// will set the log level to DEBUG
Debug bool
// will set the log level to ERROR
Quiet bool
// format and target of log messages
LogTarget string
LogFormat string
Logfile string
// will rotate when current file at the specified time interval
RotationInterval time.Duration
// will rotate when current file size exceeds this parameter.
RotationMaxSize int64
// maximum rotated files to keep (older ones will be deleted)
RotationMaxArchives int
// pick a timezone to use when logging. or type 'local' for local time.
LogWithTimezone string
// Logger instance name
InstanceName string
// Structured logging message key
StructuredLogMessageKey string
// internal log-level
logLevel telegraf.LogLevel
}
// SetupLogging configures the logging output.
func SetupLogging(cfg *Config) error {
// Issue deprecation warning for option
switch cfg.LogTarget {
case "":
// Best-case no target set or file already migrated...
case "stderr":
msg := "Agent setting %q is deprecated, please leave %q empty and remove this setting!"
deprecation := "The setting will be removed in v1.40.0."
log.Printf("W! "+msg+" "+deprecation, "logtarget", "logfile")
cfg.Logfile = ""
case "file":
msg := "Agent setting %q is deprecated, please just set %q and remove this setting!"
deprecation := "The setting will be removed in v1.40.0."
log.Printf("W! "+msg+" "+deprecation, "logtarget", "logfile")
case "eventlog":
msg := "Agent setting %q is deprecated, please set %q to %q and remove this setting!"
deprecation := "The setting will be removed in v1.40.0."
log.Printf("W! "+msg+" "+deprecation, "logtarget", "logformat", "eventlog")
if cfg.LogFormat != "" && cfg.LogFormat != "eventlog" {
return errors.New("contradicting setting between 'logtarget' and 'logformat'")
}
cfg.LogFormat = "eventlog"
default:
return fmt.Errorf("invalid deprecated 'logtarget' setting %q", cfg.LogTarget)
}
if cfg.LogFormat == "" {
cfg.LogFormat = "text"
}
if cfg.Debug {
cfg.logLevel = telegraf.Debug
}
if cfg.Quiet {
cfg.logLevel = telegraf.Error
}
if !cfg.Debug && !cfg.Quiet {
cfg.logLevel = telegraf.Info
}
if cfg.InstanceName == "" {
cfg.InstanceName = "telegraf"
}
if cfg.LogFormat == "" {
cfg.LogFormat = "text"
}
// Get configured timezone
timezoneName := cfg.LogWithTimezone
if strings.EqualFold(timezoneName, "local") {
timezoneName = "Local"
}
tz, err := time.LoadLocation(timezoneName)
if err != nil {
return fmt.Errorf("setting logging timezone failed: %w", err)
}
// Get the logging factory and create the root instance
creator, found := registry[cfg.LogFormat]
if !found {
return fmt.Errorf("unsupported log-format: %s", cfg.LogFormat)
}
l, err := creator(cfg)
if err != nil {
return err
}
// Close the previous logger if possible
if err := CloseLogging(); err != nil {
return err
}
// Update the logging instance
skipEarlyLogs := cfg.LogFormat == "text" && cfg.Logfile == ""
instance.switchSink(l, cfg.logLevel, tz, skipEarlyLogs)
return nil
}
func RedirectLogging(w io.Writer) {
instance = redirectHandler(w)
}
func CloseLogging() error {
return instance.close()
}
func init() {
once.Do(func() {
// Create a special logging instance that additionally buffers all
// messages logged before the final logger is up.
instance = defaultHandler()
// Redirect the standard logger output to our logger instance
log.SetFlags(0)
log.SetOutput(&stdlogRedirector{})
})
}