java日志
一、日志级别的选择:Debug、Info、Warn、Error还是Fatal
软件中总免不了要使用诸如 Log4net, Log4j, Tracer 等东东来写日志,不管用什么,这些东东大多是大同小异的,一般都提供了这样5个日志级别:
× Debug
× Info
× Warn
× Error
× Fatal
一个等级比一个高,但是在具体开发中,关于应该如何选择适应的等级,却没有找到好的文章进行说明。记录一下自己的一些看法,以便日后使用吧。
=== Debug ===
这个级别最低的东东,一般的来说,在系统实际运行过程中,一般都是不输出的。
因此这个级别的信息,可以随意的使用,任何觉得有利于在调试时更详细的了解系统运行状态的东东,比如变量的值等等,都输出来看看也无妨。
当然,在每一个 Debug 调用之前,一定要加上 If 判断。
=== Info ===
这个应该用来反馈系统的当前状态给最终用户的,所以,在这里输出的信息,应该对最终用户具有实际意义,也就是最终用户要能够看得明白是什么意思才行。
从某种角度上说,Info 输出的信息可以看作是软件产品的一部分(就像那些交互界面上的文字一样),所以需要谨慎对待,不可随便。
=== Warn、Error、Fatal ===
警告、错误、严重错误,这三者应该都在系统运行时检测到了一个不正常的状态,他们之间的区别,要区分还真不是那么简单的事情。我大致是这样区分的:
所谓警告,应该是这个时候进行一些修复性的工作,应该还可以把系统恢复到正常状态中来,系统应该可以继续运行下去。
所谓错误,就是说可以进行一些修复性的工作,但无法确定系统会正常的工作下去,系统在以后的某个阶段,很可能会因为当前的这个问题,导致一个无法修复的错误(例如宕机),但也可能一直工作到停止也不出现严重问题。
所谓Fatal,那就是相当严重的了,可以肯定这种错误已经无法修复,并且如果系统继续运行下去的话,可以肯定必然会越来越乱。这时候采取的最好的措施不是试图将系统状态恢复到正常,而是尽可能地保留系统有效数据并停止运行。
也就是说,选择 Warn、Error、Fatal 中的具体哪一个,是根据当前的这个问题对以后可能产生的影响而定的,如果对以后基本没什么影响,则警告之,如果肯定是以后要出严重问题的了,则Fatal之,拿不准会怎么样,则 Error 之。
二、打印异常的堆栈信息
让logger语句记录异常的堆栈信息
前言
补个日志。
其实CSND也有类似的文章,但是我也有思考过,所以我也想记录一下。我们直接用logger.info("异常信息为:"+e)或者logger.info(e.getMessage())只能记录到异常的描述信息,却没有其异常具体发生在哪一行代码。这样即使通过日志发现出现了异常,也没法马上定位问题。因此就催生了一个想法,是否能像在idea本地跑程序时出现未捕获的异常时,控制台能打印出完整的错误堆栈信息,把这个信息记录到logger语句中。
描述
写接口时,为了方便后期查看日志定位问题,我对service的logger日志进行了改造,将所有的logger语句拼接成一条记录,然后再打印出来。因此会在service层最外层使用try catch finally。这样一个请求的日志就能统一出现,不会由于多个请求导致的日志穿插记录。日志如:
[2018-06-26 17:09:54.038] -- [http-nio-8079-exec-27] -- [INFO] -- [OrderServiceImpl.java:513 >>>> Method = query] -- [Content =
********【1121订单查询】标识:useruuid==d86a732d-2da6-11e8-xxx-xxxx,source==1,phone==135705xxxxx,orderNo==201806260000xxxxx
********【1121订单查询】入参:com.uroad.etc.dto.OrderInput@6ab68cb3[useruuid=d86a732d-2da6-11e8-xxx-xxxx,orderNo=2018062600xxxx,source=1]
********【1121订单查询】请求:request decode data:1121110802CCF5B968C014E702xxxxx
********【1121订单查询】响应:response deceode data:90001A4B0D01060F103136303832323xxxxxx
********【1121订单查询】updateTradStateAndPushMsgByCardNo 更新表结果:1,cardNo:1608227140xxx
********【1121订单查询】出参:com.uroad.etc.dto.OrderOutput@42879e4b[tradeText=2018-06-26 17:09:31,tradeDate=2018xxx,tradTime=170xxx,orderNo=201806260000003xxx,tradMoney=1,tradState=3,serialNo=<null>,cardNo=160822714xxx,terminalNo=<null>,billState=<null>,orderType=1,cardType=<null>,plateNo=<null>,payChannel=6]]
1
2
3
4
5
6
7
但这样做有个不好的地方,就是当出现异常的时候,虽然捕获了,也打印了异常信息,但是异常信息不能准确定位错误信息,会像这样:
[2018-07-31 21:41:04] -- [main] -- [INFO] -- [TestException.java:17 >>>> Method = main] -- [Content =
异常信息为:java.lang.NullPointerException]
1
2
假如service层代码简单,那么可以很快就定位出出现异常的地方。可是假如service层稍稍复杂些,代码稍稍多一些,就没法迅速的找处是哪个地方出现异常。
像我们在本地测试的时候,当出现了意料之外的运行时异常,控制台是能打印出完整的错误信息,像这样:
public static void main(String[] args) {
String s = "1";
if(s.equals("1")){
s=null;
}
s.equals("1");
}
1
2
3
4
5
6
7
控制台是能打印出完整信息的:
Exception in thread "main" java.lang.NullPointerException
at TestException.main(TestException.java:14)
1
2
这样我们就能一下子就知道错误是在TestException的第14行,即s.equals("1)这里,那我们就能推出是对象s是空对象。
但是由于我在service层的最外层使用了try catch,即我的代码是像这样的:
public static void main(String[] args) {
try {
String s = "1";
if(s.equals("1")){
s=null;
}
s.equals("1");
}catch (Exception e){
logger.info("异常信息为:" + e);
}
}
1
2
3
4
5
6
7
8
9
10
11
这样,当出现异常,异常信息为:
[2018-07-31 21:46:04] -- [main] -- [INFO] -- [TestException.java:17 >>>> Method = main] -- [Content =
异常信息为:java.lang.NullPointerException]
1
2
这样,只能知道是第17行的logger语句打印的信息。但是正如我前面所说我在service层使用了try catch finally来处理日志,所以只有一个logger语句,所以没法知道错误发生在哪里。
解决方法
于是思考,是否有这么一个方法,可以打印出完整的堆栈信息,最终还是在网上找到了:
参考文章:https://blog.csdn.net/wwsscc168/article/details/51837662
public static String toStackTrace(Exception e)
{
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
try
{
e.printStackTrace(pw);
return sw.toString();
}
catch(Exception e1)
{
return "";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
只是,不知道这样会不会很占内存。很占空间。
解决方法2
这样也能打印出错误位置:logger.info(e.getMessage(),e);
输出如:
[2018-07-31 21:52:57] -- [main] -- [INFO] -- [TestException.java:17 >>>> Method = main] -- [Content =
null]
java.lang.NullPointerException: null
at TestException.main(TestException.java:15)
1
2
3
4
5
这样也行:
logger.info("出现异常啦:",e);
1
但是这样就必须得一个单独的logger语句。
可以通过线程号去追踪。。。
最优解决方法
找到了个新方法,不需要改JVM配置,也不需要用Write流:
public static String logClzInfo(Exception e) {
StringBuffer sb = new StringBuffer();
sb.append(e.getClass() + " " + e.getMessage() + "\n");
StackTraceElement[] stackTraceElement = e.getStackTrace();
for (StackTraceElement traceElement : stackTraceElement) {
sb.append("\tat " + traceElement + "\n");
}
return sb.toString();
}
1
2
3
4
5
6
7
8
9
这样,就能在catch中通过参数e获取到完整的错误信息了。这样一来,以后出了问题,通过日志,就能一下找到问题所在了。
原文:https://blog.csdn.net/weixin_30531261/article/details/81436020
3.
在Java中,通常情况下,需要将异常堆栈信息输出到日志中,这样便于纠错及修正Bug,而多数情况下,大家最常用的是使用e.printStackTrace()直接打印堆栈信息完事,这并不是值的推荐的做法。
1. 当出现异常时,调用e.printStackTrace();其实相当于什么都没做,同时也不会把异常信息输出到日志文件中
2. 使用log.error(e.getMessage());只能够输出异常信息,但是并不包括异常堆栈,所以无法追踪出错的源点
3. 使用log.error(e);除了输出异常信息外,还能输出异常类型,但是同样不包括异常堆栈,该方法doc说明为:Logs a message object with the ERROR level.显然并不会记录异常堆栈信息
4. 当然也可以自己手动写个工具类,来挨个输出e.getStackTrace();获得的堆栈信息,显然繁琐麻烦
5. 其实在log4j中只需要这样调用,就可以获得异常及堆栈信息log.error(Object var1, Throwable var2);,该方法doc说明为:Logs a message at the ERROR level including the stack trace of the Throwable t passed as parameter.
原因分析:
有时候logger.error不能完全地打印出网站的错误堆栈信息,只能打印这个错误是一个什么错误。
为什么?
看Logger.error源码
public void error(String msg, Throwable t);
public void error(String msg);
如果只传一个参数e进去,那么e就被认为是String类型(会自动调toString()方法把Exception转成String),而不是Exception类型。
如果想打印堆栈信息,那么必须传两个或以上参数,实际上就是为了调用public void error(String msg, Throwable t);
所以我们的写法可以是:
Logger.error(“xxx出错”,e); //第二个参数是e
而不是:
Logger.error(“xxx出错:”+e) 或 logger.error(e) 或logger.error(e.getMessage);