forked from op/go-logging
-
Notifications
You must be signed in to change notification settings - Fork 1
/
logger.go
213 lines (180 loc) · 5.75 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
// Copyright 2013, Örjan Persson. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package logging implements a logging infrastructure for Go. It supports
// different logging backends like syslog, file and memory. Multiple backends
// can be utilized with different log levels per backend and logger.
package logging
import (
"bytes"
"fmt"
"log"
"os"
"strings"
"sync/atomic"
"time"
)
// Redactor is an interface for types that may contain sensitive information
// (like passwords), which shouldn't be printed to the log. The idea was found
// in relog as part of the vitness project.
type Redactor interface {
Redacted() interface{}
}
// Redact returns a string of * having the same length as s.
func Redact(s string) string {
return strings.Repeat("*", len(s))
}
var (
// Sequence number is incremented and utilized for all log records created.
sequenceNo uint64
// timeNow is a customizable for testing purposes.
timeNow = time.Now
)
// Record represents a log record and contains the timestamp when the record
// was created, an increasing id, filename and line and finally the actual
// formatted log line.
type Record struct {
Id uint64
Time time.Time
Module string
Level Level
Args []interface{}
// message is kept as a pointer to have shallow copies update this once
// needed.
message *string
fmt string
formatter Formatter
formatted string
}
func (r *Record) Formatted(calldepth int) string {
if r.formatted == "" {
var buf bytes.Buffer
r.formatter.Format(calldepth+1, r, &buf)
r.formatted = buf.String()
}
return r.formatted
}
func (r *Record) Message() string {
if r.message == nil {
// Redact the arguments that implements the Redactor interface
for i, arg := range r.Args {
if redactor, ok := arg.(Redactor); ok == true {
r.Args[i] = redactor.Redacted()
}
}
msg := fmt.Sprintf(r.fmt, r.Args...)
r.message = &msg
}
return *r.message
}
type Logger struct {
Module string
}
// TODO call NewLogger and remove MustGetLogger?
// GetLogger creates and returns a Logger object based on the module name.
func GetLogger(module string) (*Logger, error) {
return &Logger{Module: module}, nil
}
// MustGetLogger is like GetLogger but panics if the logger can't be created.
// It simplifies safe initialization of a global logger for eg. a package.
func MustGetLogger(module string) *Logger {
logger, err := GetLogger(module)
if err != nil {
panic("logger: " + module + ": " + err.Error())
}
return logger
}
// Reset restores the internal state of the logging library.
func Reset() {
// TODO make a global Init() method to be less magic? or make it such that
// if there's no backends at all configured, we could use some tricks to
// automatically setup backends based if we have a TTY or not.
sequenceNo = 0
b := SetBackend(NewLogBackend(os.Stderr, "", log.LstdFlags))
b.SetLevel(DEBUG, "")
SetFormatter(DefaultFormatter)
timeNow = time.Now
}
// InitForTesting is a convenient method when using logging in a test. Once
// called, the time will be frozen to January 1, 1970 UTC.
func InitForTesting(level Level) *MemoryBackend {
Reset()
memoryBackend := NewMemoryBackend(10240)
leveledBackend := AddModuleLevel(memoryBackend)
leveledBackend.SetLevel(level, "")
SetBackend(leveledBackend)
timeNow = func() time.Time {
return time.Unix(0, 0).UTC()
}
return memoryBackend
}
// IsEnabledFor returns true if the logger is enabled for the given level.
func (l *Logger) IsEnabledFor(level Level) bool {
return defaultBackend.IsEnabledFor(level, l.Module)
}
func (l *Logger) log(lvl Level, format string, args ...interface{}) {
// Create the logging record and pass it in to the backend
record := &Record{
Id: atomic.AddUint64(&sequenceNo, 1),
Time: timeNow(),
Module: l.Module,
Level: lvl,
fmt: format,
Args: args,
}
// TODO use channels to fan out the records to all backends?
// TODO in case of errors, do something (tricky)
// calldepth=2 brings the stack up to the caller of the level
// methods, Info(), Fatal(), etc.
defaultBackend.Log(lvl, 2, record)
}
// Fatal is equivalent to l.Critical(fmt.Sprint()) followed by a call to os.Exit(1).
func (l *Logger) Fatal(args ...interface{}) {
s := fmt.Sprint(args...)
l.log(CRITICAL, "%s", s)
os.Exit(1)
}
// Fatalf is equivalent to l.Critical followed by a call to os.Exit(1).
func (l *Logger) Fatalf(format string, args ...interface{}) {
l.log(CRITICAL, format, args...)
os.Exit(1)
}
// Panic is equivalent to l.Critical(fmt.Sprint()) followed by a call to panic().
func (l *Logger) Panic(args ...interface{}) {
s := fmt.Sprint(args...)
l.log(CRITICAL, "%s", s)
panic(s)
}
// Panicf is equivalent to l.Critical followed by a call to panic().
func (l *Logger) Panicf(format string, args ...interface{}) {
s := fmt.Sprintf(format, args...)
l.log(CRITICAL, "%s", s)
panic(s)
}
// Critical logs a message using CRITICAL as log level.
func (l *Logger) Critical(format string, args ...interface{}) {
l.log(CRITICAL, format, args...)
}
// Error logs a message using ERROR as log level.
func (l *Logger) Error(format string, args ...interface{}) {
l.log(ERROR, format, args...)
}
// Warning logs a message using WARNING as log level.
func (l *Logger) Warning(format string, args ...interface{}) {
l.log(WARNING, format, args...)
}
// Notice logs a message using NOTICE as log level.
func (l *Logger) Notice(format string, args ...interface{}) {
l.log(NOTICE, format, args...)
}
// Info logs a message using INFO as log level.
func (l *Logger) Info(format string, args ...interface{}) {
l.log(INFO, format, args...)
}
// Debug logs a message using DEBUG as log level.
func (l *Logger) Debug(format string, args ...interface{}) {
l.log(DEBUG, format, args...)
}
func init() {
Reset()
}