golang logger输出格式怎么修改

Python021

golang logger输出格式怎么修改,第1张

1.Logger结构

首先来看下类型Logger的定义:

type Logger struct {

mu sync.Mutex // ensures atomic writesprotects the following fields

prefix string // prefix to write at beginning of each line

flag int// properties

outio.Writer // destination for output

buf[]byte // for accumulating text to write

}

主要有5个成员,其中3个我们比较熟悉,分别是表示Log前缀的 "prefix",表示Log头标签的 "flag" ,以及Log的输出目的地out。 buf是一个字节数组,主要用来存放即将刷入out的内容,相当于一个临时缓存,在对输出内容进行序列化时作为存储目的地。 mu是一个mutex主要用来作线程安全的实习,当有多个goroutine同时往一个目的刷内容的时候,通过mutex保证每次写入是一条完整的信息。

2.std及整体结构

在前一篇文章中我们提到了log模块提供了一套包级别的简单接口,使用该接口可以直接将日志内容打印到标准错误。那么该过程是怎么实现的呢?其实就是通过一个内置的Logger类型的变量 "std" 来实现的。该变量使用:

var std = New(os.Stderr, "", LstdFlags)

进行初始化,默认输出到系统的标准输出 "os.Stderr" ,前缀为空,使用日期加时间作为Log抬头。

当我们调用 log.Print的时候是怎么执行的呢?我们看其代码:

func Print(v ...interface{}) {

std.Output(2, fmt.Sprint(v...))

}

这里实际就是调用了Logger对象的 Output方法,将日志内容按照fmt包中约定的格式转义后传给Output。Output定义如下 :

func (l *Logger) Output(calldepth int, s string) error

其中s为日志没有加前缀和Log抬头的具体内容,xxxxx 。该函数执行具体的将日志刷入到对应的位置。

3.核心函数的实现

Logger.Output是执行具体的将日志刷入到对应位置的方法。

该方法首先根据需要获得当前时间和调用该方法的文件及行号信息。然后调用formatHeader方法将Log的前缀和Log抬头先格式化好 放入Logger.buf中,然后再将Log的内容存入到Logger.buf中,最后调用Logger.out.Write方法将完整的日志写入到输出目的地中。

由于写入文件以及拼接buf的过程是线程非安全的,因此使用mutex保证每次写入的原子性。

l.mu.Lock()

defer l.mu.Unlock()

将buf的拼接和文件的写入放入这个后面,使得在多个goroutine使用同一个Logger对象是,不会弄乱buf,也不会杂糅的写入。

该方法的第一个参数最终会传递给runtime.Caller的skip,指的是跳过的栈的深度。这里我记住给2就可以了。这样就会得到我们调用log 是所处的位置。

在golang的注释中说锁住 runtime.Caller的过程比较重,这点我还是不很了解,只是从代码中看到其在这里把锁打开了。

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()

}

在formatHeader里面首先将前缀直接复制到Logger.buf中,然后根据flag选择Log抬头的内容,这里用到了一个log模块实现的 itoa的方法,作用类似c的itoa,将一个整数转换成一个字符串。只是其转换后将结果直接追加到了buf的尾部。

纵观整个实现,最值得学习的就是线程安全的部分。在什么位置合适做怎样的同步操作。

4.对外接口的实现

在了解了核心格式化和输出结构后,在看其封装就非常简单了,几乎都是首先用Output进行日志的记录,然后在必要的时候 做os.exit或者panic的操作,这里看下Fatal的实现。

func (l *Logger) Fatal(v ...interface{}) {

l.Output(2, fmt.Sprint(v...))

os.Exit(1)

}

// Fatalf is equivalent to l.Printf() followed by a call to os.Exit(1).

func (l *Logger) Fatalf(format string, v ...interface{}) {

l.Output(2, fmt.Sprintf(format, v...))

os.Exit(1)

}

// Fatalln is equivalent to l.Println() followed by a call to os.Exit(1).

func (l *Logger) Fatalln(v ...interface{}) {

l.Output(2, fmt.Sprintln(v...))

os.Exit(1)

}

这里也验证了我们之前做的Panic的结果,先做输出日志操作。再进行panic。

本文主要研究一下golang的lumberjack

[email protected]/lumberjack.go

[email protected]/lumberjack.go

[email protected]/lumberjack.go

[email protected]/lumberjack.go

[email protected]/lumberjack.go

[email protected]/lumberjack.go

Logger定义了Filename、MaxSize( 单个文件大小最大值,单位M )、MaxAge( 单位天 )、MaxBackups、LocalTime、Compress属性;Write方法先判断要写入的长度是否超过l.max(),之后判断file是否为nil,如果为nil则执行l.openExistingOrNew;之后判断l.size+writeLen是否大于l.max(),如果大于则执行l.rotate(),否则执行l.file.Write(p),然后更新l.size。