-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathfactory.go
220 lines (184 loc) · 4.86 KB
/
factory.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
package log
import (
"encoding/json"
"fmt"
"os"
"runtime"
"strings"
"time"
"github.com/sirupsen/logrus"
"github.com/x-cray/logrus-prefixed-formatter"
"golang.org/x/crypto/ssh/terminal"
)
const (
// DebugLevel stands for debug logging level.
DebugLevel = "debug"
// InfoLevel stands for info logging level (default).
InfoLevel = "info"
// WarningLevel stands for warning logging level.
WarningLevel = "warning"
// ErrorLevel stands for error logging level.
ErrorLevel = "error"
// disabled is a special level used only when we are in test
disabledLevel = "panic"
// TextFormat stands for text logging format.
TextFormat = "text"
// JSONFormat stands for json logging format.
JSONFormat = "json"
// FluentdFormat stands for fluentd that can be parsed by Kubernetes and Google Container Engine.
FluentdFormat = "fluentd"
// DefaultLevel is the level used by LoggerFactory when Level is omitted.
DefaultLevel = InfoLevel
// DefaultFormat is the format used by LoggerFactory when Format is omitted.
DefaultFormat = TextFormat
)
var (
validLevels = map[string]bool{
InfoLevel: true, DebugLevel: true, WarningLevel: true, ErrorLevel: true,
disabledLevel: true,
}
validFormats = map[string]bool{
TextFormat: true, JSONFormat: true, FluentdFormat: true,
}
)
// LoggerFactory is a logger factory used to instanciate new Loggers, from
// string configuration, mainly coming from console flags.
type LoggerFactory struct {
// Level as string, values are "info", "debug", "warning" or "error".
Level string
// Format as string, values are "text", "json" or "fluentnd", by default "text" is used.
// when a terminal is not detected "json" is used instead.
Format string
// Fields in JSON or fluentd format to be used by configured in the new Logger.
Fields string
// ForceFormat if true the fact of being in a terminal or not is ignored.
ForceFormat bool
}
// New returns a new logger based on the LoggerFactory values.
func (f *LoggerFactory) New(fields Fields) (Logger, error) {
l := logrus.New()
if err := f.setLevel(l); err != nil {
return nil, err
}
f.setHook(l)
if err := f.setFormat(l); err != nil {
return nil, err
}
return f.setFields(l, fields)
}
// ApplyToLogrus configures the standard logrus Logger with the LoggerFactory
// values. Useful to propagate the configuration to third-party libraries using
// logrus.
func (f *LoggerFactory) ApplyToLogrus() error {
std := logrus.StandardLogger()
if err := f.setLevel(std); err != nil {
return err
}
f.setHook(std)
return f.setFormat(std)
}
func (f *LoggerFactory) setLevel(l *logrus.Logger) error {
if err := f.setDefaultLevel(); err != nil {
return err
}
level, err := logrus.ParseLevel(f.Level)
if err != nil {
return err
}
l.Level = level
return nil
}
func (f *LoggerFactory) setDefaultLevel() error {
if f.Level == "" {
f.Level = DefaultLevel
}
f.Level = strings.ToLower(f.Level)
if validLevels[f.Level] {
return nil
}
return fmt.Errorf(
"invalid level %s, valid levels are: %v",
f.Level, getKeysFromMap(validLevels),
)
}
func (f *LoggerFactory) setFormat(l *logrus.Logger) error {
if err := f.setDefaultFormat(); err != nil {
return err
}
switch f.Format {
case "text":
f := new(prefixed.TextFormatter)
f.ForceColors = runtime.GOOS != "windows"
f.FullTimestamp = true
f.TimestampFormat = time.RFC3339Nano
l.Formatter = f
case "json":
f := new(logrus.JSONFormatter)
f.TimestampFormat = time.RFC3339Nano
l.Formatter = f
case "fluentd":
f := new(logrus.JSONFormatter)
f.FieldMap = logrus.FieldMap{
logrus.FieldKeyTime: "timestamp",
logrus.FieldKeyLevel: "severity",
logrus.FieldKeyMsg: "message",
}
l.Formatter = f
}
return nil
}
func (f *LoggerFactory) setDefaultFormat() error {
if f.Format == "" {
f.Format = DefaultFormat
}
f.Format = strings.ToLower(f.Format)
if validFormats[f.Format] {
return nil
}
if !f.ForceFormat && isTerminal() {
f.Format = JSONFormat
}
return fmt.Errorf(
"invalid format %s, valid formats are: %v",
f.Format, getKeysFromMap(validFormats),
)
}
func (f *LoggerFactory) setHook(l *logrus.Logger) {
if f.Level == DebugLevel {
l.AddHook(newFilenameHook(
logrus.DebugLevel,
logrus.InfoLevel,
logrus.WarnLevel,
logrus.ErrorLevel,
logrus.PanicLevel),
)
}
}
func (f *LoggerFactory) setFields(l *logrus.Logger, fields Fields) (Logger, error) {
var envFields logrus.Fields
if f.Fields != "" {
if err := json.Unmarshal([]byte(f.Fields), &envFields); err != nil {
return nil, err
}
}
if envFields == nil {
envFields = make(logrus.Fields, 0)
}
if fields != nil {
for k, v := range fields {
envFields[k] = v
}
}
e := l.WithFields(envFields)
return &logger{*e}, nil
}
func getKeysFromMap(m map[string]bool) []string {
var keys []string
for k := range m {
keys = append(keys, k)
}
return keys
}
func isTerminal() bool {
return terminal.IsTerminal(int(os.Stdout.Fd()))
}