nightingale/vendor/github.com/toolkits/pkg/logger/logger.go

429 lines
8.5 KiB
Go

package logger
import (
"bytes"
"fmt"
"os"
"runtime"
"strings"
"sync"
"time"
)
type Severity int
const (
FATAL Severity = iota
ERROR
WARNING
INFO
DEBUG
)
var severityName = []string{
FATAL: "FATAL",
ERROR: "ERROR",
WARNING: "WARNING",
INFO: "INFO",
DEBUG: "DEBUG",
}
const (
numSeverity = 5
)
type Backend interface {
Log(s Severity, msg []byte)
close()
}
type stdBackend struct{}
func (self *stdBackend) Log(s Severity, msg []byte) {
os.Stdout.Write(msg)
}
func (self *stdBackend) close() {}
type Logger struct {
s Severity
backend Backend
mu sync.Mutex
freeList *buffer
freeListMu sync.Mutex
logToStderr bool
}
//resued buffer for fast format the output string
type buffer struct {
bytes.Buffer
tmp [64]byte
next *buffer
}
func (self *Logger) getBuffer() *buffer {
self.freeListMu.Lock()
b := self.freeList
if b != nil {
self.freeList = b.next
}
self.freeListMu.Unlock()
if b == nil {
b = new(buffer)
} else {
b.next = nil
b.Reset()
}
return b
}
// Some custom tiny helper functions to print the log header efficiently.
const digits = "0123456789"
// twoDigits formats a zero-prefixed two-digit integer at buf.tmp[i].
func (buf *buffer) twoDigits(i, d int) {
buf.tmp[i+1] = digits[d%10]
d /= 10
buf.tmp[i] = digits[d%10]
}
// nDigits formats an n-digit integer at buf.tmp[i],
// padding with pad on the left.
// It assumes d >= 0.
func (buf *buffer) nDigits(n, i, d int, pad byte) {
j := n - 1
for ; j >= 0 && d > 0; j-- {
buf.tmp[i+j] = digits[d%10]
d /= 10
}
for ; j >= 0; j-- {
buf.tmp[i+j] = pad
}
}
// someDigits formats a zero-prefixed variable-width integer at buf.tmp[i].
func (buf *buffer) someDigits(i, d int) int {
// Print into the top, then copy down. We know there's space for at least
// a 10-digit number.
j := len(buf.tmp)
for {
j--
buf.tmp[j] = digits[d%10]
d /= 10
if d == 0 {
break
}
}
return copy(buf.tmp[i:], buf.tmp[j:])
}
func (self *Logger) putBuffer(b *buffer) {
if b.Len() >= 256 {
// Let big buffers die a natural death.
return
}
self.freeListMu.Lock()
b.next = self.freeList
self.freeList = b
self.freeListMu.Unlock()
}
func (self *Logger) formatHeader(s Severity, file string, line int) *buffer {
now := time.Now()
if line < 0 {
line = 0 // not a real line number, but acceptable to someDigits
}
buf := self.getBuffer()
// Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
// It's worth about 3X. Fprintf is hard.
year, month, day := now.Date()
hour, minute, second := now.Clock()
//2015-06-16 12:00:35 ERROR test.go:12 ...
buf.nDigits(4, 0, year, '0')
buf.tmp[4] = '-'
buf.twoDigits(5, int(month))
buf.tmp[7] = '-'
buf.twoDigits(8, day)
buf.tmp[10] = ' '
buf.twoDigits(11, hour)
buf.tmp[13] = ':'
buf.twoDigits(14, minute)
buf.tmp[16] = ':'
buf.twoDigits(17, second)
buf.tmp[19] = '.'
buf.nDigits(6, 20, now.Nanosecond()/1000, '0')
buf.tmp[26] = ' '
buf.Write(buf.tmp[:27])
buf.WriteString(severityName[s])
buf.WriteByte(' ')
buf.WriteString(file)
buf.tmp[0] = ':'
n := buf.someDigits(1, line)
buf.tmp[n+1] = ' '
buf.Write(buf.tmp[:n+2])
return buf
}
func (self *Logger) header(s Severity, depth int) *buffer {
_, file, line, ok := runtime.Caller(3 + depth)
if !ok {
file = "???"
line = 1
} else {
dirs := strings.Split(file, "/")
if len(dirs) >= 2 {
file = dirs[len(dirs)-2] + "/" + dirs[len(dirs)-1]
} else {
file = dirs[len(dirs)-1]
}
}
return self.formatHeader(s, file, line)
}
func (self *Logger) print(s Severity, args ...interface{}) {
self.printDepth(s, 1, args...)
}
func (self *Logger) printf(s Severity, format string, args ...interface{}) {
self.printfDepth(s, 1, format, args...)
}
func (self *Logger) printDepth(s Severity, depth int, args ...interface{}) {
if self.s < s {
return
}
buf := self.header(s, depth)
fmt.Fprint(buf, args...)
if buf.Bytes()[buf.Len()-1] != '\n' {
buf.WriteByte('\n')
}
self.output(s, buf)
}
func (self *Logger) printfDepth(s Severity, depth int, format string, args ...interface{}) {
if self.s < s {
return
}
buf := self.header(s, depth)
fmt.Fprintf(buf, format, args...)
if buf.Bytes()[buf.Len()-1] != '\n' {
buf.WriteByte('\n')
}
self.output(s, buf)
}
func (self *Logger) printfSimple(format string, args ...interface{}) {
buf := self.getBuffer()
fmt.Fprintf(buf, format, args...)
if buf.Bytes()[buf.Len()-1] != '\n' {
buf.WriteByte('\n')
}
self.output(INFO, buf)
}
func (self *Logger) output(s Severity, buf *buffer) {
if self.s < s {
return
}
if self.logToStderr {
os.Stderr.Write(buf.Bytes())
} else {
self.backend.Log(s, buf.Bytes())
}
if s == FATAL {
trace := stacks(true)
os.Stderr.Write(trace)
os.Exit(255)
}
self.putBuffer(buf)
}
func stacks(all bool) []byte {
// We don't know how big the traces are, so grow a few times if they don't fit. Start large, though.
n := 10000
if all {
n = 100000
}
var trace []byte
for i := 0; i < 5; i++ {
trace = make([]byte, n)
nbytes := runtime.Stack(trace, all)
if nbytes < len(trace) {
return trace[:nbytes]
}
n *= 2
}
return trace
}
/*--------------------------logger public functions--------------------------*/
func NewLogger(level interface{}, backend Backend) *Logger {
l := new(Logger)
l.SetSeverity(level)
l.backend = backend
return l
}
func (l *Logger) SetSeverity(level interface{}) {
if s, ok := level.(Severity); ok {
l.s = s
} else {
if s, ok := level.(string); ok {
for i, name := range severityName {
if name == s {
l.s = Severity(i)
}
}
}
}
}
func (l *Logger) Close() {
if l.backend != nil {
l.backend.close()
}
}
func (l *Logger) LogToStderr() {
l.logToStderr = true
}
func (l *Logger) Debug(args ...interface{}) {
l.print(DEBUG, args...)
}
func (l *Logger) Debugf(format string, args ...interface{}) {
l.printf(DEBUG, format, args...)
}
func (l *Logger) Info(args ...interface{}) {
l.print(INFO, args...)
}
func (l *Logger) Infof(format string, args ...interface{}) {
l.printf(INFO, format, args...)
}
func (l *Logger) Warning(args ...interface{}) {
l.print(WARNING, args...)
}
func (l *Logger) Warningf(format string, args ...interface{}) {
l.printf(WARNING, format, args...)
}
func (l *Logger) Error(args ...interface{}) {
l.print(ERROR, args...)
}
func (l *Logger) Errorf(format string, args ...interface{}) {
l.printf(ERROR, format, args...)
}
func (l *Logger) Fatal(args ...interface{}) {
l.print(FATAL, args...)
}
func (l *Logger) Fatalf(format string, args ...interface{}) {
l.printf(FATAL, format, args...)
}
func (l *Logger) SetLogging(level interface{}, backend Backend) {
l.SetSeverity(level)
l.backend = backend
}
/////////////////////////////////////////////////////////////////
// depth version, only a low level api
func (l *Logger) LogDepth(s Severity, depth int, format string, args ...interface{}) {
l.printfDepth(s, depth+1, format, args...)
}
func (l *Logger) PrintfSimple(format string, args ...interface{}) {
l.printfSimple(format, args...)
}
/*---------------------------------------------------------------------------*/
var logging Logger
var fileback *FileBackend = nil
var sysback *syslogBackend = nil
func init() {
SetLogging(DEBUG, &stdBackend{})
}
func SetLogging(level interface{}, backend Backend) {
logging.SetLogging(level, backend)
}
func SetSeverity(level interface{}) {
logging.SetSeverity(level)
}
func Close() {
logging.Close()
}
func LogToStderr() {
logging.LogToStderr()
}
/*-----------------------------public functions------------------------------*/
func Debug(args ...interface{}) {
logging.print(DEBUG, args...)
}
func Debugf(format string, args ...interface{}) {
logging.printf(DEBUG, format, args...)
}
func Info(args ...interface{}) {
logging.print(INFO, args...)
}
func Infof(format string, args ...interface{}) {
logging.printf(INFO, format, args...)
}
func Warning(args ...interface{}) {
logging.print(WARNING, args...)
}
func Warningf(format string, args ...interface{}) {
logging.printf(WARNING, format, args...)
}
func Error(args ...interface{}) {
logging.print(ERROR, args...)
}
func Errorf(format string, args ...interface{}) {
logging.printf(ERROR, format, args...)
}
func Fatal(args ...interface{}) {
logging.print(FATAL, args...)
}
func Fatalf(format string, args ...interface{}) {
logging.printf(FATAL, format, args...)
}
func LogDepth(s Severity, depth int, format string, args ...interface{}) {
logging.printfDepth(s, depth+1, format, args...)
}
func Printf(format string, args ...interface{}) {
logging.printfSimple(format, args...)
}
func GetLogger() *Logger {
return &logging
}