内容简介:log4go源于google的一项log工程,但官方已经停止维护更新,下面这篇文章主要给大家介绍了关于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。多做一次转换。
- 每次都bytes.Splite讲format字符串以%字符分解成[][]byte。
在log里边自备了一个cheap的itoa函数。
// 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:]...) }
用这个函数替换日期和时间的字符串格式化函数。用[]byte代替string。
优化前,log4go 的 benchmark。
BenchmarkFormatLogRecord-4 300000 4480 ns/op BenchmarkConsoleLog-4 1000000 1748 ns/op BenchmarkConsoleNotLogged-4 20000000 97.5 ns/op BenchmarkConsoleUtilLog-4 300000 3496 ns/op BenchmarkConsoleUtilNotLog-4 20000000 104 ns/op
优化后:
BenchmarkFormatLogRecord-4 1000000 1443 ns/op BenchmarkConsoleLog-4 2000000 982 ns/op BenchmarkConsoleUtilLog-4 500000 3242 ns/op BenchmarkConsoleUtilNotLog-4 30000000 48.4 ns/op
格式化日期时间所花的时间是原来的1/3。
打印无格式化信息所花的时间是原来的1/2。
BenchmarkConsoleUtilLog调用了runtime.Caller
,格式化信息,且新增了输出信息到屏幕的时间。
字符串格式化――比较昂贵。
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对Codercto.com的支持。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- linux top命令详解与输出结果说明
- PHP控制缓存输出之ob系列函数详解
- 关于读取popen输出结果时未截断字符串导致的命令行注入详解
- 2. Python中的基本输入、输出、格式化输出
- console 输出对象
- 多种格式数据输出
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
跟我学Java Web
2010-9 / 58.00元
Java Web开发是目前最流行、使用最广泛的网站开发技术。《跟我学Java Web》通过对Java Web开发中所运用到的各种技术循序渐进地进行讲解,使读者能尽快掌握开发Web应用程序的方法。《跟我学Java Web》内容包括搭建Web开发环境、HTML相关技术基础知识、JavaScript相关技术基础知识、JSP技术基础知识、Servlet技术基础知识、搭建MySQL数据库开发环境、JDBC技......一起来看看 《跟我学Java Web》 这本书的介绍吧!