集成logback需要添加spring-boot-starter-logging依赖,而此依赖已经在spring-boot-starter中添加过了,所以不用再添加此依赖了。默认情况下,Spring Boot会用Logback来记录日志,并用INFO级别输出到控制台
# 输出格式是:时间(精确到毫秒) 日志级别(FATAL, ERROR, WARN, INFO, DEBUG, TRACE) 进程ID --- [线程名] Logger名(一般使用源代码的类名) : 日志内容
2018-03-21 19:50:57.892 INFO 22798 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
# file 和 path 不能同时配置,只能配置其中一个
logging.file=/Users/mengday/Documents/logs/demo.log
logging.path=/var/log
logging.pattern.dateformat=yyyy-MM-dd HH:mm:ss.SSS
logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}
logging.pattern.file=%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}
logging.file.max-size=1MB
logging.file.max-history=30
logging.level.com.example.demo.mapper=debug
logging.level.com.example.demo.controller=info
logging.config=classpath:logback-${spring.profiles.active}.xml
默认情况下,Spring Boot将日志输出到控制台,不会写到日志文件。如果要将日志写入文件,则需在application.properties中设置logging.file或logging.path属性。logging.file,设置日志文件,可以是绝对路径,也可以是相对路径。logging.path,设置日志所在的目录,会在该目录下创建spring.log文件,并写入日志内容。注意:二者不能同时使用,如若同时使用,则只有logging.file生效。
默认情况下,日志文件的大小达到10MB时会切分一次,产生新的日志文件, 如果要修改大小可以通过logging.file.max-size来配置,单位为MB,logback默认的日志级别为INFO,如果需要修改可以通过配置logging.level.包语法来配置, 包即可以是一个包名也支持全限定路径包.类,如logging.level.com.example.demo.dao=DEBUG, logging.level.包属性可以配置多个,可以为每个包进行单独设置日志级别,这种方式能够控制每个包的输出日志级别, 也可以设置root的日志级别logging.level.root=INFO
在application.properties中配置公共的日志配置, 开发测试配置相应的值,生产配置相应的值。
spring.profiles.active=prod
logging.file=/Users/mengday/Documents/logs/demo.log
logging.pattern.dateformat=yyyy-MM-dd HH:mm:ss.SSS
logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}
logging.pattern.file=%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}
logging.file.max-size=1MB
logging.file.max-history=30
logging.level.com.example.demo.controller=info
application-test.properties
logging.level.com.example.demo.mapper=debug
application-prod.properties
logging.level.com.example.demo.mapper=error
在Spring Boot中可即可以通过在application.properties中配置日志,也可以使用传统的配置文件(logback.xml)的方式来配置, 在spring boot中配置文件名字既可以是logback.xml也可以是logback-spring.xml,Spring推荐使用已logback-spring.xml, 使用logback-spring.xml可以使用多环境日志输出springProfile,当然配置文件的名称也可以是自定义的,自定义的名称需要在application.properties中使用logging.config配置对应的名字,如:logging.config=classpath:logback-config.xml, 配置文件的位置放在src/main/resources下面
logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>demo</contextName>
<property name="log.path" value="/Users/mengday/Documents/logs" />
<property name="log.name" value="demo" />
<!--输出到控制台-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>-->
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!--输出到文件-->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/${log.name}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>1MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="console" />
<appender-ref ref="file" />
</root>
<logger name="com.example.demo.mapper" level="DEBUG" additivity="false">
<appender-ref ref="console" />
</logger>
<!-- 效果不对 测试环境+开发环境. -->
<springProfile name="test,dev">
<logger name="com.example.demo" level="INFO" />
</springProfile>
<!-- 生产环境 -->
<springProfile name="prod">
<logger name="com.example.demo" level="ERROR"/>
</springProfile>
</configuration>
HelloWorldController
@RestController
public class HelloWorldController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@RequestMapping("/hello")
public String index() {
for (int i = 0; i < 15000; i++) {
logger.trace(i + "日志输出 trace");
logger.debug(i + "日志输出 debug");
logger.info(i + "日志输出 info");
logger.warn(i + "日志输出 warn");
logger.error(i +"日志输出 error");
}
return "Spring Boot Hello World!";
}
}
根节点configuration
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
contextName:每个logger都关联到logger上下文,默认上下文名称为“default”。但可以使用设置成其他名字,用于区分不同应用程序的记录。一旦设置,不能修改,可以通过%contextName来引用日志上下文名称
property:用来定义变量值的标签,通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量
appender:用来格式化日志输出节点,有俩个属性name和class,class用来指定哪种输出策略,常用就是控制台输出策略和文件输出策略。ThresholdFilter为系统定义的拦截器,例如我们用ThresholdFilter来过滤掉ERROR级别以下的日志不输出到文件中。另一种常见的日志输出到文件,随着应用的运行时间越来越长,日志也会增长的越来越多,将他们输出到同一个文件并非一个好办法。RollingFileAppender用于切分文件日志
rollingPolicy
root:用来指定最基础的日志输出级别,只有一个level属性,用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,可以包含零个或多个元素,标识这个appender将会添加到这个logger。
logger:用来设置某一个包或者具体的某一个类的日志打印级别、以及指定appender
注意:使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
<logger name="com.example.demo.dao" level="DEBUG" additivity="false">
<appender-ref ref="console" />
</logger>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
当同时有个多切面需要执行时,通过Order来定义优先级,值越小越优先执行。例如有两个切面,一个是记录请求@Order(1),一个是验证接口中的参数@Order(2),那么切面方法的执行顺序是先执行@Order(1)的before, 再执行@Order(2)的before; 然后再执行@Order(2)的@After或@AfterReturning,最后再执行@Order(1)的@After或@AfterReturning。
@Aspect
@Order(1)
@Component
public class LogAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
ThreadLocal<Long> startTime = new ThreadLocal<>();
@Pointcut("execution(public * com.example.demo.web..*.*(..))")
public void poincut(){}
@Before("poincut()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
startTime.set(System.currentTimeMillis());
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
logger.info("IP:" + request.getRemoteAddr());
logger.info("URL:" + request.getRequestURL().toString());
logger.info("HTTP_METHOD:" + request.getMethod());
logger.info("CLASS_METHOD:" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("ARGS:" + Arrays.toString(joinPoint.getArgs()));
}
@AfterReturning(pointcut = "poincut()", returning = "response")
public void doAfterReturning(Object response){
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
logger.info("IP:" + request.getRemoteAddr());
logger.info("SPEND Time:" + (System.currentTimeMillis() - startTime.get()));
logger.info("RESPONSE:" + response);
}
}
通过@Pointcut定义的切入点为com.example.demo.web包下的所有函数(对web层所有请求处理做切入点),然后通过@Before实现,对请求内容的日志记录,最后通过@AfterReturning记录请求返回的对象。
{
"groups": [
{
"name": "logging",
"type": "org.springframework.boot.context.logging.LoggingApplicationListener"
}
],
"properties": [
{
"name": "spring.banner.charset",
"type": "java.nio.charset.Charset",
"description": "Banner file encoding.",
"defaultValue": "UTF-8"
},
{
"name": "spring.banner.location",
"type": "org.springframework.core.io.Resource",
"description": "Banner text resource location.",
"defaultValue": "classpath:banner.txt"
},
{
"name": "spring.banner.image.location",
"type": "org.springframework.core.io.Resource",
"description": "Banner image file location (jpg or png can also be used).",
"defaultValue": "classpath:banner.gif"
},
{
"name": "spring.banner.image.width",
"type": "java.lang.Integer",
"description": "Width of the banner image in chars.",
"defaultValue": 76
},
{
"name": "spring.banner.image.height",
"type": "java.lang.Integer",
"description": "Height of the banner image in chars (default based on image height)."
},
{
"name": "spring.banner.image.margin",
"type": "java.lang.Integer",
"description": "Left hand image margin in chars.",
"defaultValue": 2
},
{
"name": "spring.banner.image.invert",
"type": "java.lang.Boolean",
"description": "Whether images should be inverted for dark terminal themes.",
"defaultValue": false
},
{
"name": "debug",
"type": "java.lang.Boolean",
"description": "Enable debug logs.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": false
},
{
"name": "logging.config",
"type": "java.lang.String",
"description": "Location of the logging configuration file. For instance, `classpath:logback.xml` for Logback.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener"
},
{
"name": "logging.exception-conversion-word",
"type": "java.lang.String",
"description": "Conversion word used when logging exceptions.",
"defaultValue": "%wEx",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener"
},
{
"name": "logging.file",
"type": "java.lang.String",
"description": "Log file name (for instance, `myapp.log`). Names can be an exact location or relative to the current directory.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener"
},
{
"name": "logging.file.max-size",
"type": "java.lang.String",
"description": "Maximum log file size. Only supported with the default logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": "10MB"
},
{
"name": "logging.file.max-history",
"type": "java.lang.Integer",
"description": "Maximum of archive log files to keep. Only supported with the default logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": 0
},
{
"name": "logging.level",
"type": "java.util.Map" ,
"description": "Log levels severity mapping. For instance, `logging.level.org.springframework=DEBUG`.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener"
},
{
"name": "logging.path",
"type": "java.lang.String",
"description": "Location of the log file. For instance, `/var/log`.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener"
},
{
"name": "logging.pattern.console",
"type": "java.lang.String",
"description": "Appender pattern for output to the console. Supported only with the default Logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": "%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"
},
{
"name": "logging.pattern.dateformat",
"type": "java.lang.String",
"description": "Appender pattern for log date format. Supported only with the default Logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": "yyyy-MM-dd HH:mm:ss.SSS"
},
{
"name": "logging.pattern.file",
"type": "java.lang.String",
"description": "Appender pattern for output to a file. Supported only with the default Logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": "%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"
},
{
"name": "logging.pattern.level",
"type": "java.lang.String",
"description": "Appender pattern for log level. Supported only with the default Logback setup.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": "%5p"
},
{
"name": "logging.register-shutdown-hook",
"type": "java.lang.Boolean",
"description": "Register a shutdown hook for the logging system when it is initialized.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": false
},
{
"name": "spring.application.name",
"type": "java.lang.String",
"sourceType": "org.springframework.boot.context.ContextIdApplicationContextInitializer",
"description": "Application name."
},
{
"name": "spring.beaninfo.ignore",
"type": "java.lang.Boolean",
"sourceType": "org.springframework.boot.context.config.ConfigFileApplicationListener",
"description": "Whether to skip search of BeanInfo classes.",
"defaultValue": true
},
{
"name": "spring.config.additional-location",
"type": "java.lang.String",
"sourceType": "org.springframework.boot.context.config.ConfigFileApplicationListener",
"description": "Config file locations used in addition to the defaults."
},
{
"name": "spring.config.name",
"type": "java.lang.String",
"sourceType": "org.springframework.boot.context.config.ConfigFileApplicationListener",
"description": "Config file name.",
"defaultValue": "application"
},
{
"name": "spring.config.location",
"type": "java.lang.String",
"sourceType": "org.springframework.boot.context.config.ConfigFileApplicationListener",
"description": "Config file locations that replace the defaults."
},
{
"name": "spring.jta.narayana.expiry-scanners",
"defaultValue": [
"com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner"
]
},
{
"name": "spring.main.banner-mode",
"type": "org.springframework.boot.Banner$Mode",
"sourceType": "org.springframework.boot.SpringApplication",
"description": "Mode used to display the banner when the application runs.",
"defaultValue": "console"
},
{
"name": "spring.main.show-banner",
"type": "java.lang.Boolean",
"sourceType": "org.springframework.boot.SpringApplication",
"description": "Display the banner when the application runs.",
"defaultValue": true,
"deprecation": {
"replacement": "spring.main.banner-mode"
}
},
{
"name": "spring.main.sources",
"type": "java.util.Set" ,
"sourceType": "org.springframework.boot.SpringApplication",
"description": "Sources (class names, package names, or XML resource locations) to include in the ApplicationContext."
},
{
"name": "spring.main.web-application-type",
"type": "org.springframework.boot.WebApplicationType",
"sourceType": "org.springframework.boot.SpringApplication",
"description": "Flag to explicitly request a specific type of web application. If not set, auto-detected based on the classpath."
},
{
"name": "spring.main.web-environment",
"type": "java.lang.Boolean",
"sourceType": "org.springframework.boot.SpringApplication",
"description": "Run the application in a web environment (auto-detected by default).",
"deprecation": {
"replacement": "spring.main.web-application-type"
}
},
{
"name": "spring.mandatory-file-encoding",
"sourceType": "org.springframework.boot.context.FileEncodingApplicationListener",
"type": "java.nio.charset.Charset",
"description": "Expected character encoding the application must use."
},
{
"name": "spring.output.ansi.enabled",
"type": "org.springframework.boot.ansi.AnsiOutput$Enabled",
"description": "Configures the ANSI output.",
"defaultValue": "detect"
},
{
"name": "spring.pid.file",
"type": "java.lang.String",
"description": "Location of the PID file to write (if ApplicationPidFileWriter is used).",
"sourceType": "org.springframework.boot.context.ApplicationPidFileWriter"
},
{
"name": "spring.pid.fail-on-write-error",
"type": "java.lang.Boolean",
"description": "Fails if ApplicationPidFileWriter is used but it cannot write the PID file.",
"sourceType": "org.springframework.boot.context.ApplicationPidFileWriter"
},
{
"name": "spring.profiles.active",
"type": "java.util.List" ,
"sourceType": "org.springframework.boot.context.config.ConfigFileApplicationListener",
"description": "Comma-separated list of active profiles. Can be overridden by a command line switch."
},
{
"name": "spring.profiles.include",
"type": "java.util.List" ,
"sourceType": "org.springframework.boot.context.config.ConfigFileApplicationListener",
"description": "Unconditionally activate the specified comma-separated list of profiles (or list of profiles if using YAML)."
},
{
"name": "trace",
"type": "java.lang.Boolean",
"description": "Enable trace logs.",
"sourceType": "org.springframework.boot.context.logging.LoggingApplicationListener",
"defaultValue": false
},
{
"name": "banner.charset",
"type": "java.nio.charset.Charset",
"description": "Banner file encoding.",
"defaultValue": "UTF-8",
"deprecation": {
"replacement": "spring.banner.charset",
"level": "error"
}
},
{
"name": "banner.image.height",
"type": "java.lang.Integer",
"description": "Banner image height (in chars).",
"deprecation": {
"replacement": "spring.banner.image.height",
"level": "error"
}
},
{
"name": "banner.image.invert",
"type": "java.lang.Boolean",
"description": "Invert images for dark console themes.",
"defaultValue": false,
"deprecation": {
"replacement": "spring.banner.image.invert",
"level": "error"
}
},
{
"name": "banner.image.location",
"type": "org.springframework.core.io.Resource",
"description": "Banner image file location (jpg/png can also be used).",
"defaultValue": "banner.gif",
"deprecation": {
"replacement": "spring.banner.image.location",
"level": "error"
}
},
{
"name": "banner.image.margin",
"type": "java.lang.Integer",
"description": "Left hand image margin (in chars).",
"deprecation": {
"replacement": "spring.banner.image.margin",
"level": "error"
}
},
{
"name": "banner.image.width",
"type": "java.lang.Integer",
"description": "Banner image width (in chars).",
"deprecation": {
"replacement": "spring.banner.image.width",
"level": "error"
}
},
{
"name": "banner.location",
"type": "org.springframework.core.io.Resource",
"description": "Banner text resource location.",
"defaultValue": "classpath:banner.txt",
"deprecation": {
"replacement": "spring.banner.location",
"level": "error"
}
},
{
"name": "spring.application.index",
"type": "java.lang.Integer",
"description": "Application index.",
"deprecation": {
"level": "error",
"reason": "Application context ids are now unique by default."
}
}
],
"hints": [
{
"name": "logging.level.keys",
"values": [
{
"value": "root",
"description": "Root logger used to assign the default logging level."
}
],
"providers": [
{
"name": "logger-name"
}
]
},
{
"name": "logging.level.values",
"values": [
{
"value": "trace"
},
{
"value": "debug"
},
{
"value": "info"
},
{
"value": "warn"
},
{
"value": "error"
},
{
"value": "fatal"
},
{
"value": "off"
}
],
"providers": [
{
"name": "any"
}
]
},
{
"name": "spring.profiles.active",
"providers": [
{
"name": "spring-profile-name"
}
]
},
{
"name": "spring.profiles.include",
"providers": [
{
"name": "spring-profile-name"
}
]
}
]
}
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
application.yml
management:
endpoints:
web:
exposure:
include: 'loggers'
@RestController
public class LogController {
private static final Logger LOGGER = LoggerFactory.getLogger(LogController.class);
@GetMapping("/log")
public String log() {
LOGGER.debug("debug");
LOGGER.info("info");
LOGGER.warn("warn");
LOGGER.error("error");
return "log";
}
}
默认日志级别为info,我们修改指定包下某个类的日志级别为debug级别
curl -X POST http://localhost:8080/actuator/loggers/com.example.springbootlog.LogController \
-H "Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8" \
--data '{"configuredLevel":"debug"}'
参考文章:https://mp.weixin.qq.com/s/LPczTb8mR8ptycPKlPbnzg