参考:https://www.cnblogs.com/warking/p/5710303.html
https://www.cnblogs.com
一.logback简介
1.logback:
Logback是由log4j创始人设计的另一个开源日志组件。(好的日志记录方式可以提供我们足够多的定位错误的依据)。
2.主要有三个模块组成:
logback-core:其它两个模块的基础模块。
logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API使你可以很方便地更换成其它日志系统如log4j或JDK14 Logging。()
logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能。(第三方软件通过该模块访问日志)
3.logback主要标签:
logger:日志的记录器(可以存放日志对象,也可以第一日志的级别,类型等);
appender:指定日志输出的媒介,即输出地(可以是控制台,可以是文件,远程套接字服务器等);
layout:用来格式化日志信息的输出。
二.logback优于log4的原因
Logback对log4j进行了大量改进,Logback就是更好的log4j,这里参考官方文档列举主要部分【官网的翻译:】。
更快的实施:基于我们之前关于log4j的工作,已经重写了logback内部,以便在某些关键执行路径上执行大约十倍的速度。不仅logback组件更快,而且内存占用更少。
大量的测试:Logback提供了在几年和无数小时工作中开发的大量测试。虽然log4j也经过测试,但logback将测试带到了完全不同的水平。我们认为,这是优先于log4j进行logback的最重要原因。您希望您的日志框架即使在不利条件下也能够坚如磐石且可靠。
logback本身就实现了SLF4J:由于Logger
logback-classic中的类本身实现了SLF4J API,因此在调用带有logback-classic作为底层实现的SLF4J记录器时,会产生零开销。此外,由于logback-classic强烈鼓励使用SLF4J作为其客户端API,如果需要切换到log4j或jul,可以通过将一个jar文件替换为另一个来实现。您无需通过SLF4J API触摸代码记录。这可以大大减少切换日志框架所涉及的工作。
XML或Groovy的中的配置文件:配置logback的传统方法是通过XML文件。文档中的大多数示例都使用此XML语法。但是,从logback版本0.9.22开始,也支持用Groovy编写的配置文件。与XML相比,Groovy风格的配置更直观,更一致,并且语法更短。还有一个工具可以自动将logback.xml文件迁移到logback.groovy。
自动重新加载配置文件:Logback-classic可以在修改后自动重新加载其配置文件。扫描过程快速,无争用,并且动态扩展到数百个线程上每秒数百万次调用。它还可以播放中的应用服务器以及更一般地内的JEE环境,它并没有涉及扫描一个单独的线程的创建。
从I / O故障中恢复正常:Logback FileAppender
及其所有子类(包括RollingFileAppender
)可以从I / O故障中正常恢复。因此,如果文件服务器暂时失败,您不再需要重新启动应用程序只是为了让日志记录再次运行。一旦文件服务器恢复,相关的logback appender将从之前的错误状态透明地快速恢复。
自动删除旧的日志存档:通过设置TimeBasedRollingPolicy 或SizeAndTimeBasedFNATP的maxHistory属性,可以控制最大归档文件数。如果您的滚动策略要求每月滚动并且您希望保留一年的日志,只需将maxHistory属性设置为12.将自动删除超过12个月的存档日志文件。
自动压缩存档的日志文件:RollingFileAppender 可以在翻转期间自动压缩归档日志文件。压缩始终以异步方式发生,因此即使对于大型日志文件,您的应用程序也不会在压缩期间被阻止。
谨慎模式:在谨慎模式下,FileAppender
在多个JVM上运行的多个实例可以安全地写入同一个日志文件。由于某些限制,谨慎模式延伸至RollingFileAppender
。
莉莉丝:Lilith是一个用于logback的日志记录和访问事件查看器。它与log4j的电锯相当,只不过Lilith设计用于处理大量的测井数据而不会退缩。
条件处理配置文件:开发人员经常需要在针对不同环境(如开发,测试和生产)的多个logback配置文件之间进行操作。这些配置文件有很多共同点,仅在少数几个地方有所不同。为了避免重复,支持的logback 的配置文件条件处理的帮助下 <if>
,<then>
和 <else>
元件,使得一个配置文件能充分瞄准几个环境。
过滤器:Logback提供了大量的过滤功能,远远超出了log4j所提供的功能。例如,假设您在生产服务器上部署了业务关键型应用程序。在处理大量事务的情况下,将日志记录级别设置为WARN,以便仅记录警告和错误。现在想象一下,您遇到的错误可以在生产系统上重现,但由于这两个环境(生产/测试)之间存在未指定的差异,因此在测试平台上仍然难以捉摸。
使用log4j,您唯一的选择是将生产系统上的日志记录级别降低到DEBUG以尝试识别问题。不幸的是,这将产生大量的记录数据,使分析变得困难。更重要的是,广泛的日志记录会影响应用程序在生产系统上的性能。
使用logback,您可以选择将所有用户的日志记录保持在WARN级别,但负责识别问题的Alice用户除外。当Alice登录时,她将以DEBUG级别登录,而其他用户可以继续登录WARN级别。这一壮举可以通过在配置文件中添加4行XML来实现。在手册MDCFilter
的相关部分中搜索 。
SiftingAppender:SiftingAppender 是一个非常多才多艺的appender。它可用于根据任何给定的运行时属性分离(或筛选)日志记录。例如,SiftingAppender
可以根据用户会话分离日志记录事件,以便每个用户生成的日志进入不同的日志文件,每个用户一个日志文件。
堆栈跟踪包装数据:当logback打印异常时,堆栈跟踪将包含打包数据。以下是logback-demo Web应用程序生成的示例堆栈跟踪。
14:28:48.835 [btpool0-7] INFO cqldemo.prime.PrimeAction - 99不是有效值 java.lang.Exception:99无效 在ch.qos.logback.demo.prime.PrimeAction.execute(PrimeAction.java:28)[classes /:na] at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431)[struts-1.2.9.jar:1.2.9] 在org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236)[struts-1.2.9.jar:1.2.9] at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432)[struts-1.2.9.jar:1.2.9] 在javax.servlet.http.HttpServlet.service(HttpServlet.java:820)[servlet-api-2.5-6.1.12.jar:6.1.12] 在org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502)[jetty-6.1.12.jar:6.1.12] 在ch.qos.logback.demo.UserServletFilter.doFilter(UserServletFilter.java:44)[classes /:na] 在org.mortbay.jetty.servlet.ServletHandler $ CachedChain.doFilter(ServletHandler.java:1115)[jetty-6.1.12.jar:6.1.12] 在org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:361)[jetty-6.1.12.jar:6.1.12] 在org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:417)[jetty-6.1.12.jar:6.1.12] at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)[jetty-6.1.12.jar:6.1.12]
从上面可以看出,应用程序正在使用Struts 1.2.9版,并在jetty版本6.1.12下部署。因此,堆栈跟踪将快速告知读者介入异常的类以及它们所属的包和包版本。当您的客户向您发送堆栈跟踪时,作为开发人员,您将不再需要让他们向您发送有关他们正在使用的软件包版本的信息。该信息将成为堆栈跟踪的一部分。有关详细信息,请参阅“%xThrowable”转换字。
这个功能非常有用,因为有些用户错误地认为它是IDE的一个功能。
Logback-access,即带有大脑的HTTP访问日志记录,是logback的一个组成部分,最后但同样重要的是,logback-access模块是logback发行版的一部分,它与Servlet容器(如Jetty或Tomcat)集成,以提供丰富而强大的HTTP访问日志功能。由于logback-access是初始设计的一部分,因此您喜欢的所有logback-classic功能也可用于logback-access。
三.logback配置
1.Java程序在运行时按照一定的顺序加载先关的logback配置文件:
(1)如果使用Java -Dlogback.configurationFile=xxxx/xxx.xml(指定配置文件),就会优先加载该配置文件;
(2)如果没有,就会在classpath下寻找是是否有logback.groovy(即logback支持groovy与xml两种配置方式);
(3)在classpath下寻找是否有logback-test.xml;
(4)在classpath下寻找是否有logback.xm;
(5)如果上面都没找到且JDK版本是1.6以上,就会查找ch.qos.logback.classic.spi.Configuraror会自动调用相关类做相关配置;
(6)如果还没有,就会自动去找ch.qos.logback.classic.BasicConfigurator的configure这个实现类直接将日志输入到控制台。
2.配置介绍:
(1)Logger、appender及layout
Logger作为日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。
Appender主要用于指定日志输出的目的地,目的地可以是控制台、文件、远程套接字服务器、 MySQL、PostreSQL、 Oracle和其他数据库、 JMS和远程UNIX Syslog守护进程等。
Layout 负责把事件转换成字符串,格式化的日志信息的输出。
(2)logger context
各个logger 都被关联到一个 LoggerContext,LoggerContext负责制造logger,也负责以树结构排列各logger。其他所有logger也通过org.slf4j.LoggerFactory 类的静态方法getLogger取得。 getLogger方法以 logger名称为参数。用同一名字调用LoggerFactory.getLogger 方法所得到的永远都是同一个logger对象的引用。
(3)有效级别及级别的继承
Logger 可以被分配级别。级别包括:TRACE、DEBUG、INFO、WARN 和 ERROR,定义于ch.qos.logback.classic.Level类。如果 logger没有被分配级别,那么它将从有被分配级别的最近的祖先那里继承级别。root logger 默认级别是 DEBUG。
(4)打印方法与基本的选择规则
打印方法决定记录请求的级别。例如,如果 L 是一个 logger 实例,那么,语句 L.info("..")是一条级别为 INFO的记录语句。记录请求的级别在高于或等于其 logger 的有效级别时被称为被启用,否则,称为被禁用。记录请求级别为 p,其 logger的有效级别为 q,只有则当 p>=q时,该请求才会被执行。
该规则是 logback 的核心。级别排序为: TRACE < DEBUG < INFO < WARN < ERROR(常用的为标红的)
3.logback.xml配置(我的配置):
<?xml version="1.0" encoding="UTF-8"?> <!-- scan="true"表示配置文件发生改变时自动更新,scanPeriod表示多久扫描一次配置文件看是否更新 debug="true"会打印logback内部的日志信息,查看运行状态 --> <configuration scan="true" scanPeriod="60 seconds" debug="false"> <!-- 定义参数常量 --> <!-- TRACE < DEBUG < INFO < WARN < ERROR --> <!-- logger.trace("msg") 某个级别的文件只能显示该级别或者大于该级别的日志 --> <property name="log.level" value="debug" /> <!-- 文件保留多长时间 --> <property name="log.maxHistory" value="30" /> <!-- 日志存储的根路径 ${catalina.base}是存储Tomcat实例的根目录 --> <property name="log.filePath" value="${catalina.base}/logs/webapps" /> <!-- 日志展示格式 %d表示时间格式,[%thread]线程 ,level级别,logger表示哪一个package下的哪一个类,%msg表示信息 --> <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss:SSS}[%thread]-5level%logger{50}-%msg%n" /> <!-- 控制台设置 --> <!-- 日志输出的地方 --> <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender"> <!-- 相当于一个layout(将envent事件转换为byte数组),但是它即负责转换字符串,也负责输入到日志文件中 --> <encoder> <!-- 定义格式 --> <pattern>${log.pattern}</pattern> </encoder> </appender> <!-- DEBUG --> <appender name="debugAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 文件路径 --> <file>${log.filePath}/debug.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 文件名称 --> <fileNamePattern>${log.filePath}/debug/.%d{yyyy-MM-dd}.log.gz </fileNamePattern> <!-- 文件最大历史数量 --> <maxHistory>${log.maxHistory}</maxHistory> </rollingPolicy> <encoder> <pattern>${log.pattern}</pattern> </encoder> <!-- 过滤器 过滤level为DEBUG的日志信息 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>DEBUG</level> <onMach>ACCEPT</onMach> <OnMismath>DENY</OnMismath> </filter> </appender> <!-- INFO --> <appender name="infoAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 文件路径 --> <file>${log.filePath}/info.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 文件名称 --> <fileNamePattern>${log.filePath}/info/.%d{yyyy-MM-dd}.log.gz </fileNamePattern> <!-- 文件最大历史数量 --> <maxHistory>${log.maxHistory}</maxHistory> </rollingPolicy> <encoder> <pattern>${log.pattern}</pattern> </encoder> <!-- 过滤器 过滤level为INFO的日志信息 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <onMach>ACCEPT</onMach> <OnMismath>DENY</OnMismath> </filter> </appender> <!-- ERROR --> <appender name="errorAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 文件路径 --> <file>${log.filePath}/error.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 文件名称 --> <fileNamePattern>${log.filePath}/error/.%d{yyyy-MM-dd}.log.gz </fileNamePattern> <!-- 文件最大历史数量 --> <maxHistory>${log.maxHistory}</maxHistory> </rollingPolicy> <encoder> <pattern>${log.pattern}</pattern> </encoder> <!-- 过滤器 过滤level为ERROR的日志信息 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMach>ACCEPT</onMach> <OnMismath>DENY</OnMismath> </filter> </appender> <!-- 指定记录的package,${log.level}指定哪个level以上,addtivity默认为true,会将父类root里的appender-ref也会放到自己的logger中 --> <!-- 注:一个类只能定义一个logger --> <logger name="com.swpu.o2o" level="${log.level}" addtivity="true"> <!-- 与appender绑定 --> <appender-ref ref="debugAppender" /> <appender-ref ref="infoAppender" /> <appender-ref ref="errorAppender" /> </logger> <!-- 特殊的logger,如果logger的level没有指定,默认会继承root的level --> <root level="info"> <appender-ref ref="consoleAppender" /> </root> </configuration>
4.测试:
package com.swpu.o2o.web.superadmin; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.swpu.o2o.entity.Area; import com.swpu.o2o.service.AreaService; import ch.qos.logback.classic.Logger; @Controller @RequestMapping("/superadmin") public class AreaController { org.slf4j.Logger logger = LoggerFactory.getLogger(AreaController.class); @Autowired private AreaService areaService; @RequestMapping(value = "/listarea", method = RequestMethod.GET) @ResponseBody private Map<String, Object> listArea() { logger.info("===start==="); Long startTime=System.currentTimeMillis(); Map<String, Object> modelMap = new HashMap<String, Object>(); List<Area> list = new ArrayList<Area>(); try { list = areaService.getAreaList(); modelMap.put("rows", list); modelMap.put("total", list.size()); } catch (Exception e) { e.printStackTrace(); modelMap.put("success", false); modelMap.put("errMsg", e.toString()); } logger.error("test error"); Long endTime=System.currentTimeMillis(); logger.debug("costTime:[{}ms]",endTime-startTime); logger.info("===end==="); return modelMap; } }
在所配置的目录查看成功: