java⽅法将数据写⼊log_Java写Log
1. ⼀个最基本的例⼦
读取配置文件失败
使⽤Logging框架写Log基本上就三个步骤
引⼊loggerg类和logger⼯⼚类
声明logger
记录⽇志
下⾯看⼀个例⼦
//1. 引⼊slf4j接⼝的Logger和LoggerFactory
importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;
public classUserService {//2. 声明⼀个Logger,这个是static的⽅式,我⽐较习惯这么写。
private final static Logger logger = Logger(UserService.class);
public booleanverifyLoginInfo(String userName, String password) {//3. log it,输出的log信息将会是:"Start to verify User [Justfly]
logger.info("Start to verify User [{}]", userName);return false;
}
}
其中的第⼆步,关于Logger对象是否要声明为静态的业界有过⼀些讨论,Logback的作者最早是推荐使⽤对象变量的⽅式来声明,后来他⾃⼰也改变了想法。想详细了解的同学可以去看⼀下: /faq.html#declared_static
两种⽅式的优劣概述如下:
静态Logger对象相对来说更符合语义,节省CPU,节省内存,不⽀持注⼊
对象变量Logger⽀持注⼊,对于⼀个JVM中运⾏的多个引⽤了同⼀个类库的应⽤程序,可以在不同的应⽤程序中对同个类的Logger进⾏不同的配置。⽐如Tomcat上部署了俩个应⽤,他们都引⽤了同⼀个lib。
2. Logger接⼝的⽅法
Logger接⼝分为俩个版本,⼀个带Marker版本和⼀个没带Marker版本的。带Marker版本的我没⽤过就不介绍了。没带Marker版本的接⼝⽅法可以分为以下两组:
2.1 判断Logger级别是否开启的⽅法
public boolean isTraceEnabled();
public boolean isDebugEnabled();
public boolean isInfoEnabled();
public boolean isWarnEnabled();
public boolean isErrorEnabled();
这 组⽅法的作⽤主要是避免没必要的log信息对象的产⽣,尤其是对于不⽀持参数化信息的Log框架(Log4j 1, commons-logging)。如下⾯的例⼦所⽰,如果没有加debug级别判断,在Debug级别被禁⽤的环境(⽣产环境)中,第⼆⾏的代码将没有 必要的产⽣多个String对象。
if(logger.isDebugEnabled()){
logger.debug("["+resultCount+"]/["+totalCount+"] of users are returned");
}
如果使⽤了参数信息的⽅法,在如下代码中,即使没有添加debug级别(第⼀⾏)判断,在⽣产环境中,第⼆⾏代码只会⽣成⼀个String对象。
if(logger.isDebugEnabled()){
logger.debug("[{}]/[{}] of users in group are returned", resultCount,totalCount);
}
因此,为了代码的可读性,我⼀般情况下使⽤参数化信息的⽅法,并且不做Logger级别是否开启的判断,换句话说,这组⽅法我⼀般情况下不会⽤。
2.2 log信息的⽅法
2.2.1 ⽅法说明
Logger中有五个级别:track,debug,info,warn,error。对于每个级别,分别有五个log⽅法,以info级别为例⼦:
public void info(String msg);
⽆参数的log⽅法,例⼦:
logger.info("开始初始化配置⽂件读取模块");
输出
2014-08-11 23:36:17,783 [main] INFO  aining.logging.service.UserService - 开始初始化配置⽂件读取模块
public void info(String format, Object arg);
⽀持⼀个参数的参数化log⽅法,例⼦:
logger.info("开始导⼊配置⽂件[{}]","/somePath/config.properties");
输出2018高考作文题
2014-08-11 23:36:17,787 [main] INFO  aining.logging.service.UserService - 开始导⼊配置⽂件
[/somePath/config.properties]
public void info(String format, Object arg1, Object arg2);
⽀持俩个参数的参数化log⽅法,例⼦:
logger.info("开始从配置⽂件[{}]中读取配置项[{}]的值","/somePath/config.properties","maxSize");
输出
2014-08-11 23:36:17,789 [main] INFO  aining.logging.service.UserService - 开始从配置⽂件
[/somePath/config.properties]中读取配置项[maxSize]的值
public void info(String format, arguments);
⽀持多个参数的参数化log⽅法,对⽐上⾯的俩个⽅法来说,会多增加构造⼀个Object[]的开销。例⼦:
logger.info("在配置⽂件[{}]中读取到配置项[{}]的值为[{}]","/somePath/config.properties","maxSize", 5);
输出
2014-08-11 23:36:17,789 [main] INFO  aining.logging.service.UserService - 在配置⽂件[/somePath/config.properties]中读取到配置项[maxSize]的值为[5]
public void info(String msg, Throwable t);
⽆参数化记录log异常信息
logger.info("读取配置⽂件时出现异常",new FileNotFoundException("File not exists"));
输出
2014-08-11 23:36:17,794 [main] INFO  aining.logging.service.UserService - 读取配置⽂件时出现异常
java.io.FileNotFoundException: File not exists
at aining.logging.stLogResult(UserServiceTest.java:31) ~[test-classes/:na]
flect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_45]
flect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.6.0_45]
flect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.6.0_45]
at flect.Method.invoke(Method.java:597) ~[na:1.6.0_45]
参数化说明
在上⾯的例⼦中,我们可以看到log信息中的{}将会按照顺序被后⾯的参数所替换。这样带来了⼀个好处:如果在运⾏时不需要打印该Log,则不会重复产⽣String对象。
2.2.2 如何Log Exception
2.2.2.1 把Exception作为Log⽅法的最后⼀个参数
上⾯讲的参数化Log⽅法的中的最后⼀个参数如果是⼀个Exception类型的对象的时候,logback将会打印该Exception的StackTrace信息。看下⾯的这个例⼦:
logger.info("读取配置⽂件[{}]时出错。","/somePath/config.properties",new FileNotFoundException("File not exists"));
上⾯的代码在执⾏的时候会输出如下内容:
2014-08-12 00:22:49,167 [main] INFO  aining.logging.service.UserService - 读取配置⽂件
[/somePath/config.properties]时出错。
java.io.FileNotFoundException: File not exists
at aining.logging.stLogResult(UserServiceTest.java:30) [test-classes/:na]
flect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_45]
flect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.6.0_45]
flect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.6.0_45]
at flect.Method.invoke(Method.java:597) ~[na:1.6.0_45]贾青图片
at org.del.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) [junit.jar:na]
at org.junit.del.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit.jar:na]
at org.del.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) [junit.jar:na]
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) [junit.jar:na]
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) [junit.jar:na]
2.2.2.2 Exception不会替换log信息中的参数
另外需要注意的时,该Exception不会作为参数化内容中的参数进⾏替换。⽐如下⾯的代码:
logger.info("读取配置⽂件[{}]时出错。异常为[{}]","/somePath/config.properties",new FileNotFoundException("File not exists"));
其执⾏结果如下所⽰,第⼆个参数没有进⾏替换
2014-08-12 00:25:37,994 [main] INFO  aining.logging.service.UserService - 读取配置⽂件
[/somePath/config.properties]时出错。异常为[{}]
java.io.FileNotFoundException: File not exists
at aining.logging.stLogResult(UserServiceTest.java:30) [test-classes/:na]
flect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_45]
flect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.6.0_45]盖世谎言
flect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.
6.0_45]
at flect.Method.invoke(Method.java:597) ~[na:1.6.0_45]
2.2.2.3 参数化Exception
如果你就是不想要打印StackTrace,就是要将其参数化的话怎么弄?⼀般情况下不建议这么做,因为你把Exception中有⽤的东西吃掉了。但是如果你⾮要这么做的话,也不是不可以,有俩个⽅法:
把Exception的toString()⽅法的返回值作为参数
例⼦如下所⽰,注意我们不⽤ex.getMessage()⽽是⽤toString()⽅法,原因在于不是每个Message实例都有Message,但是默认的toString()⽅法⾥⾯包括有Message
logger.info("读取配置⽂件[{}]时出错。异常为[{}]","/somePath/config.properties",new FileNotFoundException("File not exists").toString());
执⾏结果为:
2014-08-12 00:29:24,018 [main] INFO  aining.logging.service.UserService - 读取配置⽂件
[/somePath/config.properties]时出错。异常为[java.io.FileNotFoundException: File not exists]
不要让Exception成为最后⼀个参数
例⼦如下:
logger.info("读取参数[{}]的时候出错:[{}], 请检查你的配置⽂件[{}]","maxSize",new FileNotFoundException("File not exists"),"/somePath/config.properties");
执⾏结果为:
2014-08-12 00:35:11,125 [main] INFO  aining.logging.service.UserService - 读取参数[maxSize]的时候出错:
[java.io.FileNotFoundException: File not exists], 请检查你的配置⽂件[/somePath/config.properties]
3. Log什么
前⾯讲了怎么使⽤Loggger的⽅法log⽇志,下⾯继续讲讲在什么地⽅需要记录什么级别的log,以及需要记录什么内容。
3.1 如何使⽤不同级别的Log
SLF4J把Log分成了Error,Warn,Info,Debug和Trace五个级别。我们可以把这俩个级别分成两组
3.1.1 ⽤户级别
Error、Warn和Info这三个级别的Log会出现在⽣产环境上,他们必须是运维⼈员能阅读明⽩的
3.1.1.1 Error
伊能静不雅光碟照片
影响到程序正常运⾏、当前请求正常运⾏的异常情况,例如:
打开配置⽂件失败
第三⽅应⽤⽹络连接异常
SQLException
不应该出现的情况,例如:
某个Service⽅法返回的List⾥⾯应该有元素的时候缺获得⼀个空List
做字符转换的时候居然报错说没有GBK字符集
3.1.1.2 Warn
不应该出现但是不影响程序、当前请求正常运⾏的异常情况,例如:
有容错机制的时候出现的错误情况
不到配置⽂件,但是系统能⾃动创建配置⽂件
即将接近临界值的时候,例如:
缓存池占⽤达到警告线
3.1.1.3 Info
系统运⾏信息
Service⽅法的出⼊⼝
主要逻辑中的分步骤
主持人李晨老婆
外部接⼝部分
客户端请求参数和返回给客户端的结果
调⽤第三⽅时的调⽤参数和调⽤结果
3.1.2 开发级别
Debug和Trace这俩个级别主要是在开发期间使⽤或者当系统出现问题后开发⼈员介⼊调试的时候⽤的,需要有助于提供详细的信息。
3.1.2.1 Debug
⽤于记录程序变量,例如:
多次迭代中的变量
⽤于替代代码中的注释
如果你习惯在代码实现中写:
//1. 获取⽤户基本薪资
//2. 获取⽤户休假情况
//3. 计算⽤户应得薪资
不妨这么写试试
logger.debug("开始获取员⼯[{}] [{}]年基本薪资",employee,year);
logger.debug("获取员⼯[{}] [{}]年的基本薪资为[{}]",employee,year,basicSalary);
logger.debug("开始获取员⼯[{}] [{}]年[{}]⽉休假情况",employee,year,month);