java程序如何正确的打日志
什么是日志
简单的说,日志就是记录程序的运行轨迹,方便查找关键信息,也方便快速定位解决问题。
我们 Java 程序员在开发项目时都是依赖 Eclipse/ Idea 等开发工具的 Debug 调试功能来跟踪解决 Bug,在开发环境可以这么做,但项目发布到了测试、生产环境呢?你有可能会说可以使用远程调试,但实际并不能允许让你这么做。
所以,日志的作用就是在测试、生产环境没有 Debug 调试工具时开发、测试人员定位问题的手段。日志打得好,就能根据日志的轨迹快速定位并解决线上问题,反之,日志输出不好不能定位到问题不说反而会影响系统的性能。
优秀的项目都是能根据日志定位问题的,而不是在线调试,或者半天找不到有用的日志而抓狂…
常用日志框架
log4j、Logging、commons-logging、slf4j、logback,开发的同学对这几个日志相关的技术不陌生吧,为什么有这么多日志技术,它们都是什么区别和联系呢?相信大多数人搞不清楚它们的关系,下面我将一一介绍一下,以后大家再也不用傻傻分不清楚了。
Logging
这是 Java 自带的日志工具类,在 JDK 1.5 开始就已经有了,在 java.util.logging
包下。
Log4j
Log4j 是 Apache 的一个开源日志框架,也是市场占有率最多的一个框架。大多数没用过 Java Logging, 但没人敢说没用过 Log4j 吧,反正从我接触 Java 开始就是这种情况,做 Java 项目必有 Log4j 日志框架。
注意:log4j 在 2015/08/05 这一天被 Apache 宣布停止维护了,用户需要切换到 Log4j2上面去。
commons-logging
上面介绍的 log4j 是一个具体的日志框架的实现,而 commons-logging 就是日志的门面接口,它也是 apache 最早提供的日志门面接口,用户可以根据喜好选择不同的日志实现框架,而不必改动日志定义,这就是日志门面的好处,符合面对接口抽象编程。
Slf4j
全称:Simple Logging Facade for Java,即简单日志门面接口,和 Apache 的 commons-logging 是一样的概念,它们都不是具体的日志框架,你可以指定其他主流的日志实现框架。
Slf4j 也是现在主流的日志门面框架,使用 Slf4j 可以很灵活的使用占位符进行参数占位,简化代码,拥有更好的可读性,这个后面会讲到。
Logback
Logback 是 Slf4j
的原生实现框架,同样也是出自 Log4j
一个人之手,但拥有比 log4j
更多的优点、特性和更做强的性能,现在基本都用来代替 log4j
成为主流。
为什么 Logback 会成为主流?
无论从设计上还是实现上,Logback相对log4j而言有了相对多的改进。不过尽管难以一一细数,这里还是列举部分理由为什么选择logback而不是log4j。牢记logback与log4j在概念上面是很相似的,它们都是有同一群开发者建立。所以如果你已经对log4j很熟悉,你也可以很快上手logback。如果你喜欢使用log4j,你也许会迷上使用logback。
更快的执行速度
基于我们先前在log4j上的工作,logback 重写了内部的实现,在某些特定的场景上面,甚至可以比之前的速度快上10倍。在保证logback的组件更加快速的同时,同时所需的内存更加少。
日志框架总结
commons-loggin
、slf4j
只是一种日志抽象门面,不是具体的日志框架。log4j
、logback
是具体的日志实现框架。- 一般首选强烈推荐使用
slf4j + logback
。当然也可以使用slf4j + log4j
、commons-logging + log4j
这两种日志组合框架。
从上图可以看出 slf4j
很强大吧,不但能和各种日志框架对接,还能和日志门面 commons-logging
进行融合。
日志级别详解
日志的输出都是分级别的,不同的设置不同的场合打印不同的日志。下面拿最普遍用的 Log4j
日志框架来做个日志级别的说明,这个也比较奇全,其他的日志框架也都大同小异。
Log4j 的级别类 org.apache.log4j.Level
里面定义了日志级别,日志输出优先级由高到底分别为以下8种。
日志级别 | 描述 |
---|---|
OFF | 关闭:最高级别,不输出日志。 |
FATAL | 致命:输出非常严重的可能会导致应用程序终止的错误。 |
ERROR | 错误:输出错误,但应用还能继续运行。 |
WARN | 警告:输出可能潜在的危险状况。 |
INFO | 信息:输出应用运行过程的详细信息。 |
DEBUG | 调试:输出更细致的对调试应用有用的信息。 |
TRACE | 跟踪:输出更细致的程序运行轨迹。 |
ALL | 所有:输出所有级别信息。 |
所以,日志级别优先级标准顺序为:
ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF,all优先级最小,off优先级最大。
如果日志设置为 L
,一个级别为 P
的输出日志只有当 P >= L
时日志才会输出。
即如果日志级别 L 设置 INFO,只有 P 的输出级别为 INFO、WARN,后面的日志才会正常输出。
打日志的规范准则
最开始也说过了,日志不能乱打,不然起不到日志本应该起到的作用不说,还会造成系统的负担。在 BAT、华为一些大公司都是对日志规范有要求的,什么时候该打什么日志都是有规范的。
1. 正确的定义日志
private static final Logger LOG = LoggerFactory.getLogger(this.getClass());
通常一个类只有一个 LOG 对象,如果有父类可以将 LOG 定义在父类中。
日志变量类型定义为门面接口(如 slf4j 的 Logger),实现类可以是 Log4j
、Logback
等日志实现框架,不要把实现类定义为变量类型,否则日志切换不方便,也不符合抽象编程思想。
2、使用参数化形式{}
占位,[]
进行参数隔离
LOG.debug("Save order with order no:[{}], and order amount:[{}]");
这种可读性好,这样一看就知道[]
里面是输出的动态参数,{}
用来占位类似绑定变量,而且只有真正准备打印的时候才会处理参数,方便定位问题。
如果日志框架不支持参数化形式,且日志输出时不支持该日志级别时会导致对象冗余创建,浪费内存,此时就需要使用 isXXEnabled
判断,如:
if(LOG.isDebugEnabled()){
// 如果日志不支持参数化形式,debug又没开启,那字符串拼接就是无用的代码拼接,影响系统性能
logger.debug("Save order with order no:" + orderNo + ", and order amount:" + orderAmount);
}
至少 debug
级别是需要开启判断的,线上日志级别至少应该是 info
以上的。
这里推荐大家用 SLF4J
的门面接口,可以用参数化形式输出日志,debug
级别也不必用 if
判断,简化代码。
3、输出不同级别的日志
项目中最常用有日志级别是ERROR
、WARN
、INFO
、DEBUG
四种了,这四个都有怎样的应用场景呢。
- ERROR(错误)
一般用来记录程序中发生的任何异常错误信息(Throwable),或者是记录业务逻辑出错。
- WARN(警告)
一般用来记录一些用户输入参数错误、
- INFO(信息)
这个也是平时用的最低的,也是默认的日志级别,用来记录程序运行中的一些有用的信息。如程序运行开始、结束、耗时、重要参数等信息,需要注意有选择性的有意义的输出,到时候自己找问题看一堆日志却找不到关键日志就没意义了。
- DEBUG(调试)
这个级别一般记录一些运行中的中间参数信息,只允许在开发环境开启,选择性在测试环境开启。
几个错误的打日志方式
1. 不要使用 System.out.print..
输出日志的时候只能通过日志框架来输出日志,而不能使用 System.out.print..
来打印日志,这个只会打印到 tomcat
控制台,而不会记录到日志文件中,不方便管理日志,如果通过服务形式启动把日志丢弃了那更是找不到日志了。
2. 不要使用 e.printStackTrace()
首先来看看它的源码:
public void printStackTrace() {
printStackTrace(System.err);
}
它其实也是利用 System.err
输出到了 tomcat
控制台。
3. 不要抛出异常后又输出日志
如捕获异常后又抛出了自定义业务异常,此时无需记录错误日志,由最终捕获方进行异常处理。不能又抛出异常,又打印错误日志,不然会造成重复输出日志。
try {
// ...
} catch (Exception e) {
// 错误
LOG.error("xxx", e);
throw new RuntimeException();
}
4. 不要使用具体的日志实现类
InterfaceImpl interface = new InterfaceImpl();
这段代码大家都看得懂吧?应该面向接口的对象编程,而不是面向实现,这也是软件设计模式的原则,正确的做法应该是。
Interface interface = new InterfaceImpl();
日志框架里面也是如此,上面也说了,日志有门面接口,有具体实现的实现框架,所以大家不要面向实现编程。
5. 没有输出全部错误信息
看以下代码,这样不会记录详细的堆栈异常信息,只会记录错误基本描述信息,不利于排查问题。
try {
// ...
} catch (Exception e) {
// 错误
LOG.error('XX 发生异常', e.getMessage());
// 正确
LOG.error('XX 发生异常', e);
}
6. 不要使用错误的日志级别
try {
// ...
} catch (Exception e) {
// 错误
LOG.info("XX 发生异常...", e);
}
大家看出了什么问题吗?用 info
记录 error
日志,日志输出到了 info
日志文件中了,同事拼命地在 error
错误日志文件里面找怎么能找到呢?
7. 不要在千层循环中打印日志
这个是什么意思,如果你的框架使用了性能不高的 Log4j
框架,那就不要在上千个 for
循环中打印日志,这样可能会拖垮你的应用程序,如果你的程序响应时间变慢,那要考虑是不是日志打印的过多了。
for(int i=0; i<2000; i++){
LOG.info("XX");
}
最好的办法是在循环中记录要点,在循环外面总结打印出来。
8. 禁止在线上环境开启 debug
这是最后一点,也是最重要的一点。
一是因为项目本身 debug
日志太多,二是各种框架中也大量使用 debug
的日志,线上开启 debug
不久就会打满磁盘,影响业务系统的正常运行。
转自:https://blog.csdn.net/juncle113/article/details/80973547
java程序如何正确的打日志的更多相关文章
- java程序中输出console的日志到文本
http://blog.sina.com.cn/s/blog_76a8411a01010u2h.html 首先:当我们引入data-integration\lib文件夹下的所有jar包后 运行java ...
- MySQL数据库时区问题导致java程序无法连接数据库
转载自https://blog.csdn.net/man_zuo/article/details/81027934 先把报错信息贴上, The server time zone value '???ú ...
- Java程序员最常用的8个Java日志框架
转自:http://www.codeceo.com/article/8-java-log-framework.html 作为一名Java程序员,我们开发了很多Java应用程序,包括桌面应用.WEB应用 ...
- 转:Java程序员最常用的8个Java日志框架
作为一名Java程序员,我们开发了很多Java应用程序,包括桌面应用.WEB应用以及移动应用.然而日志系统是一个成熟Java应用所必不可少的,在开发和调试阶段,日志可以帮助我们更好更快地定位bug:在 ...
- Java基础学习总结(40)——Java程序员最常用的8个Java日志框架
作为一名Java程序员,我们开发了很多Java应用程序,包括桌面应用.WEB应用以及移动应用.然而日志系统是一个成熟Java应用所必不可少的,在开发和调试阶段,日志可以帮助我们更好更快地定位bug:在 ...
- IntelliJ IDEA远程Debug Linux的Java程序,找问题不要只会看日志了
1 前言 欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章! 我们习惯于在本地开发的时候debug,能快速定位与解决问题,那部署在服务器上是不是就没有办法了呢?只能通过查看日志来定位? ...
- 在云环境上使用SLF4J对Java程序进行日志记录
我开发了一个Java应用,部署到云环境上之后,用postman测试发现不能按照我期望的工作,但是返回的消息对我没有任何帮助. 因为部署在云端的应用很难像本地Java应用一样调试,所以我打算用SLF4J ...
- 编写高质量代码:改善Java程序的151个建议(第4章:字符串___建议56~59)
建议56:自由选择字符串拼接方法 对一个字符串拼接有三种方法:加号.concat方法及StringBuilder(或StringBuffer ,由于StringBuffer的方法与StringBuil ...
- Java 程序员在写 SQL 时常犯的 10 个错误
Java程序员编程时需要混合面向对象思维和一般命令式编程的方法,能否完美的将两者结合起来完全得依靠编程人员的水准: 技能(任何人都能容易学会命令式编程) 模式(有些人用“模式-模式”,举个例子,模式可 ...
随机推荐
- Lua中的userdata
[话从这里说起] 在我发表<Lua中的类型与值>这篇文章时,就有读者给我留言了,说:你应该好好总结一下Lua中的function和userdata类型.现在是时候总结了.对于functio ...
- 递归遍历所有xml的节点及子节点
import java.io.File; import java.util.List; import org.dom4j.Attribute; import org.dom4j.Document; i ...
- Python 爬虫 58同城
目标站点需求分析 获取各类产品的名字,地区,时间,价格 涉及的库 BeautifulSoup,requests,time,pymongo 获取各大类产品的链接 获取单页源码 解析单页源码 保存到文件中 ...
- Android 键盘键名和键值列表
电话键 KEYCODE_CALL 拨号键 5 KEYCODE_ENDCALL 挂机键 6 KEYCODE_HOME 按键Home 3 KEYCODE_MENU 菜单键 82 KEYCODE_BACK ...
- Windows安装activemq
1.Windows下载解压就可以了,官网:http://activemq.apache.org/ 8161查看端口 61616消息服务端口 2.浏览器访问localhost:8161测试 3.可以在c ...
- elasticsearch-5.2.1在windows下的安装方法
elasticsearch-5.2.1安装方法 1. 安装java 下载安装java jdk 1.8 以上 配置java环境变量 右击[我的电脑]---[属性]-----[高级系统设置]---[环境变 ...
- C# 错误集锦
①字段重复 js → qs 仔细 ② 代码臃肿 通过判断 资产类型zc_type来判断模块的显隐 实际在其中嵌入 <%=zc_type == "2"?"" ...
- Urozero Autumn 2016. BAPC 2016
A. Airport Logistics 根据光路最快原理以及斯涅尔定律,可以得到从定点$P$进入某条直线的最佳入射角. 求出每个端点到每条线段的最佳点,建图求最短路即可. 时间复杂度$O(n^2\l ...
- JavaScript函数表达式与函数声明
什么是函数? 函数是事件驱动或者被调用时执行的重复代码块. 作用域: 1. 全局作用域 2. 函数作用域(局部作用域) var i = 100; //全局作用域 function fun(){ var ...
- laravel之ORM增删改查数据
1.首先在控制器中添加方法,然后添加路由,接着在模型中操作: 以下是模型 2.以下是控制器中的操作 一下是通过ORM进行更新 删除数据