1 Star 0 Fork 0

andrew.zhang / libgo

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
logger.go 9.36 KB
一键复制 编辑 原始数据 按行查看 历史
andrew.zhang 提交于 2022-10-31 06:14 . 整理以前代码中...
// 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.
// Package log implements a simple logging package. It defines a type, Logger,
// with methods for formatting output. It also has a predefined 'standard'
// Logger accessible through helper functions Print[f|ln], Fatal[f|ln], and
// Panic[f|ln], which are easier to use than creating a Logger manually.
// That logger writes to standard error and prints the date and time
// of each logged message.
// The Fatal functions call os.Exit(1) after writing the log message.
// The Panic functions call panic after writing the log message.
package logger
import (
"fmt"
"io"
"os"
"runtime"
"sync"
"time"
)
// These flags define which text to prefix to each log entry generated by the Logger.
const (
// Bits or'ed together to control what's printed.
// There is no control over the order they appear (the order listed
// here) or the format they present (as described in the comments).
// The prefix is followed by a colon only when Llongfile or Lshortfile
// is specified.
// For example, flags Ldate | Ltime (or LstdFlags) produce,
// 2009/01/23 01:23:23 message
// while flags Ldate | Ltime | Lmicroseconds | Llongfile produce,
// 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
Ldate = 1 << iota // the date in the local time zone: 2009/01/23
Ltime // the time in the local time zone: 01:23:23
Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.
Llongfile // full file name and line number: /a/b/c/d.go:23
Lshortfile // final file name element and line number: d.go:23. overrides Llongfile
LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone
LstdFlags = Ldate | Ltime // initial values for the standard logger
)
type LOGlevl int8
const (
all LOGlevl = iota
dbg LOGlevl = iota
inf LOGlevl = iota
war LOGlevl = iota
err LOGlevl = iota
fta LOGlevl = iota
off LOGlevl = iota
)
var curlogLv LOGlevl = all
var prefix = []string{"", "DBG", "INF", "WAR", "ERR", "FTA"}
// A logger represents an active logging object that generates lines of
// output to an io.Writer. Each logging operation 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; protects the following fields
flag int // properties
out io.Writer // destination for output
buf []byte // for accumulating text to write
name string // logfile's fullpath
}
// Name 获取日志的文件名
func (l *logger) Name() string { return l.name }
// SetOutput sets the output destination for the logger.
// 现有日志文件会自动更名备份为bakName,如果bakName为空,会自动产生一时间标记的唯一文件名;
// w只有在newName为""的时候才会生效
func (l *logger) SetOutput(bakName, newName string, w io.Writer) (err error) {
l_oldName, newfile := l.Name(), (*os.File)(nil)
if l_oldName != newName && newName != "" {
newfile, err = os.OpenFile(newName, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
if err != nil {
return
}
}
l.mu.Lock()
defer l.mu.Unlock()
if l_oldName != "" {
//关闭现有日志文件
if f, ok := l.out.(*os.File); ok {
if err = f.Sync(); err != nil {
if newfile != nil {
newfile.Close()
}
return
} else if err = f.Close(); err != nil {
if newfile != nil {
newfile.Close()
}
return
}
}
//更名现有日志文件
if bakName == "" && (newName == "" || newName == l_oldName) { //没有传递备份名但新老名字有同名冲突,产生一个日期备份名字
os.Mkdir(GetLogDir(), 0755)
bakName = GetLogDir() + "/" + time.Now().Format("06-01-02_15.04.05_") + GetExeBaseName() + ".log"
}
if bakName != "" && bakName != l_oldName {
os.Rename(l_oldName, bakName)
}
}
if newfile == nil && newName != "" {
newfile, err = os.OpenFile(newName, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
if err != nil {
l.out = os.Stderr
l.name = ""
return
}
}
if newfile != nil {
l.out = newfile
l.name = newName
} else {
l.out = w
l.name = ""
if fout, ok := w.(*os.File); ok {
if fout != nil && fout != os.Stderr && fout != os.Stdout {
l.name = fout.Name()
}
}
}
return
}
var GetLogDir func() string
var GetExeBaseName func() string
// Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding.
func itoa(buf *[]byte, 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 = append(*buf, b[bp:]...)
}
func (l *logger) formatHeader(prefix string, buf *[]byte, t time.Time, file string, line int) {
if len(prefix) > 0 {
*buf = append(*buf, prefix...)
*buf = append(*buf, '/')
}
if l.flag&LUTC != 0 {
t = t.UTC()
}
if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
if l.flag&Ldate != 0 {
_, month, day := t.Date()
// itoa(buf, year, 4)
// *buf = append(*buf, '/')
itoa(buf, int(month), 2)
*buf = append(*buf, '/')
itoa(buf, day, 2)
*buf = append(*buf, ' ')
}
if l.flag&(Ltime|Lmicroseconds) != 0 {
hour, min, sec := t.Clock()
itoa(buf, hour, 2)
*buf = append(*buf, ':')
itoa(buf, min, 2)
*buf = append(*buf, ':')
itoa(buf, sec, 2)
if l.flag&Lmicroseconds != 0 {
*buf = append(*buf, '.')
itoa(buf, t.Nanosecond()/1e3, 6)
}
*buf = append(*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
}
*buf = append(*buf, file...)
*buf = append(*buf, ':')
itoa(buf, line, -1)
*buf = append(*buf, ": "...)
}
}
// Output writes the output for a logging event. The string s contains
// the text to print after the prefix specified by the flags of the
// Logger. A newline is appended if the last character of s is not
// already a newline. Calldepth is used to recover the PC and is
// provided for generality, although at the moment on all pre-defined
// paths it will be 2.
func (l *logger) Output(loglv LOGlevl, newln bool, calldepth int, s string) error {
if loglv < curlogLv {
return nil
}
now := time.Now() // get this early.
var file string
var line int
l.mu.Lock()
defer l.mu.Unlock()
if l.flag&(Lshortfile|Llongfile) != 0 {
// release lock while getting caller info - it's expensive.
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]
if newln {
l.formatHeader("\n"+prefix[loglv], &l.buf, now, file, line)
} else {
l.formatHeader(prefix[loglv], &l.buf, 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
}
// Recoverf 调用recover捕获异常,并在标题下打印异常栈,用于defer语句
// v可以额外传递最后一个参数panicfunc func()以作异常最后特殊处理比如恢复数据等
func (this *logger) Recoverf(loglv LOGlevl, format string, v ...any) (recover_err any) {
if e := recover(); e != nil {
var panicfun func()
if vlen := len(v); vlen > 0 {
if panicfun, _ = v[vlen-1].(func()); panicfun != nil {
v = v[:vlen-1]
}
}
if loglv >= curlogLv {
l_buf := [2048]byte{'\n'}
l_stack := l_buf[:runtime.Stack(l_buf[1:len(l_buf)-2], false)+2]
l_stack[len(l_stack)-1] = '\n'
this.Output(loglv, true, 2, fmt.Sprintf(format, v...)+fmt.Sprintf(":%v", e)+string(l_stack))
}
if panicfun != nil {
panicfun()
}
return e
}
return nil
}
// RecoverCtx 调用recover捕获异常,并在标题下打印异常栈,用于defer语句
// panicfunc func()以作异常最后特殊处理比如恢复数据等,set_context函数可以返回异常函数的一些变量数据以打印到栈标题处
func (this *logger) RecoverCtx(loglv LOGlevl, panicfun func(err any), set_context func() string) (recover_err any) {
if e := recover(); e != nil {
if loglv >= curlogLv {
l_buf := [2048]byte{'\n'}
l_stack := l_buf[:runtime.Stack(l_buf[1:len(l_buf)-2], false)+2]
l_stack[len(l_stack)-1] = '\n'
if set_context != nil {
this.Output(loglv, true, 2, set_context()+fmt.Sprintf(":%v", e)+string(l_stack))
} else {
this.Output(loglv, true, 2, fmt.Sprintf("%v", e)+string(l_stack))
}
}
if panicfun != nil {
panicfun(e)
}
return e
}
return nil
}
// Recoverln 最简单的在标题下打印异常函数,用于defer语句
func (this *logger) Recoverln(loglv LOGlevl, format string, v ...any) (recover_err any) {
if e := recover(); e != nil {
if loglv >= curlogLv {
this.Output(loglv, true, 2, fmt.Sprintf(format, v...)+fmt.Sprintf(":%v\n", e))
}
return e
}
return nil
}
// Stackf 在日志中打印任何调用Stackf的回溯调用栈
func (this *logger) Stackf(loglv LOGlevl, format string, v ...any) {
if loglv >= curlogLv {
l_buf := [2048]byte{'\n'}
l_stack := l_buf[:runtime.Stack(l_buf[1:len(l_buf)-2], false)+2]
l_stack[len(l_stack)-1] = '\n'
this.Output(loglv, true, 2, fmt.Sprintf(format, v...)+string(l_stack))
}
}
Go
1
https://gitee.com/andrewzh/libgo.git
git@gitee.com:andrewzh/libgo.git
andrewzh
libgo
libgo
v1.0.3

搜索帮助