养成良好的Log习惯

不好的Log习惯带来了哪些问题?

通常创新项目初期,我的确不是很注意编码规范,测试以及日志。由于需求和技术实现都不明确,如果太注重测试可能会框住自己的手脚。不过编码规范和日志是什么项目都应该注意的,一开始注意便能提升整个项目的效率,避免后期重构。

项目各方面逐渐明确后,各种测试也是很重要的一环。在重构之前确定一些自动的回归测试和单元测试,能避免出现很多低级错误,这些错误一旦在集成时出现,可能会花费半天进行调试,最近苦不堪言下定决心改正。

之前打log一般便就是printf大法,总之输出各种变量的值,完全就是debug时的调试,项目后期便会各种调试信息混杂在一起,非得手动注释才行。在log中使用各种“奇技淫巧”,比如输出’=’组成的分割线,使用特殊符号的数量方便搜索,不断地改log、调试、再改,最后调试出了bug,log之后再也看不懂了。

最近问题来了,服务器代码需要发布和部署,后期我也没有机会维护了,重构后的代码虽然可读性好了很多,但log还是一团糟。最后花了一天时间统一了log输出的规范,方便进行fliter,调试起来也开心和效率很多。而且设置断点,单步调试的效率往往比printf大法好很多,应该少使用log进行调试。

一个入门级别的log规范

Javascript && Android

框架已经提供了足够好的log工具,console.log/info/warning/error/table自带分级,并且结合Chrome的fliter,十分易用。在代码中加入debugger;便能让Chrome陷入断点,变量查看等都非常方便,Web程序员十分幸福。
Android也提供了类似的Logger,设置Tag并分级,Logcat中也可以进行搜索。在JNI开发中,通过#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG,ALOG,__VA_ARGS__)定义宏,也能很方便使用。之前一直通过stdout打印,非常混乱。

Golang

最后发现服务器的Log更是乱成一团,所有输出都是printf,不带分级和Tag,看起来混乱且不方便发布。Golang自带的Log包也不存在这些复杂的功能,最后并不想再依赖其他的包,还是简单封装一下为好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

var logger *log.Logger
var LogLevel int = INFO

func init() {
logger = log.New(os.Stdout, "", log.LstdFlags)
}

func Debugf(tag string, argv ...interface{}) {
if LogLevel <= DEBUG {
logger.Println("DEBUG: ["+tag+"]", argv)
}
}

......

// 从命令行读取log level
log_level := flag.Int("loglevel", INFO, "the log level")
webanalyzer.LogLevel = *log_level

func server(...){
defer func() {
if err := recover(); err != nil {
// 在数据处理函数中,接下所有的panic,并将它们发给browser
// 这样服务器panic后,客户端可以显示一个比较友好的消息
}
}()

...
}

```

最后可以通过命令行参数控制log等级,方便发布,可以直接log到文件中,并可以在这里实现一个缓存(计划中),这样混乱的Debug信息部署时就不可见了。

看到其他程序很有条理的log信息,才意识到自己这里做得非常不足,养成这一习惯,应该能提升接下来的开发与调试效率。