main
1// Copyright 2014-2022 Ulrich Kunitz. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package xlog provides a simple logging package that allows to disable
6// certain message categories. It defines a type, Logger, with multiple
7// methods for formatting output. The package has also a predefined
8// 'standard' Logger accessible through helper function Print[f|ln],
9// Fatal[f|ln], Panic[f|ln], Warn[f|ln], Print[f|ln] and Debug[f|ln]
10// that are easier to use then creating a Logger manually. That logger
11// writes to standard error and prints the date and time of each logged
12// message, which can be configured using the function SetFlags.
13//
14// The Fatal functions call os.Exit(1) after the message is output
15// unless not suppressed by the flags. The Panic functions call panic
16// after the writing the log message unless suppressed.
17package xlog
18
19import (
20 "fmt"
21 "io"
22 "os"
23 "runtime"
24 "sync"
25 "time"
26)
27
28// The flags define what information is prefixed to each log entry
29// generated by the Logger. The Lno* versions allow the suppression of
30// specific output. The bits are or'ed together to control what will be
31// printed. There is no control over the order of the items printed and
32// the format. The full format is:
33//
34// 2009-01-23 01:23:23.123123 /a/b/c/d.go:23: message
35const (
36 Ldate = 1 << iota // the date: 2009-01-23
37 Ltime // the time: 01:23:23
38 Lmicroseconds // microsecond resolution: 01:23:23.123123
39 Llongfile // full file name and line number: /a/b/c/d.go:23
40 Lshortfile // final file name element and line number: d.go:23
41 Lnopanic // suppresses output from Panic[f|ln] but not the panic call
42 Lnofatal // suppresses output from Fatal[f|ln] but not the exit
43 Lnowarn // suppresses output from Warn[f|ln]
44 Lnoprint // suppresses output from Print[f|ln]
45 Lnodebug // suppresses output from Debug[f|ln]
46 // initial values for the standard logger
47 Lstdflags = Ldate | Ltime | Lnodebug
48)
49
50// A Logger represents an active logging object that generates lines of
51// output to an io.Writer. Each logging operation if not suppressed
52// makes a single call to the Writer's Write method. A Logger can be
53// used simultaneously from multiple goroutines; it guarantees to
54// serialize access to the Writer.
55type Logger struct {
56 mu sync.Mutex // ensures atomic writes; and protects the following
57 // fields
58 prefix string // prefix to write at beginning of each line
59 flag int // properties
60 out io.Writer // destination for output
61 buf []byte // for accumulating text to write
62}
63
64// New creates a new Logger. The out argument sets the destination to
65// which the log output will be written. The prefix appears at the
66// beginning of each log line. The flag argument defines the logging
67// properties.
68func New(out io.Writer, prefix string, flag int) *Logger {
69 return &Logger{out: out, prefix: prefix, flag: flag}
70}
71
72// std is the standard logger used by the package scope functions.
73var std = New(os.Stderr, "", Lstdflags)
74
75// itoa converts the integer to ASCII. A negative widths will avoid
76// zero-padding. The function supports only non-negative integers.
77func itoa(buf *[]byte, i int, wid int) {
78 var u = uint(i)
79 if u == 0 && wid <= 1 {
80 *buf = append(*buf, '0')
81 return
82 }
83 var b [32]byte
84 bp := len(b)
85 for ; u > 0 || wid > 0; u /= 10 {
86 bp--
87 wid--
88 b[bp] = byte(u%10) + '0'
89 }
90 *buf = append(*buf, b[bp:]...)
91}
92
93// formatHeader puts the header into the buf field of the buffer.
94func (l *Logger) formatHeader(t time.Time, file string, line int) {
95 l.buf = append(l.buf, l.prefix...)
96 if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
97 if l.flag&Ldate != 0 {
98 year, month, day := t.Date()
99 itoa(&l.buf, year, 4)
100 l.buf = append(l.buf, '-')
101 itoa(&l.buf, int(month), 2)
102 l.buf = append(l.buf, '-')
103 itoa(&l.buf, day, 2)
104 l.buf = append(l.buf, ' ')
105 }
106 if l.flag&(Ltime|Lmicroseconds) != 0 {
107 hour, min, sec := t.Clock()
108 itoa(&l.buf, hour, 2)
109 l.buf = append(l.buf, ':')
110 itoa(&l.buf, min, 2)
111 l.buf = append(l.buf, ':')
112 itoa(&l.buf, sec, 2)
113 if l.flag&Lmicroseconds != 0 {
114 l.buf = append(l.buf, '.')
115 itoa(&l.buf, t.Nanosecond()/1e3, 6)
116 }
117 l.buf = append(l.buf, ' ')
118 }
119 }
120 if l.flag&(Lshortfile|Llongfile) != 0 {
121 if l.flag&Lshortfile != 0 {
122 short := file
123 for i := len(file) - 1; i > 0; i-- {
124 if file[i] == '/' {
125 short = file[i+1:]
126 break
127 }
128 }
129 file = short
130 }
131 l.buf = append(l.buf, file...)
132 l.buf = append(l.buf, ':')
133 itoa(&l.buf, line, -1)
134 l.buf = append(l.buf, ": "...)
135 }
136}
137
138func (l *Logger) output(calldepth int, now time.Time, s string) error {
139 var file string
140 var line int
141 if l.flag&(Lshortfile|Llongfile) != 0 {
142 l.mu.Unlock()
143 var ok bool
144 _, file, line, ok = runtime.Caller(calldepth)
145 if !ok {
146 file = "???"
147 line = 0
148 }
149 l.mu.Lock()
150 }
151 l.buf = l.buf[:0]
152 l.formatHeader(now, file, line)
153 l.buf = append(l.buf, s...)
154 if len(s) == 0 || s[len(s)-1] != '\n' {
155 l.buf = append(l.buf, '\n')
156 }
157 _, err := l.out.Write(l.buf)
158 return err
159}
160
161// Output writes the string s with the header controlled by the flags to
162// the l.out writer. A newline will be appended if s doesn't end in a
163// newline. Calldepth is used to recover the PC, although all current
164// calls of Output use the call depth 2. Access to the function is serialized.
165func (l *Logger) Output(calldepth, noflag int, v ...interface{}) error {
166 now := time.Now()
167 l.mu.Lock()
168 defer l.mu.Unlock()
169 if l.flag&noflag != 0 {
170 return nil
171 }
172 s := fmt.Sprint(v...)
173 return l.output(calldepth+1, now, s)
174}
175
176// Outputf works like output but formats the output like Printf.
177func (l *Logger) Outputf(calldepth int, noflag int, format string, v ...interface{}) error {
178 now := time.Now()
179 l.mu.Lock()
180 defer l.mu.Unlock()
181 if l.flag&noflag != 0 {
182 return nil
183 }
184 s := fmt.Sprintf(format, v...)
185 return l.output(calldepth+1, now, s)
186}
187
188// Outputln works like output but formats the output like Println.
189func (l *Logger) Outputln(calldepth int, noflag int, v ...interface{}) error {
190 now := time.Now()
191 l.mu.Lock()
192 defer l.mu.Unlock()
193 if l.flag&noflag != 0 {
194 return nil
195 }
196 s := fmt.Sprintln(v...)
197 return l.output(calldepth+1, now, s)
198}
199
200// Panic prints the message like Print and calls panic. The printing
201// might be suppressed by the flag Lnopanic.
202func (l *Logger) Panic(v ...interface{}) {
203 l.Output(2, Lnopanic, v...)
204 s := fmt.Sprint(v...)
205 panic(s)
206}
207
208// Panic prints the message like Print and calls panic. The printing
209// might be suppressed by the flag Lnopanic.
210func Panic(v ...interface{}) {
211 std.Output(2, Lnopanic, v...)
212 s := fmt.Sprint(v...)
213 panic(s)
214}
215
216// Panicf prints the message like Printf and calls panic. The printing
217// might be suppressed by the flag Lnopanic.
218func (l *Logger) Panicf(format string, v ...interface{}) {
219 l.Outputf(2, Lnopanic, format, v...)
220 s := fmt.Sprintf(format, v...)
221 panic(s)
222}
223
224// Panicf prints the message like Printf and calls panic. The printing
225// might be suppressed by the flag Lnopanic.
226func Panicf(format string, v ...interface{}) {
227 std.Outputf(2, Lnopanic, format, v...)
228 s := fmt.Sprintf(format, v...)
229 panic(s)
230}
231
232// Panicln prints the message like Println and calls panic. The printing
233// might be suppressed by the flag Lnopanic.
234func (l *Logger) Panicln(v ...interface{}) {
235 l.Outputln(2, Lnopanic, v...)
236 s := fmt.Sprintln(v...)
237 panic(s)
238}
239
240// Panicln prints the message like Println and calls panic. The printing
241// might be suppressed by the flag Lnopanic.
242func Panicln(v ...interface{}) {
243 std.Outputln(2, Lnopanic, v...)
244 s := fmt.Sprintln(v...)
245 panic(s)
246}
247
248// Fatal prints the message like Print and calls os.Exit(1). The
249// printing might be suppressed by the flag Lnofatal.
250func (l *Logger) Fatal(v ...interface{}) {
251 l.Output(2, Lnofatal, v...)
252 os.Exit(1)
253}
254
255// Fatal prints the message like Print and calls os.Exit(1). The
256// printing might be suppressed by the flag Lnofatal.
257func Fatal(v ...interface{}) {
258 std.Output(2, Lnofatal, v...)
259 os.Exit(1)
260}
261
262// Fatalf prints the message like Printf and calls os.Exit(1). The
263// printing might be suppressed by the flag Lnofatal.
264func (l *Logger) Fatalf(format string, v ...interface{}) {
265 l.Outputf(2, Lnofatal, format, v...)
266 os.Exit(1)
267}
268
269// Fatalf prints the message like Printf and calls os.Exit(1). The
270// printing might be suppressed by the flag Lnofatal.
271func Fatalf(format string, v ...interface{}) {
272 std.Outputf(2, Lnofatal, format, v...)
273 os.Exit(1)
274}
275
276// Fatalln prints the message like Println and calls os.Exit(1). The
277// printing might be suppressed by the flag Lnofatal.
278func (l *Logger) Fatalln(format string, v ...interface{}) {
279 l.Outputln(2, Lnofatal, v...)
280 os.Exit(1)
281}
282
283// Fatalln prints the message like Println and calls os.Exit(1). The
284// printing might be suppressed by the flag Lnofatal.
285func Fatalln(format string, v ...interface{}) {
286 std.Outputln(2, Lnofatal, v...)
287 os.Exit(1)
288}
289
290// Warn prints the message like Print. The printing might be suppressed
291// by the flag Lnowarn.
292func (l *Logger) Warn(v ...interface{}) {
293 l.Output(2, Lnowarn, v...)
294}
295
296// Warn prints the message like Print. The printing might be suppressed
297// by the flag Lnowarn.
298func Warn(v ...interface{}) {
299 std.Output(2, Lnowarn, v...)
300}
301
302// Warnf prints the message like Printf. The printing might be suppressed
303// by the flag Lnowarn.
304func (l *Logger) Warnf(format string, v ...interface{}) {
305 l.Outputf(2, Lnowarn, format, v...)
306}
307
308// Warnf prints the message like Printf. The printing might be suppressed
309// by the flag Lnowarn.
310func Warnf(format string, v ...interface{}) {
311 std.Outputf(2, Lnowarn, format, v...)
312}
313
314// Warnln prints the message like Println. The printing might be suppressed
315// by the flag Lnowarn.
316func (l *Logger) Warnln(v ...interface{}) {
317 l.Outputln(2, Lnowarn, v...)
318}
319
320// Warnln prints the message like Println. The printing might be suppressed
321// by the flag Lnowarn.
322func Warnln(v ...interface{}) {
323 std.Outputln(2, Lnowarn, v...)
324}
325
326// Print prints the message like fmt.Print. The printing might be suppressed
327// by the flag Lnoprint.
328func (l *Logger) Print(v ...interface{}) {
329 l.Output(2, Lnoprint, v...)
330}
331
332// Print prints the message like fmt.Print. The printing might be suppressed
333// by the flag Lnoprint.
334func Print(v ...interface{}) {
335 std.Output(2, Lnoprint, v...)
336}
337
338// Printf prints the message like fmt.Printf. The printing might be suppressed
339// by the flag Lnoprint.
340func (l *Logger) Printf(format string, v ...interface{}) {
341 l.Outputf(2, Lnoprint, format, v...)
342}
343
344// Printf prints the message like fmt.Printf. The printing might be suppressed
345// by the flag Lnoprint.
346func Printf(format string, v ...interface{}) {
347 std.Outputf(2, Lnoprint, format, v...)
348}
349
350// Println prints the message like fmt.Println. The printing might be
351// suppressed by the flag Lnoprint.
352func (l *Logger) Println(v ...interface{}) {
353 l.Outputln(2, Lnoprint, v...)
354}
355
356// Println prints the message like fmt.Println. The printing might be
357// suppressed by the flag Lnoprint.
358func Println(v ...interface{}) {
359 std.Outputln(2, Lnoprint, v...)
360}
361
362// Debug prints the message like Print. The printing might be suppressed
363// by the flag Lnodebug.
364func (l *Logger) Debug(v ...interface{}) {
365 l.Output(2, Lnodebug, v...)
366}
367
368// Debug prints the message like Print. The printing might be suppressed
369// by the flag Lnodebug.
370func Debug(v ...interface{}) {
371 std.Output(2, Lnodebug, v...)
372}
373
374// Debugf prints the message like Printf. The printing might be suppressed
375// by the flag Lnodebug.
376func (l *Logger) Debugf(format string, v ...interface{}) {
377 l.Outputf(2, Lnodebug, format, v...)
378}
379
380// Debugf prints the message like Printf. The printing might be suppressed
381// by the flag Lnodebug.
382func Debugf(format string, v ...interface{}) {
383 std.Outputf(2, Lnodebug, format, v...)
384}
385
386// Debugln prints the message like Println. The printing might be suppressed
387// by the flag Lnodebug.
388func (l *Logger) Debugln(v ...interface{}) {
389 l.Outputln(2, Lnodebug, v...)
390}
391
392// Debugln prints the message like Println. The printing might be suppressed
393// by the flag Lnodebug.
394func Debugln(v ...interface{}) {
395 std.Outputln(2, Lnodebug, v...)
396}
397
398// Flags returns the current flags used by the logger.
399func (l *Logger) Flags() int {
400 l.mu.Lock()
401 defer l.mu.Unlock()
402 return l.flag
403}
404
405// Flags returns the current flags used by the standard logger.
406func Flags() int {
407 return std.Flags()
408}
409
410// SetFlags sets the flags of the logger.
411func (l *Logger) SetFlags(flag int) {
412 l.mu.Lock()
413 defer l.mu.Unlock()
414 l.flag = flag
415}
416
417// SetFlags sets the flags for the standard logger.
418func SetFlags(flag int) {
419 std.SetFlags(flag)
420}
421
422// Prefix returns the prefix used by the logger.
423func (l *Logger) Prefix() string {
424 l.mu.Lock()
425 defer l.mu.Unlock()
426 return l.prefix
427}
428
429// Prefix returns the prefix used by the standard logger of the package.
430func Prefix() string {
431 return std.Prefix()
432}
433
434// SetPrefix sets the prefix for the logger.
435func (l *Logger) SetPrefix(prefix string) {
436 l.mu.Lock()
437 defer l.mu.Unlock()
438 l.prefix = prefix
439}
440
441// SetPrefix sets the prefix of the standard logger of the package.
442func SetPrefix(prefix string) {
443 std.SetPrefix(prefix)
444}
445
446// SetOutput sets the output of the logger.
447func (l *Logger) SetOutput(w io.Writer) {
448 l.mu.Lock()
449 defer l.mu.Unlock()
450 l.out = w
451}
452
453// SetOutput sets the output for the standard logger of the package.
454func SetOutput(w io.Writer) {
455 std.SetOutput(w)
456}