本篇文章介绍Spring Boot中打印日志信息以及将日志输出到指定文件中,部分内容总结摘抄自Logback手册,仅作笔记。
笔者这篇文章之前写的所有的文章中要打印信息的地方都是使用的System.out.print,此方法在执行时会使程序执行线程等待,且此方法只能输出到控制台。而slf4j、log4j等工具是异步线程的,程序执行线程无需等待,且它们除了将日志输出到控制台外还可以将日志输入到日志文件中。笔者写这篇文章的目的除了让自己了解Spring Boot日志外,也是督促自己以后在可以使用日志打印的地方使用日志打印而不是简单的System.out.print。
Spring Boot有默认的日志系统:Logback,日志类库是slf4j。日志系统和日志类库的关系是:日志类库是对不同的日志系统的API封装。使用slf4j本需要添加名为spring-boot-starter-logging的依赖,但因为spring-boot-starter-web已经包含了spring-boot-starter-logging的依赖,因此无序额外再添加依赖。
在Logback日志系统中,日志信息的打印需要依赖Logger上下文对象。logger的命名具有层次结构:如果一个logger的名字加上“.”作为另一个logger的前缀,那个该logger就是另一个logger的祖先。如果一个logger与另一个logger之间没有其他的logger,则该logger就是另一个logger的父级。例如com.example是com.example.springboot的父级,com是com.example.springboot的祖先。
logger可以通过org.slf4j.LoggerFactory的类静态方法getLogger()获取,该方法可以传入一个String字符串或Class对象作为该logger的名字。一般情况下,我们都传入当前类的Class对象以便于查看日志。例如:
Logger logger = LoggerFactory.getLogger(getClass());
日志的级别从低到高为:TRACE < DEBUG < INFO < WARN < ERROR < FATAL,Spring Boot默认的日志级别是INFO。只有当一条日志的打印级别大于logger的有效级别时,该日志才会被打印出来。这条规则总结为:如果日志的打印级别为p,Logger实例的级别为q,只有当p>=q时,该条日志才可以被打印出来。该规则也被称为基本选择规则。
那么一条日志的打印级别是怎么确定的呢?这就得看Logger接口了,Logger接口主要有以下几个方法:
void trace(String var1);
void debug(String var1);
void info(String var1);
void warn(String var1);
void error(String var1);
可以看到这几个方法的方法名与日志级别类似,事实上,打印日志时使用的方法就代表着这条日志的打印级别。例如下面这条语句的打印级别是INFO。
logger.info("logger test");
那么我们怎么设置logger的有效级别呢?又如何设置多个logger的有效级别呢?这就需要我们在application.yml配置”logging.level+路径“属性了。例如我们想统一设置项目的所有logger的有效级别为WARN,那需要在application.yml添加以下配置:
logging:
level:
root: warn
启动项目控制台打印信息如下:
没有任何信息打印,这种情况是正常的。该项目所有的logger的有效级别都被设置为了WARN,根据基本选择原则,只有当日志级别>=WARN时才会打印日志,而Spring Boot启动时打印的日志级别都为INFO,因此没有日志信息打印。
我们还可以将logger的粒度细化到package,即为每个包都设置不同的打印级别。例如将Controller包的打印级别设置为INFO,将Service包的打印级别设置为WARN。如下:
logging:
level:
com:
example:
hellospringboot:
controller: info
service: warn
Controller类文件代码如下:
@RestController
@RequestMapping("/index")
public class IndexController {
Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private IndexService indexServiceImpl;
@RequestMapping("/hello/{id}")
public String hello(@PathVariable String id){
logger.trace("controller trace...");
logger.debug("controller debug...");
logger.info("controller info...");
logger.warn("controller warn...");
logger.error("controller error...");
return indexServiceImpl.index(id);
}
}
Service类代码如下:
@Service
public class IndexServiceImpl implements IndexService {
Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private StudentMapper studentMapper;
@Override
@Cacheable(value = "sname",key = "#id")
public String index(String id) {
logger.trace("service trace...");
logger.debug("service debug...");
logger.info("service info...");
logger.warn("service warn...");
logger.error("service error...");
Student student = studentMapper.selectByPrimaryKey(id);
System.out.println("service方法调用...");
return student!=null ? student.getsName() : null;
}
}
在浏览器访问该接口,控制台打印信息如下:
可以看到Controller打印了日志级别>=INFO的日志,Service打印了日志级别>=WARN的日志,说明我们配置是成功的。除此之外,还可以看到日志信息有输出类的全限定类名,这是因为我们在创建logger对象时传入了当前类的Class对象作为该logger的名称。
在本篇文章的开头提到过日志除了可以打印到控制台,还可以输出到指定文件,那么怎么将日志输出到指定的文件中呢?还是在application.yml中配置,只是这次配置的不是logging.level属性,而是logging.file属性。例如将当前项目的日志输出到目录“D:\WorkSpace\springboot”下,日志名称为“springboot.log”,配置如下:
logging:
level:
com:
example:
hellospringboot:
controller: info
service: warn
file:
path: D:\WorkSpace\springboot
name: springboot.log
启动项目,调用接口,在目录“D:\WorkSpace\springboot”下可以看到名为“springboot.log”的文件。
打开该文件,内容如下:
出现上述内容的原因是我刷新了几次网页,然后重启了该项目。
除以上功能外,还可以通过在application.yml中配置日志打印格式等等,此处不再赘述。