又看了一些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,格式化信息,且新增了输出信息到屏幕的时间。

字符串格式化——比较昂贵。


参考

  1. GO Logger 日志实践

  2. 使用golang log库包实现日志文件输出

  3. Golang 优化之路——自己造一个日志轮子

  4. 几个log lib对比

  5. Go语言实战笔记(十八)| Go log 日志

  6. Golang Tips: 加锁 Mutex 和 channel 性能对比

  7. 测试Channel和Lock之间的性能差别

log4go的输出优化的更多相关文章

  1. c++ 读入优化、输出优化模板

    0. 在有些输入数据很多的变态题中,scanf会大大拖慢程序的时间,cin就更慢了,所以就出现了读入优化.其原理就是一个一个字符的读入,输出优化同理,主要使用getchar,putchar函数. 1. ...

  2. C++中的读入输出优化及清新脱俗的宏命令

    C和C++有了#define,从此它就变了模样 宏命令就是#define,#if,#error之类的 本文主要介绍宏命令和相关的骚操作 读入输出优化 inline int read() { int a ...

  3. 读入输出优化_C++

    当我们考试时遇到大量的读入或者输出时,这些代码会耗费许多运行程序的时间,导致TL 本来 log2n 的算法因为读入被卡成线性的就太不划算了,所以我们这里要采用读入输出优化 getchar 和 putc ...

  4. Android 项目Log日志输出优化

    概述 Android开发过程中经常需要向控制台输出日志信息,有些人还在用Log.i(tag,msg)的形式或者system.out.println(msg)方式吗?本篇文章对日志信息输出进行优化,以达 ...

  5. 读入优化&输出优化

    读入优化 int read() { ; ') ; '; ') num=num*+c-'; return ff*num; } 输出优化 void write(int x) { ) { putchar(' ...

  6. 【CSP模拟赛】天才绅士少女助手克里斯蒂娜(线段树&读入优化&输出优化)

    题面描述 红莉栖想要弄清楚楼下天王寺大叔的显像管电视对“电话微波炉(暂定)”的影响.选取显像管的任意一个平面,一开始平面内有个n电子,初始速度分别为vi,定义飘升系数为 $$\sum_{1\leqsl ...

  7. C++读入输出优化

    读入输出优化虽然对于小数据没有半点作用,但是对于大数据来说,可以优化几十ms. 有时就是那么几十ms,可以被卡掉大数据的点 读入优化 int read() { int x=0,sig=1; char ...

  8. C++ 读入优化&输出优化

    读入优化:读入优化只是针对整数,由于getchar()读字符非常的快,所以采用getchar()来进行读入,下设输入的数为x 负数处理:用一个标志变量f,开始时为1,当读入了'-'时,f变为-1,最后 ...

  9. 大数据技术之_19_Spark学习_04_Spark Streaming 应用解析 + Spark Streaming 概述、运行、解析 + DStream 的输入、转换、输出 + 优化

    第1章 Spark Streaming 概述1.1 什么是 Spark Streaming1.2 为什么要学习 Spark Streaming1.3 Spark 与 Storm 的对比第2章 运行 S ...

随机推荐

  1. [转载] Hive与HBase的联系与区别

    转载自http://blog.csdn.net/wangmuming/article/details/23954527和http://www.cnblogs.com/justinzhang/p/427 ...

  2. 《java.util.concurrent 包源码阅读》18 Exchanger

    Exchanger可以看做双向数据传输的SynchronousQueue,即没有生产者和消费者之分,任意两个线程都可以交换数据. 在JDK5中Exchanger被设计成一个容量为1的容器,存放一个等待 ...

  3. 加密代理和Retrofit解密Converter

    最近在研究安卓的Retrofit框架,服务器的数据全部用加密算法加密了,发现无法使用"com.squareup.retrofit2:converter-gson:2.1.0"Jar ...

  4. JAVA基础3——常见关键字解读(2)

    synchronized Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 用法说明 synchronized 关键字,它包括两种用法: ...

  5. VS2015远程调试

    原文链接 VS2015远程调试   在PayPal支付时,PayPal回调函数一直报错,本地没有外网IP,没有办法在本地调试,需要远程调试: 1.找到远程调试的文件夹: 找到对应的服务器的型号:64位 ...

  6. HITCON-Training-master 部分 Writeup(12月11更新)

    HITCON-Training-master Writeup 0x01.lab3 首先checksec一下,发现连NX保护都没开,结合题目提示ret2sc,确定可以使用shellcode得到权限. I ...

  7. Python连接MySQL数据库中各种坑

    第一个坑 要想连接数据库,我们必须拥有MySQL-python这个模块,首先,我在安装这个模块的时候就到了第一个大坑. 常规安装方法:进入cmd 使用 pip install MySQL-python ...

  8. phpstorm快捷键记录

    快捷键记录 Ctrl + N 按类名查找Ctrl + Shift + N 按文件名查找,快速查找文件Ctrl + Shift+Alt+N 根据函数名查找Ctrl + F 当前文件查找Ctrl + Sh ...

  9. Js中for循环的阻塞机制

    Js阻塞机制,跟Js引擎的单线程处理方式有关,每个window一个JS线程.所谓单线程,在某个特定的时刻只有特定的代码能够被执行,并阻塞其它的代码. 由于浏览器是事件驱动的(Event driven) ...

  10. Windows 服务多语言化时读取配置文件失败的问题。

    在Installer中,按一般读取配置文件的方法(ConfigurationManager.AppSettings["CultureName"])读取不到内容. 可以这样读取: v ...