forked from vulcand/vulcand
/
log.go
199 lines (168 loc) · 4.28 KB
/
log.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
package log
import (
"errors"
"fmt"
"os"
"runtime"
"strings"
"sync/atomic"
)
var loggers []Logger
var pid = os.Getpid()
var currentSeverity Severity
// Severity implementation is borrowed from glog, uses sync/atomic int32
type Severity int32
const (
SeverityInfo Severity = iota
SeverityWarn
SeverityError
SeverityFatal
)
var severityName = map[Severity]string{
SeverityInfo: "INFO",
SeverityWarn: "WARN",
SeverityError: "ERROR",
SeverityFatal: "FATAL",
}
// get returns the value of the severity.
func (s *Severity) Get() Severity {
return Severity(atomic.LoadInt32((*int32)(s)))
}
// set sets the value of the severity.
func (s *Severity) Set(val Severity) {
atomic.StoreInt32((*int32)(s), int32(val))
}
// less returns if this severity is greater than passed severity
func (s *Severity) Gt(val Severity) bool {
return s.Get() > val
}
func (s Severity) String() string {
n, ok := severityName[s]
if !ok {
return "UNKNOWN SEVERITY"
}
return n
}
func SeverityFromString(s string) (Severity, error) {
s = strings.ToUpper(s)
for k, val := range severityName {
if val == s {
return k, nil
}
}
return -1, fmt.Errorf("unsupported severity: %s", s)
}
// Logger is a unified interface for all loggers.
type Logger interface {
Info(string)
Warning(string)
Error(string)
Fatal(string)
}
type logger struct{}
// Logging configuration to be passed to all loggers during initialization.
type LogConfig struct {
Name string
}
// SetSeverity sets current logging severity. Acceptable values are SeverityInfo, SeverityWarn, SeverityError, SeverityFatal
func SetSeverity(s Severity) {
currentSeverity.Set(s)
}
// GetSeverity returns currently set severity.
func GetSeverity() Severity {
return currentSeverity
}
// Loggin initialization, must be called at the beginning of your cool app.
func Init(logConfigs []*LogConfig) error {
for _, config := range logConfigs {
logger, err := newLogger(config)
if err != nil {
return err
}
loggers = append(loggers, logger)
}
return nil
}
// Make a proper logger from a given configuration.
func newLogger(config *LogConfig) (Logger, error) {
switch config.Name {
case "console":
return NewConsoleLogger(config)
case "syslog":
return NewSysLogger(config)
}
return nil, errors.New(fmt.Sprintf("Unknown logger: %v", config))
}
// Infof logs to the INFO log.
func Infof(format string, args ...interface{}) {
if currentSeverity.Gt(SeverityInfo) {
return
}
message := makeMessage(currentSeverity, format, args...)
for _, logger := range loggers {
logger.Info(message)
}
}
// Warningf logs to the WARNING and INFO logs.
func Warningf(format string, args ...interface{}) {
if currentSeverity.Gt(SeverityWarn) {
return
}
message := makeMessage(currentSeverity, format, args...)
for _, logger := range loggers {
logger.Warning(message)
}
}
// Errorf logs to the ERROR, WARNING, and INFO logs.
func Errorf(format string, args ...interface{}) {
if currentSeverity.Gt(SeverityError) {
return
}
message := makeMessage(currentSeverity, format, args...)
for _, logger := range loggers {
logger.Error(message)
}
}
// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(255).
func Fatalf(format string, args ...interface{}) {
if currentSeverity.Gt(SeverityFatal) {
return
}
message := makeMessage(currentSeverity, format, args...)
stacks := stackTraces()
for _, logger := range loggers {
logger.Fatal(message)
logger.Fatal(stacks)
}
exit()
}
func makeMessage(sev Severity, format string, args ...interface{}) string {
file, line := callerInfo()
return fmt.Sprintf("PID:%d [%s:%d] %s", pid, file, line, fmt.Sprintf(format, args...))
}
// Return stack traces of all the running goroutines.
func stackTraces() string {
trace := make([]byte, 100000)
nbytes := runtime.Stack(trace, true)
return string(trace[:nbytes])
}
// Return a file name and a line number.
func callerInfo() (string, int) {
_, file, line, ok := runtimeCaller(3) // number of frames to the user's call.
if !ok {
file = "unknown"
line = 0
} else {
slashPosition := strings.LastIndex(file, "/")
if slashPosition >= 0 {
file = file[slashPosition+1:]
}
}
return file, line
}
// runtime functions for mocking
var runtimeCaller = runtime.Caller
var exit = func() {
os.Exit(255)
}