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

374 lines
8.1 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)
Squashed commit of the following: commit 721f5df77672d904a8926433d33a1546d6853724 Author: q191201771 <191201771@qq.com> Date: Mon Sep 30 11:09:38 2019 +0800 nezha -> naza commit 94cbffe32a567ac3b11583675900cd9ac1561149 Author: q191201771 <191201771@qq.com> Date: Mon Sep 30 10:44:23 2019 +0800 rename package log -> package nazalog commit db8122c69d18cd6b16dd2853207f0397373fa863 Author: q191201771 <191201771@qq.com> Date: Mon Sep 30 10:26:00 2019 +0800 package connection: bugfix make exitChan commit 083db929f6341a35c170e023e5d4a8aa8386b87d Author: q191201771 <191201771@qq.com> Date: Mon Sep 30 10:06:00 2019 +0800 rename package nzjson -> nazajson commit e01dda7218b4390aa5b9e8280be109761c8fc576 Author: q191201771 <191201771@qq.com> Date: Mon Sep 30 09:59:35 2019 +0800 new package: nazajson commit 09421c2b985e610a5940bf3c8a2e6bd3b4838274 Author: q191201771 <191201771@qq.com> Date: Sun Sep 29 12:04:21 2019 +0800 1. package connection & package log: Config -> Option 2. package log: IsToStdOut default -> ture, ShortFileFlag default -> true commit 1052ef0ead7c4ad2c8acfdcccefe186d8f743d51 Author: q191201771 <191201771@qq.com> Date: Sun Sep 29 11:13:57 2019 +0800 package connection: rename wmsg struct -> wMsg commit a450ed21b008b10898d363c4a58a04662ac6a283 Author: q191201771 <191201771@qq.com> Date: Sun Sep 29 11:11:11 2019 +0800 package connection: erase func Printf commit 810d41a83beb54110461c288d7078a9c08b16b5b Author: q191201771 <191201771@qq.com> Date: Sun Sep 29 11:06:57 2019 +0800 package log: mkdir 777 -> 644 commit e087bc8fe70f1ba619d87ca77356b198f117cd9c Merge: f117d4b d6006a3 Author: q191201771 <191201771@qq.com> Date: Fri Sep 27 14:21:05 2019 +0800 Merge branch 'master' into dev commit f117d4b20e19812aa9f0aa618daa60a73169e486 Merge: f2f631e 059da41 Author: q191201771 <191201771@qq.com> Date: Fri Sep 27 14:12:38 2019 +0800 Merge branch 'master' into dev commit f2f631eb21e1a99f1b40470dc517be97f36d74fd Author: q191201771 <191201771@qq.com> Date: Wed Sep 25 19:37:31 2019 +0800 test.sh 只在开发机 macos 下执行 gofmt 检查 commit fb692804a04147c96bd17cd262988595bad08d3d Author: q191201771 <191201771@qq.com> Date: Wed Sep 25 17:26:36 2019 +0800 1. package log: 增加 panic 接口 2. test.sh 中增加 gofmt 检查提醒 commit b2c187b59f30c0e1f7c2d2ac867784a39aefa445 Author: q191201771 <191201771@qq.com> Date: Wed Sep 25 10:56:04 2019 +0800 1. package log: bugfix 日志业务方内容有换行,并且有源码行号时,整行日志添加换行 2. package connection: debug log commit a9b2fe8f863cb0b28189a257246703351a604b02 Author: q191201771 <191201771@qq.com> Date: Tue Sep 24 16:42:09 2019 +0800 package connection: bugfix func Done commit 5b13acb3d83860cf98cc8b02358f97a7fc514ffb Author: q191201771 <191201771@qq.com> Date: Tue Sep 24 15:59:02 2019 +0800 package connection: 1. 增加 ModWriteChanSize 方法 2. bugfix Close 中忘记调用 close commit 4beaa82c509cc44ddb0ecda91674191e715b83d8 Author: q191201771 <191201771@qq.com> Date: Tue Sep 24 15:44:37 2019 +0800 package connection: 增加 Done 方法 commit 0f3da700ec8cb90068dda7a8f091aefd9076be07 Author: q191201771 <191201771@qq.com> Date: Tue Sep 24 14:05:39 2019 +0800 package connection: 1. 增加 wChanSize 配置,用于指定是否使用异步发送 2. 添加 Flush 方法 commit d154645a44a9e66b3aa2b655571d566ce28ea264 Author: q191201771 <191201771@qq.com> Date: Mon Sep 23 19:48:08 2019 +0800 package connection: Flush before Close
5 years ago
package nazalog
import (
"bytes"
"fmt"
"os"
"path/filepath"
"runtime"
"sync"
"time"
"github.com/q191201771/naza/pkg/nazareflect"
"github.com/q191201771/naza/pkg/fake"
)
var _ Logger = new(logger)
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 {
prefixs []string
core *core
}
type core struct {
option Option
m sync.Mutex
fp *os.File
console *os.File
buf bytes.Buffer
currRoundTime time.Time
}
func (l *logger) Debugf(format string, v ...interface{}) {
l.Out(LevelDebug, 2, fmt.Sprintf(format, v...))
}
func (l *logger) Infof(format string, v ...interface{}) {
l.Out(LevelInfo, 2, fmt.Sprintf(format, v...))
}
func (l *logger) Warnf(format string, v ...interface{}) {
l.Out(LevelWarn, 2, fmt.Sprintf(format, v...))
}
func (l *logger) Errorf(format string, v ...interface{}) {
l.Out(LevelError, 2, fmt.Sprintf(format, v...))
}
func (l *logger) Fatalf(format string, v ...interface{}) {
l.Out(LevelFatal, 2, fmt.Sprintf(format, v...))
fake.OS_Exit(1)
}
func (l *logger) Panicf(format string, v ...interface{}) {
l.Out(LevelPanic, 2, fmt.Sprintf(format, v...))
panic(fmt.Sprintf(format, v...))
}
func (l *logger) Debug(v ...interface{}) {
l.Out(LevelDebug, 2, fmt.Sprint(v...))
}
func (l *logger) Info(v ...interface{}) {
l.Out(LevelInfo, 2, fmt.Sprint(v...))
}
func (l *logger) Warn(v ...interface{}) {
l.Out(LevelWarn, 2, fmt.Sprint(v...))
}
func (l *logger) Error(v ...interface{}) {
l.Out(LevelError, 2, fmt.Sprint(v...))
}
func (l *logger) Fatal(v ...interface{}) {
l.Out(LevelFatal, 2, fmt.Sprint(v...))
fake.OS_Exit(1)
}
func (l *logger) Panic(v ...interface{}) {
l.Out(LevelPanic, 2, fmt.Sprint(v...))
panic(fmt.Sprint(v...))
}
func (l *logger) Output(calldepth int, s string) error {
l.Out(LevelInfo, calldepth, s)
return nil
}
func (l *logger) Print(v ...interface{}) {
l.Out(LevelInfo, 2, fmt.Sprint(v...))
}
func (l *logger) Printf(format string, v ...interface{}) {
l.Out(LevelInfo, 2, fmt.Sprintf(format, v...))
}
func (l *logger) Println(v ...interface{}) {
l.Out(LevelInfo, 2, fmt.Sprint(v...))
}
func (l *logger) Fatalln(v ...interface{}) {
l.Out(LevelInfo, 2, fmt.Sprint(v...))
fake.OS_Exit(1)
}
func (l *logger) Panicln(v ...interface{}) {
l.Out(LevelInfo, 2, fmt.Sprint(v...))
panic(fmt.Sprint(v...))
}
func (l *logger) Assert(expected interface{}, actual interface{}) {
if !nazareflect.Equal(expected, actual) {
err := fmt.Sprintf("assert failed. excepted=%+v, but actual=%+v", expected, actual)
switch l.core.option.AssertBehavior {
case AssertError:
l.Out(LevelError, 2, err)
case AssertFatal:
l.Out(LevelFatal, 2, err)
fake.OS_Exit(1)
case AssertPanic:
l.Out(LevelPanic, 2, err)
panic(err)
}
}
}
func (l *logger) Out(level Level, calldepth int, s string) {
if l.core.option.Level > level {
return
}
now := fake.Time_Now()
var file string
var line int
if l.core.option.ShortFileFlag {
_, file, line, _ = runtime.Caller(calldepth)
}
l.core.m.Lock()
l.core.buf.Reset()
if l.core.option.TimestampFlag {
writeTime(&l.core.buf, now, l.core.option.TimestampWithMSFlag)
}
if l.core.option.LevelFlag {
if l.core.console != nil {
l.core.buf.WriteString(levelToColorString[level])
} else {
l.core.buf.WriteString(levelToString[level])
}
}
if l.prefixs != nil {
for _, s := range l.prefixs {
l.core.buf.WriteString("[")
l.core.buf.WriteString(s)
l.core.buf.WriteString("] ")
}
}
l.core.buf.WriteString(s)
if file != "" && line > 0 {
l.core.buf.WriteString(" - ")
short := file
for i := len(file) - 1; i > 0; i-- {
if file[i] == '/' {
short = file[i+1:]
break
}
}
file = short
l.core.buf.WriteString(file)
l.core.buf.WriteByte(':')
itoa(&l.core.buf, line, -1)
}
if l.core.buf.Len() == 0 || l.core.buf.Bytes()[l.core.buf.Len()-1] != '\n' {
l.core.buf.WriteByte('\n')
}
// 输出至控制台
if l.core.console != nil {
_, _ = l.core.console.Write(l.core.buf.Bytes())
if level == LevelFatal || level == LevelPanic {
_ = l.core.console.Sync()
}
}
// 输出至日志文件
if l.core.fp != nil {
if l.core.option.IsRotateDaily && now.Day() != l.core.currRoundTime.Day() {
backupName := l.core.option.Filename + "." + l.core.currRoundTime.Format("20060102")
if err := os.Rename(l.core.option.Filename, backupName); err == nil {
_ = l.core.fp.Close()
l.core.fp, _ = os.Create(l.core.option.Filename)
}
l.core.currRoundTime = now
}
_, _ = l.core.fp.Write(l.core.buf.Bytes())
if level == LevelFatal || level == LevelPanic {
_ = l.core.fp.Sync()
}
}
l.core.m.Unlock()
}
func (l *logger) Sync() {
l.core.m.Lock()
defer l.core.m.Unlock()
if l.core.console != nil {
_ = l.core.console.Sync()
}
if l.core.fp != nil {
_ = l.core.fp.Sync()
}
}
func (l *logger) WithPrefix(s string) Logger {
var prefixs []string
if l.prefixs != nil {
prefixs = make([]string, len(l.prefixs))
copy(prefixs, l.prefixs)
}
prefixs = append(prefixs, s)
ll := &logger{
prefixs: prefixs,
core: l.core,
}
return ll
}
func newLogger(modOptions ...ModOption) (*logger, error) {
var err error
l := &logger{
core: &core{
currRoundTime: time.Now(),
},
}
l.core.option = defaultOption
for _, fn := range modOptions {
fn(&l.core.option)
}
if err := validate(l.core.option); err != nil {
return nil, err
}
if l.core.option.Filename != "" {
dir := filepath.Dir(l.core.option.Filename)
if err = os.MkdirAll(dir, 0777); err != nil {
return nil, err
}
if l.core.fp, err = os.OpenFile(l.core.option.Filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666); err != nil {
return nil, err
}
}
if l.core.option.IsToStdout {
l.core.console = os.Stdout
}
return l, nil
}
func validate(option Option) error {
if option.Level < LevelDebug || option.Level > LevelPanic {
return ErrLog
}
if option.AssertBehavior < AssertError || option.AssertBehavior > AssertPanic {
return ErrLog
}
return nil
}
func writeTime(buf *bytes.Buffer, t time.Time, withMS bool) {
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)
if withMS {
buf.WriteByte('.')
itoa(buf, t.Nanosecond()/1e3, 6)
}
buf.WriteByte(' ')
}
// 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:])
}