458 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			458 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
| // Copyright 2014-2019 Ulrich Kunitz. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| // Package xlog provides a simple logging package that allows to disable
 | |
| // certain message categories. It defines a type, Logger, with multiple
 | |
| // methods for formatting output. The package has also a predefined
 | |
| // 'standard' Logger accessible through helper function Print[f|ln],
 | |
| // Fatal[f|ln], Panic[f|ln], Warn[f|ln], Print[f|ln] and Debug[f|ln]
 | |
| // that are easier to use then creating a Logger manually. That logger
 | |
| // writes to standard error and prints the date and time of each logged
 | |
| // message, which can be configured using the function SetFlags.
 | |
| //
 | |
| // The Fatal functions call os.Exit(1) after the message is output
 | |
| // unless not suppressed by the flags. The Panic functions call panic
 | |
| // after the writing the log message unless suppressed.
 | |
| package xlog
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"runtime"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| // The flags define what information is prefixed to each log entry
 | |
| // generated by the Logger. The Lno* versions allow the suppression of
 | |
| // specific output. The bits are or'ed together to control what will be
 | |
| // printed. There is no control over the order of the items printed and
 | |
| // the format. The full format is:
 | |
| //
 | |
| //   2009-01-23 01:23:23.123123 /a/b/c/d.go:23: message
 | |
| //
 | |
| const (
 | |
| 	Ldate         = 1 << iota // the date: 2009-01-23
 | |
| 	Ltime                     // the time: 01:23:23
 | |
| 	Lmicroseconds             // microsecond resolution: 01:23:23.123123
 | |
| 	Llongfile                 // full file name and line number: /a/b/c/d.go:23
 | |
| 	Lshortfile                // final file name element and line number: d.go:23
 | |
| 	Lnopanic                  // suppresses output from Panic[f|ln] but not the panic call
 | |
| 	Lnofatal                  // suppresses output from Fatal[f|ln] but not the exit
 | |
| 	Lnowarn                   // suppresses output from Warn[f|ln]
 | |
| 	Lnoprint                  // suppresses output from Print[f|ln]
 | |
| 	Lnodebug                  // suppresses output from Debug[f|ln]
 | |
| 	// initial values for the standard logger
 | |
| 	Lstdflags = Ldate | Ltime | Lnodebug
 | |
| )
 | |
| 
 | |
| // A Logger represents an active logging object that generates lines of
 | |
| // output to an io.Writer. Each logging operation if not suppressed
 | |
| // makes a single call to the Writer's Write method. A Logger can be
 | |
| // used simultaneously from multiple goroutines; it guarantees to
 | |
| // serialize access to the Writer.
 | |
| type Logger struct {
 | |
| 	mu sync.Mutex // ensures atomic writes; and protects the following
 | |
| 	// fields
 | |
| 	prefix string    // prefix to write at beginning of each line
 | |
| 	flag   int       // properties
 | |
| 	out    io.Writer // destination for output
 | |
| 	buf    []byte    // for accumulating text to write
 | |
| }
 | |
| 
 | |
| // New creates a new Logger. The out argument sets the destination to
 | |
| // which the log output will be written. The prefix appears at the
 | |
| // beginning of each log line. The flag argument defines the logging
 | |
| // properties.
 | |
| func New(out io.Writer, prefix string, flag int) *Logger {
 | |
| 	return &Logger{out: out, prefix: prefix, flag: flag}
 | |
| }
 | |
| 
 | |
| // std is the standard logger used by the package scope functions.
 | |
| var std = New(os.Stderr, "", Lstdflags)
 | |
| 
 | |
| // itoa converts the integer to ASCII. A negative widths will avoid
 | |
| // zero-padding. The function supports only non-negative integers.
 | |
| func itoa(buf *[]byte, i int, wid int) {
 | |
| 	var u = uint(i)
 | |
| 	if u == 0 && wid <= 1 {
 | |
| 		*buf = append(*buf, '0')
 | |
| 		return
 | |
| 	}
 | |
| 	var b [32]byte
 | |
| 	bp := len(b)
 | |
| 	for ; u > 0 || wid > 0; u /= 10 {
 | |
| 		bp--
 | |
| 		wid--
 | |
| 		b[bp] = byte(u%10) + '0'
 | |
| 	}
 | |
| 	*buf = append(*buf, b[bp:]...)
 | |
| }
 | |
