比较常用的搭配是commons-logging+log4j,slf4j+logback
Logging
Java 自带的日志工具类,很少使用
commons-logging
commons-logging 就是日志的门面接口,用户可以根据喜好选择不同的日志实现框架,而不必改动日志定义,这就是日志门面的好处,符合面对接口抽象编程。
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class XXXService {
private static final Log log = LogFactory.getLog(XXXService.class);
public void doSomething(){
log.info("begin dosomething....");
}
}
Slf4j
简单日志门面接口。官方文档:官方文档
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class XXXService {
private static final Logger logger = LoggerFactory.getLogger(XXXService.class);
public void doSomething() {
logger.info("begin dosomething...");
}
}
好处:
1、更好的可读性;
2、不需要使用logger.isDebugEnabled()来解决日志因为字符拼接产生的性能问题。比如:logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol)
Log4j
Log4j 是 Apache 的一个开源日志框架,是一个具体的日志框架的实现。我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。并且只需要一个配置文件来灵活地进行配置,而不需要修改应用的代码。
Logback
Logback 是 Slf4j
的原生实现框架,同样也是出自 Log4j
一个人之手,但拥有比 log4j
更多的优点、特性和更做强的性能,现在基本都用来代替 log4j
成为主流。
日志级别选择
日志级别
日志级别 | 描述 |
---|---|
OFF | 关闭:最高级别,不输出日志。 |
FATAL | 致命:输出非常严重的可能会导致应用程序终止的错误。 |
ERROR | 错误:输出错误,但应用还能继续运行。 |
WARN | 警告:输出可能潜在的危险状况。 |
INFO | 信息:输出应用运行过程的详细信息。 |
DEBUG | 调试:输出更细致的对调试应用有用的信息。 |
TRACE | 跟踪:输出更细致的程序运行轨迹。 |
如何正确的打日志
1.定义日志
private static final Logger LOG = LoggerFactory.getLogger(this.getClass());
2.使用参数化形式{}
占位,[]
进行参数隔离
LOG.debug("Save order with order no:[{}], and order amount:[{}]");
3.输出不同级别的日志
- ERROR(错误)
影响到程序正常运行、当前请求正常运行的异常情况,一般用来记录程序中发生的任何异常错误信息(Throwable),或者是记录业务逻辑出错。如:
(1)打开配置文件失败
(2)所有第三方对接的异常(包括第三方返回错误码)
(3)所有影响功能使用的异常,包括:SQLException 和除了业务异常之外的所有异常(RuntimeException 和 Exception)
- WARN(警告)
不影响程序、当前请求正常运行的,不应该出现的异常情况:一般用来记录一些用户输入参数错误
(1)有容错机制的时候出现的错误情况
(2)业务异常的记录
- INFO(信息)
这个也是平时用的最低的,也是默认的日志级别,用来记录程序运行中的一些有用的信息。如程序运行开始、结束、耗时、重要参数等信息
- DEBUG(调试)
这个级别一般记录一些运行中的中间参数信息,只允许在开发环境开启,选择性在测试环境开启。
规范日志样例
@Override
@Transactional
public void createUserAndBindMobile(@NotBlank String mobile, @NotNull User user) throws CreateConflictException{
boolean debug = log.isDebugEnabled();
if(debug){
log.debug("开始创建用户并绑定手机号. args[mobile=[{}],user=[{}]]", mobile, LogObjects.toString(user));
}
try {
user.setCreateTime(new Date());
user.setUpdateTime(new Date());
userRepository.insertSelective(user);
if(debug){
log.debug("创建用户信息成功. insertedUser=[{}]",LogObjects.toString(user));
}
UserMobileRelationship relationship = new UserMobileRelationship();
relationship.setMobile(mobile);
relationship.setOpenId(user.getOpenId());
relationship.setCreateTime(new Date());
relationship.setUpdateTime(new Date());
userMobileRelationshipRepository.insertOnDuplicateKey(relationship);
if(debug){
log.debug("绑定手机成功. relationship=[{}]",LogObjects.toString(relationship));
}
log.info("创建用户并绑定手机号. userId=[{}],openId=[{}],mobile=[{}]",user.getId(),user.getOpenId(),mobile); // 如果考虑安全,手机号记得脱敏
}catch(DuplicateKeyException e){
log.info("创建用户并绑定手机号失败,已存在相同的用户. openId=[{}],mobile=[{}]",user.getOpenId(),mobile);
throw new CreateConflictException("创建用户发生冲突, openid=[%s]",user.getOpenId());
}
}