1 package log
2
3 import (
4 "encoding/base64"
5 "encoding/binary"
6 "encoding/json"
7 "errors"
8 "fmt"
9 "hash/crc32"
10 "io"
11 "log/syslog"
12 "os"
13 "strings"
14 "sync"
15
16 "github.com/jmhodges/clock"
17 "golang.org/x/term"
18
19 "github.com/letsencrypt/boulder/core"
20 )
21
22
23
24
25
26 type Logger interface {
27 Err(msg string)
28 Errf(format string, a ...interface{})
29 Warning(msg string)
30 Warningf(format string, a ...interface{})
31 Info(msg string)
32 Infof(format string, a ...interface{})
33 InfoObject(string, interface{})
34 Debug(msg string)
35 Debugf(format string, a ...interface{})
36 AuditInfo(msg string)
37 AuditInfof(format string, a ...interface{})
38 AuditObject(string, interface{})
39 AuditErr(string)
40 AuditErrf(format string, a ...interface{})
41 }
42
43
44 type impl struct {
45 w writer
46 }
47
48
49 type singleton struct {
50 once sync.Once
51 log Logger
52 }
53
54
55 var _Singleton singleton
56
57
58 const auditTag = "[AUDIT]"
59
60
61
62 func New(log *syslog.Writer, stdoutLogLevel int, syslogLogLevel int) (Logger, error) {
63 if log == nil {
64 return nil, errors.New("Attempted to use a nil System Logger")
65 }
66 return &impl{
67 &bothWriter{
68 sync.Mutex{},
69 log,
70 newStdoutWriter(stdoutLogLevel),
71 syslogLogLevel,
72 },
73 }, nil
74 }
75
76
77
78 func StdoutLogger(level int) Logger {
79 return &impl{newStdoutWriter(level)}
80 }
81
82 func newStdoutWriter(level int) *stdoutWriter {
83 prefix, clkFormat := getPrefix()
84 return &stdoutWriter{
85 prefix: prefix,
86 level: level,
87 clkFormat: clkFormat,
88 clk: clock.New(),
89 stdout: os.Stdout,
90 stderr: os.Stderr,
91 isatty: term.IsTerminal(int(os.Stdout.Fd())),
92 }
93 }
94
95
96
97 func initialize() {
98 const defaultPriority = syslog.LOG_INFO | syslog.LOG_LOCAL0
99 syslogger, err := syslog.Dial("", "", defaultPriority, "test")
100 if err != nil {
101 panic(err)
102 }
103 logger, err := New(syslogger, int(syslog.LOG_DEBUG), int(syslog.LOG_DEBUG))
104 if err != nil {
105 panic(err)
106 }
107
108 _ = Set(logger)
109 }
110
111
112
113
114 func Set(logger Logger) (err error) {
115 if _Singleton.log != nil {
116 err = errors.New("You may not call Set after it has already been implicitly or explicitly set")
117 _Singleton.log.Warning(err.Error())
118 } else {
119 _Singleton.log = logger
120 }
121 return
122 }
123
124
125
126
127
128 func Get() Logger {
129 _Singleton.once.Do(func() {
130 if _Singleton.log == nil {
131 initialize()
132 }
133 })
134
135 return _Singleton.log
136 }
137
138 type writer interface {
139 logAtLevel(syslog.Priority, string, ...interface{})
140 }
141
142
143 type bothWriter struct {
144 sync.Mutex
145 *syslog.Writer
146 *stdoutWriter
147 syslogLevel int
148 }
149
150
151 type stdoutWriter struct {
152
153
154 prefix string
155 level int
156 clkFormat string
157 clk clock.Clock
158 stdout io.Writer
159 stderr io.Writer
160 isatty bool
161 }
162
163 func LogLineChecksum(line string) string {
164 crc := crc32.ChecksumIEEE([]byte(line))
165
166
167 buf := make([]byte, binary.MaxVarintLen32)
168 binary.PutUvarint(buf, uint64(crc))
169 return base64.RawURLEncoding.EncodeToString(buf)
170 }
171
172 func checkSummed(msg string) string {
173 return fmt.Sprintf("%s %s", LogLineChecksum(msg), msg)
174 }
175
176
177
178 func (w *bothWriter) logAtLevel(level syslog.Priority, msg string, a ...interface{}) {
179 var err error
180
181
182 if a != nil {
183 msg = fmt.Sprintf(msg, a...)
184 }
185
186
187
188 msg = strings.Replace(msg, "\n", "\\n", -1)
189
190 w.Lock()
191 defer w.Unlock()
192
193 switch syslogAllowed := int(level) <= w.syslogLevel; level {
194 case syslog.LOG_ERR:
195 if syslogAllowed {
196 err = w.Err(checkSummed(msg))
197 }
198 case syslog.LOG_WARNING:
199 if syslogAllowed {
200 err = w.Warning(checkSummed(msg))
201 }
202 case syslog.LOG_INFO:
203 if syslogAllowed {
204 err = w.Info(checkSummed(msg))
205 }
206 case syslog.LOG_DEBUG:
207 if syslogAllowed {
208 err = w.Debug(checkSummed(msg))
209 }
210 default:
211 err = w.Err(fmt.Sprintf("%s (unknown logging level: %d)", checkSummed(msg), int(level)))
212 }
213
214 if err != nil {
215 fmt.Fprintf(os.Stderr, "Failed to write to syslog: %d %s (%s)\n", int(level), checkSummed(msg), err)
216 }
217
218 w.stdoutWriter.logAtLevel(level, msg)
219 }
220
221
222 func (w *stdoutWriter) logAtLevel(level syslog.Priority, msg string, a ...interface{}) {
223 if int(level) <= w.level {
224 output := w.stdout
225 if int(level) <= int(syslog.LOG_WARNING) {
226 output = w.stderr
227 }
228
229
230 if a != nil {
231 msg = fmt.Sprintf(msg, a...)
232 }
233
234 msg = strings.Replace(msg, "\n", "\\n", -1)
235
236 var color string
237 var reset string
238
239 const red = "\033[31m\033[1m"
240 const yellow = "\033[33m"
241 const gray = "\033[37m\033[2m"
242
243 if w.isatty {
244 if int(level) == int(syslog.LOG_DEBUG) {
245 color = gray
246 reset = "\033[0m"
247 } else if int(level) == int(syslog.LOG_WARNING) {
248 color = yellow
249 reset = "\033[0m"
250 } else if int(level) <= int(syslog.LOG_ERR) {
251 color = red
252 reset = "\033[0m"
253 }
254 }
255
256 if _, err := fmt.Fprintf(output, "%s%s %s%d %s %s%s\n",
257 color,
258 w.clk.Now().UTC().Format(w.clkFormat),
259 w.prefix,
260 int(level),
261 core.Command(),
262 checkSummed(msg),
263 reset); err != nil {
264 panic(fmt.Sprintf("failed to write to stdout: %v\n", err))
265 }
266 }
267 }
268
269 func (log *impl) auditAtLevel(level syslog.Priority, msg string, a ...interface{}) {
270 msg = fmt.Sprintf("%s %s", auditTag, msg)
271 log.w.logAtLevel(level, msg, a...)
272 }
273
274
275
276 func (log *impl) Err(msg string) {
277 log.Errf(msg)
278 }
279
280
281
282 func (log *impl) Errf(format string, a ...interface{}) {
283 log.auditAtLevel(syslog.LOG_ERR, format, a...)
284 }
285
286
287 func (log *impl) Warning(msg string) {
288 log.Warningf(msg)
289 }
290
291
292 func (log *impl) Warningf(format string, a ...interface{}) {
293 log.w.logAtLevel(syslog.LOG_WARNING, format, a...)
294 }
295
296
297 func (log *impl) Info(msg string) {
298 log.Infof(msg)
299 }
300
301
302 func (log *impl) Infof(format string, a ...interface{}) {
303 log.w.logAtLevel(syslog.LOG_INFO, format, a...)
304 }
305
306
307 func (log *impl) InfoObject(msg string, obj interface{}) {
308 jsonObj, err := json.Marshal(obj)
309 if err != nil {
310 log.auditAtLevel(syslog.LOG_ERR, fmt.Sprintf("Object for msg %q could not be serialized to JSON. Raw: %+v", msg, obj))
311 return
312 }
313
314 log.Infof("%s JSON=%s", msg, jsonObj)
315 }
316
317
318 func (log *impl) Debug(msg string) {
319 log.Debugf(msg)
320
321 }
322
323
324 func (log *impl) Debugf(format string, a ...interface{}) {
325 log.w.logAtLevel(syslog.LOG_DEBUG, format, a...)
326 }
327
328
329
330 func (log *impl) AuditInfo(msg string) {
331 log.AuditInfof(msg)
332 }
333
334
335
336 func (log *impl) AuditInfof(format string, a ...interface{}) {
337 log.auditAtLevel(syslog.LOG_INFO, format, a...)
338 }
339
340
341
342 func (log *impl) AuditObject(msg string, obj interface{}) {
343 jsonObj, err := json.Marshal(obj)
344 if err != nil {
345 log.auditAtLevel(syslog.LOG_ERR, fmt.Sprintf("Object for msg %q could not be serialized to JSON. Raw: %+v", msg, obj))
346 return
347 }
348
349 log.auditAtLevel(syslog.LOG_INFO, fmt.Sprintf("%s JSON=%s", msg, jsonObj))
350 }
351
352
353 func (log *impl) AuditErr(msg string) {
354 log.AuditErrf(msg)
355 }
356
357
358 func (log *impl) AuditErrf(format string, a ...interface{}) {
359 log.auditAtLevel(syslog.LOG_ERR, format, a...)
360 }
361
View as plain text