| 
 | |
| // formatHeader puts the header into the buf field of the buffer.
 | |
| func (l *Logger) formatHeader(t time.Time, file string, line int) {
 | |
| 	l.buf = append(l.buf, l.prefix...)
 | |
| 	if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
 | |
| 		if l.flag&Ldate != 0 {
 | |
| 			year, month, day := t.Date()
 | |
| 			itoa(&l.buf, year, 4)
 | |
| 			l.buf = append(l.buf, '-')
 | |
| 			itoa(&l.buf, int(month), 2)
 | |
| 			l.buf = append(l.buf, '-')
 | |
| 			itoa(&l.buf, day, 2)
 | |
| 			l.buf = append(l.buf, ' ')
 | |
| 		}
 | |
| 		if l.flag&(Ltime|Lmicroseconds) != 0 {
 | |
| 			hour, min, sec := t.Clock()
 | |
| 			itoa(&l.buf, hour, 2)
 | |
| 			l.buf = append(l.buf, ':')
 | |
| 			itoa(&l.buf, min, 2)
 | |
| 			l.buf = append(l.buf, ':')
 | |
| 			itoa(&l.buf, sec, 2)
 | |
| 			if l.flag&Lmicroseconds != 0 {
 | |
| 				l.buf = append(l.buf, '.')
 | |
| 				itoa(&l.buf, t.Nanosecond()/1e3, 6)
 | |
| 			}
 | |
| 			l.buf = append(l.buf, ' ')
 | |
| 		}
 | |
| 	}
 | |
