对于普通开发人员来说,如果程序遇到问题,第一个想到的肯定是看日志Log,可以通过查看日志Log发现出了什么错,便于开发,调试程序,试想如果程序crash了,没有日志Log,发生了什么将一无所知。
很多人忽略日志的重要性,日志可以记录程序的运行状况,出现问题时以便分析。日志不仅是给程序员自己看的,对于系统管理员来说也可以通过写脚本来分析日志,提取对系统运维有用的东西。系统运维相关工具的依据就是日志Log,所以自动监视系统、系统运维人员、系统管理员、开发人员等等,都需要日志Log。
(1)日志种类
根据程序的类型不同,日志种类又很多,比如:
- 操作系统的Log(Windows的Event Log)
- 各个应用程序的Log
- Web服务器的访问日志(Apache的access_log、Nginx的access.log)
- Web应用的日志
很多应用在出现致命错误的时候,其实都会把相应的日志文件上传到服务器去以便分析。
(2)日志工具
基于Java的应用,可选的Logger有很多,而且很多开源框架使用的也不同。
比如Guava使用JUL、Spring和Apache的框架都是用Apache Commons Logging、
- Apache Log4j (1999-2008)
- Apache Commons Logging (JCL, ACL) (2002)
- java.util.logging (JUL) (2002)
- Logback (2006-)
- SLF4J (2007-)
- Apache Log4j2 (2012-)
- Netflix Blitz4j (2012-)
(3)基础知识
- 输出Log时避免自己出错
- 过多的日志会拖慢系统
- 日志至少应该包含描述和数据
- 重要的日志应该添加特殊的可搜索字符
- 不要输出密码、个人信息、信用卡信息等敏感数据
- 输出重要方法的参数和返回值
- 输出重要方法的开始和结束(异常时是否输出结束日志?)
- 如果和外部系统链接,需要输出INPUT和OUTPUT
- 合理输出异常信息(不要主动输出异常,合理通过框架、共通代码输出;避免异常被输出多次)
- 考虑数据的输出格式,最好输出多种格式利用人看或者工具分析
- 合理输出二进制文件信息(name、size)
(4)输出形式
1) 键值对
引用
key1=value1, key2=value2, key3=value3 . . .
2) JSON
引用
{ "sender" : "michael" "recipient": { "name" : "michael", "name" : "andrea", "name" : "itay" } subject:"I heart logs" }
对于一个Bean对象来说,需要注意他的输出形式,比如对象的Serialization、或者覆写toString()、commons-lang的ToStringBuilder、Arrays#deepToString、Beanutils#collect等等,都可以提高可读性。
(5)日志级别
可能你知道INFO、WARN、ERROR、DEBUG、TRACE,但是90%的开发人员在输出日志的时候就没考虑过日志的级别,只是简单的使用统一级别。
系统上线后会被调整的最优化的级别,而且不同的人会看不同的日志。
(6)配置日志Log
输出格式pattern:
引用
Apache
引用
192.168.56.6 - - [24/Sep/2012:11:11:11 +0900] "GET /info.php HTTP/1.0" 200 9 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:10.0.7) Gecko/20120829 Firefox/10.0.7"
Ngnix
引用
192.168.21.198 - - [06/Mar/2015:18:19:06 +0900] "GET /index.php HTTP/1.1" 200 87243 "-" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/4.4.3.4000 Chrome/30.0.1599.101 Safari/537.36"
设置级别:
Rotate日志文件:
日志文件会占用大量的磁盘空间,所以必须rotate日志文件
(7)常见错误
- 设置错误的日志级别
- Catch异常后再Throw
- 日志输出到stderr 或 stdout (System.out.println / e.printStackTrace)
- 复杂的日志代码
引用
Diagnostic Contexts
Contextual logging (e.g. thread name, userId)
Available on log4j, logback, and log4j2
Two different types: Nested & Mapped
NDC
Nested Diagnostic Contexts are stacks
NDC.push(“x”);
NDC.pop();
Useful for debugging looping or recursive structures
MDC
Mapped Diagnostic Context
key-value pairs
MDC.put(“userId”, user.getId())
Usually what you’ll use
Show up in Graylog using the Graylog Appender
Contextual logging (e.g. thread name, userId)
Available on log4j, logback, and log4j2
Two different types: Nested & Mapped
NDC
Nested Diagnostic Contexts are stacks
NDC.push(“x”);
NDC.pop();
Useful for debugging looping or recursive structures
MDC
Mapped Diagnostic Context
key-value pairs
MDC.put(“userId”, user.getId())
Usually what you’ll use
Show up in Graylog using the Graylog Appender
参考:
Java日志终极指南
Good Logging Practices
10 Tips for Proper Application Logging
Benchmarking Java Logging Frameworks
Java Logging API
Ultimate Guide for Logging
Log4j Best Practices