You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
naza/pkg/nazalog/log.go

269 lines
6.0 KiB
Go

// Copyright 2019, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package nazalog
import (
"bytes"
"fmt"
"os"
"runtime"
"sync"
"time"
"github.com/q191201771/naza/pkg/fake"
)
const (
levelDebugString = "DEBUG "
levelInfoString = " INFO "
levelWarnString = " WARN "
levelErrorString = "ERROR "
levelFatalString = "FATAL "
levelPanicString = "PANIC "
levelDebugColorString = "\033[22;37mDEBUG\033[0m "
levelInfoColorString = "\033[22;36m INFO\033[0m "
levelWarnColorString = "\033[22;33m WARN\033[0m "
levelErrorColorString = "\033[22;31mERROR\033[0m "
levelFatalColorString = "\033[22;31mFATAL\033[0m " // 颜色和 error 级别一样
levelPanicColorString = "\033[22;31mPANIC\033[0m " // 颜色和 error 级别一样
)
var (
levelToString = map[Level]string{
LevelDebug: levelDebugString,
LevelInfo: levelInfoString,
LevelWarn: levelWarnString,
LevelError: levelErrorString,
LevelFatal: levelFatalString,
LevelPanic: levelPanicString,
}
levelToColorString = map[Level]string{
LevelDebug: levelDebugColorString,
LevelInfo: levelInfoColorString,
LevelWarn: levelWarnColorString,
LevelError: levelErrorColorString,
LevelFatal: levelFatalColorString,
LevelPanic: levelPanicColorString,
}
)
type logger struct {
option Option
dir string
m sync.Mutex
fp *os.File
console *os.File
buf bytes.Buffer
currRoundTime time.Time
}
func (l *logger) Outputf(level Level, calldepth int, format string, v ...interface{}) {
l.Out(level, 3, fmt.Sprintf(format, v...))
}
func (l *logger) Debugf(format string, v ...interface{}) {
l.Out(LevelDebug, 3, fmt.Sprintf(format, v...))
}
func (l *logger) Infof(format string, v ...interface{}) {
l.Out(LevelInfo, 3, fmt.Sprintf(format, v...))
}
func (l *logger) Warnf(format string, v ...interface{}) {
l.Out(LevelWarn, 3, fmt.Sprintf(format, v...))
}
func (l *logger) Errorf(format string, v ...interface{}) {
l.Out(LevelError, 3, fmt.Sprintf(format, v...))
}
func (l *logger) Fatalf(format string, v ...interface{}) {
l.Out(LevelFatal, 3, fmt.Sprintf(format, v...))
fake.Exit(1)
}
func (l *logger) Panicf(format string, v ...interface{}) {
l.Out(LevelPanic, 3, fmt.Sprintf(format, v...))
panic(fmt.Sprintf(format, v...))
}
func (l *logger) Output(level Level, calldepth int, v ...interface{}) {
l.Out(level, 3, fmt.Sprint(v...))
}
func (l *logger) Debug(v ...interface{}) {
l.Out(LevelDebug, 3, fmt.Sprint(v...))
}
func (l *logger) Info(v ...interface{}) {
l.Out(LevelInfo, 3, fmt.Sprint(v...))
}
func (l *logger) Warn(v ...interface{}) {
l.Out(LevelWarn, 3, fmt.Sprint(v...))
}
func (l *logger) Error(v ...interface{}) {
l.Out(LevelError, 3, fmt.Sprint(v...))
}
func (l *logger) Fatal(v ...interface{}) {
l.Out(LevelFatal, 3, fmt.Sprint(v...))
fake.Exit(1)
}
func (l *logger) Panic(v ...interface{}) {
l.Out(LevelPanic, 3, fmt.Sprint(v...))
panic(fmt.Sprint(v...))
}
func (l *logger) FatalIfErrorNotNil(err error) {
if err != nil {
l.Out(LevelError, 3, fmt.Sprintf("fatal since error not nil. err=%+v", err))
fake.Exit(1)
}
}
func (l *logger) PanicIfErrorNotNil(err error) {
if err != nil {
l.Out(LevelPanic, 3, fmt.Sprintf("panic since error not nil. err=%+v", err))
panic(err)
}
}
func (l *logger) Out(level Level, calldepth int, s string) {
if l.option.Level > level {
return
}
l.m.Lock()
defer l.m.Unlock()
now := time.Now()
// 格式化日志内容
l.buf.Reset()
writeTime(&l.buf, now)
if l.console != nil {
l.buf.WriteString(levelToColorString[level])
} else {
l.buf.WriteString(levelToString[level])
}
l.buf.WriteString(s)
if l.option.ShortFileFlag {
writeShortFile(&l.buf, calldepth)
}
if l.buf.Len() == 0 || l.buf.Bytes()[l.buf.Len()-1] != '\n' {
l.buf.WriteByte('\n')
}
// 输出至控制台
if l.console != nil {
_, _ = l.console.Write(l.buf.Bytes())
if level == LevelFatal || level == LevelPanic {
_ = l.console.Sync()
}
}
// 输出至日志文件
if l.fp != nil {
if now.Day() != l.currRoundTime.Day() {
backupName := l.option.Filename + "." + l.currRoundTime.Format("20060102")
if err := os.Rename(l.option.Filename, backupName); err == nil {
_ = l.fp.Close()
l.fp, _ = os.Create(l.option.Filename)
}
l.currRoundTime = now
}
_, _ = l.fp.Write(l.buf.Bytes())
if level == LevelFatal || level == LevelPanic {
_ = l.fp.Sync()
}
}
}
func (l *logger) Sync() {
l.m.Lock()
defer l.m.Unlock()
if l.console != nil {
_ = l.console.Sync()
}
if l.fp != nil {
_ = l.fp.Sync()
}
}
func writeTime(buf *bytes.Buffer, t time.Time) {
year, month, day := t.Date()
itoa(buf, year, 4)
buf.WriteByte('/')
itoa(buf, int(month), 2)
buf.WriteByte('/')
itoa(buf, day, 2)
buf.WriteByte(' ')
hour, min, sec := t.Clock()
itoa(buf, hour, 2)
buf.WriteByte(':')
itoa(buf, min, 2)
buf.WriteByte(':')
itoa(buf, sec, 2)
buf.WriteByte('.')
itoa(buf, t.Nanosecond()/1e3, 6)
buf.WriteByte(' ')
}
func writeShortFile(buf *bytes.Buffer, calldepth int) {
buf.Write([]byte{' ', '-', ' '})
_, file, line, ok := runtime.Caller(calldepth)
if !ok {
file = "???"
line = 0
}
short := file
for i := len(file) - 1; i > 0; i-- {
if file[i] == '/' {
short = file[i+1:]
break
}
}
file = short
buf.WriteString(file)
buf.WriteByte(':')
itoa(buf, line, -1)
}
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// @NOTICE 该函数拷贝自 Go 标准库 /src/log/log.go: func itoa(buf *[]byte, i int, wid int)
// Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding.
func itoa(buf *bytes.Buffer, i int, wid int) {
// Assemble decimal in reverse order.
var b [20]byte
bp := len(b) - 1
for i >= 10 || wid > 1 {
wid--
q := i / 10
b[bp] = byte('0' + i - q*10)
bp--
i = q
}
// i < 10
b[bp] = byte('0' + i)
buf.Write(b[bp:])
}