| 	if l.flag&(Lshortfile|Llongfile) != 0 {
 | |
| 		if l.flag&Lshortfile != 0 {
 | |
| 			short := file
 | |
| 			for i := len(file) - 1; i > 0; i-- {
 | |
| 				if file[i] == '/' {
 | |
| 					short = file[i+1:]
 | |
| 					break
 | |
| 				}
 | |
| 			}
 | |
| 			file = short
 | |
| 		}
 | |
| 		l.buf = append(l.buf, file...)
 | |
| 		l.buf = append(l.buf, ':')
 | |
| 		itoa(&l.buf, line, -1)
 | |
| 		l.buf = append(l.buf, ": "...)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (l *Logger) output(calldepth int, now time.Time, s string) error {
 | |
| 	var file string
 | |
| 	var line int
 | |
| 	if l.flag&(Lshortfile|Llongfile) != 0 {
 | |
| 		l.mu.Unlock()
 | |
| 		var ok bool
 | |
| 		_, file, line, ok = runtime.Caller(calldepth)
 | |
| 		if !ok {
 | |
| 			file = "???"
 | |
| 			line = 0
 | |
| 		}
 | |
| 		l.mu.Lock()
 | |
| 	}
 | |
| 	l.buf = l.buf[:0]
 | |
| 	l.formatHeader(now, file, line)
 | |
| 	l.buf = append(l.buf, s...)
 | |
| 	if len(s) == 0 || s[len(s)-1] != '\n' {
 | |
| 		l.buf = append(l.buf, '\n')
 | |
| 	}
 | |
| 	_, err := l.out.Write(l.buf)
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // Output writes the string s with the header controlled by the flags to
 | |
| // the l.out writer. A newline will be appended if s doesn't end in a
 | |
| // newline. Calldepth is used to recover the PC, although all current
 | |
| // calls of Output use the call depth 2. Access to the function is serialized.
 | |
| func (l *Logger) Output(calldepth, noflag int, v ...interface{}) error {
 | |
| 	now := time.Now()
 | |
| 	l.mu.Lock()
 | |
| 	defer l.mu.Unlock()
 | |
| 	if l.flag&noflag != 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 	s := fmt.Sprint(v...)
 | |
| 	return l.output(calldepth+1, now, s)
 | |
| }
 | |
| 
 | |
| // Outputf works like output but formats the output like Printf.
 | |
| func (l *Logger) Outputf(calldepth int, noflag int, format string, v ...interface{}) error {
 | |
| 	now := time.Now()
 | |
| 	l.mu.Lock()
 | |
| 	defer l.mu.Unlock()
 | |
| 	if l.flag&noflag != 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 	s := fmt.Sprintf(format, v...)
 | |
| 	return l.output(calldepth+1, now, s)
 | |
| }
 | |
| 
 | |
| // Outputln works like output but formats the output like Println.
 | |
| func (l *Logger) Outputln(calldepth int, noflag int, v ...interface{}) error {
 | |
| 	now := time.Now()
 | |
| 	l.mu.Lock()
 | |
| 	defer l.mu.Unlock()
 | |
| 	if l.flag&noflag != 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 	s := fmt.Sprintln(v...)
 | |
| 	return l.output(calldepth+1, now, s)
 | |
| }
 | |
| 
 | |
| // Panic prints the message like Print and calls panic. The printing
 | |
| // might be suppressed by the flag Lnopanic.
 | |
| func (l *Logger) Panic(v ...interface{}) {
 | |
| 	l.Output(2, Lnopanic, v...)
 | |
| 	s := fmt.Sprint(v...)
 | |
| 	panic(s)
 | |
| }
 | |
| 
 | |
| // Panic prints the message like Print and calls panic. The printing
 | |
| // might be suppressed by the flag Lnopanic.
 | |
| func Panic(v ...interface{}) {
 | |
| 	std.Output(2, Lnopanic, v...)
 | |
| 	s := fmt.Sprint(v...)
 | |
| 	panic(s)
 | |
| }
 | |
| 
 | |
| // Panicf prints the message like Printf and calls panic. The printing
 | |
| // might be suppressed by the flag Lnopanic.
 | |
| func (l *Logger) Panicf(format string, v ...interface{}) {
 | |
| 	l.Outputf(2, Lnopanic, format, v...)
 | |
| 	s := fmt.Sprintf(format, v...)
 | |
| 	panic(s)
 | |
| }
 | |
| 
 | |
| // Panicf prints the message like Printf and calls panic. The printing
 | |
| // might be suppressed by the flag Lnopanic.
 | |
| func Panicf(format string, v ...interface{}) {
 | |
| 	std.Outputf(2, Lnopanic, format, v...)
 | |
| 	s := fmt.Sprintf(format, v...)
 | |
| 	panic(s)
 | |
| }
 | |
| 
 | |
| // Panicln prints the message like Println and calls panic. The printing
 | |
| // might be suppressed by the flag Lnopanic.
 | |
| func (l *Logger) Panicln(v ...interface{}) {
 | |
| 	l.Outputln(2, Lnopanic, v...)
 | |
| 	s := fmt.Sprintln(v...)
 | |
| 	panic(s)
 | |
| }
 | |
| 
 | |
| // Panicln prints the message like Println and calls panic. The printing
 | |
| // might be suppressed by the flag Lnopanic.
 | |
| func Panicln(v ...interface{}) {
 | |
| 	std.Outputln(2, Lnopanic, v...)
 | |
| 	s := fmt.Sprintln(v...)
 | |
| 	panic(s)
 | |
| }
 | |
| 
 | |
| // Fatal prints the message like Print and calls os.Exit(1). The
 | |
| // printing might be suppressed by the flag Lnofatal.
 | |
| func (l *Logger) Fatal(v ...interface{}) {
 | |
| 	l.Output(2, Lnofatal, v...)
 | |
| 	os.Exit(1)
 | |
| }
 | |
| 
 | |
| // Fatal prints the message like Print and calls os.Exit(1). The
 | |
| // printing might be suppressed by the flag Lnofatal.
 | |
| func Fatal(v ...interface{}) {
 | |
| 	std.Output(2, Lnofatal, v...)
 | |
| 	os.Exit(1)
 | |
| }
 | |
| 
 | |
| // Fatalf prints the message like Printf and calls os.Exit(1). The
 | |
| // printing might be suppressed by the flag Lnofatal.
 | |
| func (l *Logger) Fatalf(format string, v ...interface{}) {
 | |
| 	l.Outputf(2, Lnofatal, format, v...)
 | |
| 	os.Exit(1)
 | |
| }
 | |
| 
 | |
| // Fatalf prints the message like Printf and calls os.Exit(1). The
 | |
| // printing might be suppressed by the flag Lnofatal.
 | |
| func Fatalf(format string, v ...interface{}) {
 | |
| 	std.Outputf(2, Lnofatal, format, v...)
 | |
| 	os.Exit(1)
 | |
| }
 | |
| 
 | |
| // Fatalln prints the message like Println and calls os.Exit(1). The
 | |
| // printing might be suppressed by the flag Lnofatal.
 | |
| func (l *Logger) Fatalln(format string, v ...interface{}) {
 | |
| 	l.Outputln(2, Lnofatal, v...)
 | |
| 	os.Exit(1)
 | |
| }
 | |
| 
 | |
| // Fatalln prints the message like Println and calls os.Exit(1). The
 | |
| // printing might be suppressed by the flag Lnofatal.
 | |
| func Fatalln(format string, v ...interface{}) {
 | |
| 	std.Outputln(2, Lnofatal, v...)
 | |
| 	os.Exit(1)
 | |
| }
 | |
| 
 | |
| // Warn prints the message like Print. The printing might be suppressed
 | |
| // by the flag Lnowarn.
 | |
| func (l *Logger) Warn(v ...interface{}) {
 | |
| 	l.Output(2, Lnowarn, v...)
 | |
| }
 | |
| 
 | |
| // Warn prints the message like Print. The printing might be suppressed
 | |
| // by the flag Lnowarn.
 | |
| func Warn(v ...interface{}) {
 | |
| 	std.Output(2, Lnowarn, v...)
 | |
| }
 | |
| 
 | |
| // Warnf prints the message like Printf. The printing might be suppressed
 | |
| // by the flag Lnowarn.
 | |
| func (l *Logger) Warnf(format string, v ...interface{}) {
 | |
| 	l.Outputf(2, Lnowarn, format, v...)
 | |
| }
 | |
| 
 | |
| // Warnf prints the message like Printf. The printing might be suppressed
 | |
| // by the flag Lnowarn.
 | |
| func Warnf(format string, v ...interface{}) {
 | |
| 	std.Outputf(2, Lnowarn, format, v...)
 | |
| }
 | |
| 
 | |
| // Warnln prints the message like Println. The printing might be suppressed
 | |
| // by the flag Lnowarn.
 | |
| func (l *Logger) Warnln(v ...interface{}) {
 | |
| 	l.Outputln(2, Lnowarn, v...)
 | |
| }
 | |
| 
 | |
| // Warnln prints the message like Println. The printing might be suppressed
 | |
| // by the flag Lnowarn.
 | |
| func Warnln(v ...interface{}) {
 | |
| 	std.Outputln(2, Lnowarn, v...)
 | |
| }
 | |
| 
 | |
| // Print prints the message like fmt.Print. The printing might be suppressed
 | |
| // by the flag Lnoprint.
 | |
| func (l *Logger) Print(v ...interface{}) {
 | |
| 	l.Output(2, Lnoprint, v...)
 | |
| }
 | |
| 
 | |
| // Print prints the message like fmt.Print. The printing might be suppressed
 | |
| // by the flag Lnoprint.
 | |
| func Print(v ...interface{}) {
 | |
| 	std.Output(2, Lnoprint, v...)
 | |
| }
 | |
| 
 | |
| // Printf prints the message like fmt.Printf. The printing might be suppressed
 | |
| // by the flag Lnoprint.
 | |
| func (l *Logger) Printf(format string, v ...interface{}) {
 | |
| 	l.Outputf(2, Lnoprint, format, v...)
 | |
| }
 | |
| 
 | |
| // Printf prints the message like fmt.Printf. The printing might be suppressed
 | |
| // by the flag Lnoprint.
 | |
| func Printf(format string, v ...interface{}) {
 | |
| 	std.Outputf(2, Lnoprint, format, v...)
 | |
| }
 | |
| 
 | |
| // Println prints the message like fmt.Println. The printing might be
 | |
| // suppressed by the flag Lnoprint.
 | |
| func (l *Logger) Println(v ...interface{}) {
 | |
| 	l.Outputln(2, Lnoprint, v...)
 | |
| }
 | |
| 
 | |
| // Println prints the message like fmt.Println. The printing might be
 | |
| // suppressed by the flag Lnoprint.
 | |
| func Println(v ...interface{}) {
 | |
| 	std.Outputln(2, Lnoprint, v...)
 | |
| }
 | |
| 
 | |
| // Debug prints the message like Print. The printing might be suppressed
 | |
| // by the flag Lnodebug.
 | |
| func (l *Logger) Debug(v ...interface{}) {
 | |
| 	l.Output(2, Lnodebug, v...)
 | |
| }
 | |
| 
 | |
| // Debug prints the message like Print. The printing might be suppressed
 | |
| // by the flag Lnodebug.
 | |
| func Debug(v ...interface{}) {
 | |
| 	std.Output(2, Lnodebug, v...)
 | |
| }
 | |
| 
 | |
| // Debugf prints the message like Printf. The printing might be suppressed
 | |
| // by the flag Lnodebug.
 | |
| func (l *Logger) Debugf(format string, v ...interface{}) {
 | |
| 	l.Outputf(2, Lnodebug, format, v...)
 | |
| }
 | |
| 
 | |
| // Debugf prints the message like Printf. The printing might be suppressed
 | |
| // by the flag Lnodebug.
 | |
| func Debugf(format string, v ...interface{}) {
 | |
| 	std.Outputf(2, Lnodebug, format, v...)
 | |
| }
 | |
| 
 | |
| // Debugln prints the message like Println. The printing might be suppressed
 | |
| // by the flag Lnodebug.
 | |
| func (l *Logger) Debugln(v ...interface{}) {
 | |
| 	l.Outputln(2, Lnodebug, v...)
 | |
| }
 | |
| 
 | |
| // Debugln prints the message like Println. The printing might be suppressed
 | |
| // by the flag Lnodebug.
 | |
| func Debugln(v ...interface{}) {
 | |
| 	std.Outputln(2, Lnodebug, v...)
 | |
| }
 | |
| 
 | |
| // Flags returns the current flags used by the logger.
 | |
| func (l *Logger) Flags() int {
 | |
| 	l.mu.Lock()
 | |
| 	defer l.mu.Unlock()
 | |
| 	return l.flag
 | |
| }
 | |
| 
 | |
| // Flags returns the current flags used by the standard logger.
 | |
| func Flags() int {
 | |
| 	return std.Flags()
 | |
| }
 | |
| 
 | |
| // SetFlags sets the flags of the logger.
 | |
| func (l *Logger) SetFlags(flag int) {
 | |
| 	l.mu.Lock()
 | |
| 	defer l.mu.Unlock()
 | |
| 	l.flag = flag
 | |
| }
 | |
| 
 | |
| // SetFlags sets the flags for the standard logger.
 | |
| func SetFlags(flag int) {
 | |
| 	std.SetFlags(flag)
 | |
| }
 | |
| 
 | |
| // Prefix returns the prefix used by the logger.
 | |
| func (l *Logger) Prefix() string {
 | |
| 	l.mu.Lock()
 | |
| 	defer l.mu.Unlock()
 | |
| 	return l.prefix
 | |
| }
 | |
| 
 | |
| // Prefix returns the prefix used by the standard logger of the package.
 | |
| func Prefix() string {
 | |
| 	return std.Prefix()
 | |
| }
 | |
| 
 | |
| // SetPrefix sets the prefix for the logger.
 | |
| func (l *Logger) SetPrefix(prefix string) {
 | |
| 	l.mu.Lock()
 | |
| 	defer l.mu.Unlock()
 | |
| 	l.prefix = prefix
 | |
| }
 | |
| 
 | |
| // SetPrefix sets the prefix of the standard logger of the package.
 | |
| func SetPrefix(prefix string) {
 | |
| 	std.SetPrefix(prefix)
 | |
| }
 | |
| 
 | |
| // SetOutput sets the output of the logger.
 | |
| func (l *Logger) SetOutput(w io.Writer) {
 | |
| 	l.mu.Lock()
 | |
| 	defer l.mu.Unlock()
 | |
| 	l.out = w
 | |
| }
 | |
| 
 | |
| // SetOutput sets the output for the standard logger of the package.
 | |
| func SetOutput(w io.Writer) {
 | |
| 	std.SetOutput(w)
 | |
| }
 |