golang log4go的日志输出优化详解的日志输出优化详解
前言前言
在go语言中,自身已经集成了一定log模块,开发者可以使用go语言自身的log包(import “log”) 。也有不少对自身log的开源封
装。对于一些简单的开发,自身的log模块就已经足够应付。但是对一些大型,复杂的开发,log需要分门别类的输出,或者通
过网络进行输出,自身log模块将难以应对。
当前也有一些比较重量级的log模块,比如logrus,可以实现比较复杂的功能。这里介绍一个轻量级的log模块——log4go
最近又看了一些golang的日志包和相关的文章,仔细阅读了go 1.9.2系统提供的log和go-log,产生了对log4go的日志输出进行
优化的想法。
结构化与结构化与multiwriter
log使用multiwriter支持多个日志输出,用 Mutex 加锁解决多线程日志输出的冲突。log4go 则采用结构化编程用 channel 传递
LogRecord 日志记录。
原来以为 channel 的效率比较高……其实这是一个伪命题。channel 是一个全局加锁的队列,可以用来加锁,但效率比较低。
因为它多了传递数据、协调顺序处理、timout等功能,并不仅仅是加锁。跟Mutex不是一回事儿。
log4go 将屏幕日志输出 termlog 放在了结构里,这带来一个小问题。当我们用log4go调试小程序时,运行的太快,termlog 的
goroutine 还没有运行起来,程序就退出了。结果屏幕上没有显示日志。这个问题只能通过在 Close() 时加延时,等待 goroutine
启动来解决。然后还要检查 channel ……
func (f *Filter) Close() {
if f.closed {
return
}
// sleep at most one second and let go routine running
// drain the log channel before closing
for i := 10; i > 0; i-- {
// Must call Sleep here, otherwise, may panic send on closed channel
time.Sleep(100 * time.Millisecond)
if len(f.rec) <= 0 {
break
}
}
// block write channel
f.closed = true
defer f.LogWriter.Close()
close(f.rec)
if len(f.rec) <= 0 {
return
}
// drain the log channel and write driect
for rec := range f.rec {
f.LogWrite(rec)
}
}
log直接将格式化日志信息输出到屏幕,简单多了。
试着兼顾两者,在 log4go 中增加了 writer,直接输出到屏幕。拟将FileLog,SocketLog作为backend,仍然放在结构里。这
样,调试小程序和生产程序可以使用同一个日志库。实测效率略有降低。不知道 windows 下的 ColorLog 如何,以后再说。
在log4go中可以通过调用 SetOutput(nil) ,使out = nil 来关闭屏幕输出。
Determine caller func – it’s expensive
这句话注释在 log 源文件中,log4go也要调用runtime.Caller(skip int)函数来获取源文件名和行号。它是昂贵的——消耗了CPU。
建议在生产环境中关闭,log.SetSkip(-1) 。如果要对log4go进行封装,设置 log.SetSkip(log.GetSkip()+1) 。
format优化优化
其实,这才是文章的主题。
日志输出避免不了打印日期和时间,linux 环境下还要打印微秒,说不定还要打印时区。log4go的pattlog.go就是完成这些工作
的。
有一个1秒更新一次的cache机制。很漂亮。
大量使用字符串格式化函数——fmt.Sprintf。
返回字符串。而writer一般支持的是[]byte。多做一次转换。
评论0
最新资源