本文的架构如下
一.log是什么?
学习java-log之前,我们先回忆下,我们最开始是怎么调试java的代码,是通过System.out.println()输出,定点输出查看代码的输出是否正确.所以我们先从System.out引入log.
1.java-log的介绍
(1)System.out
主要作用:
1.主要是帮我们查看程序的输出
2.帮助我们调试程序.
(2)System.out和log之间的区别
- system.out检验程序的输出比log方便,直接输出就可以了,log还需要调用log对象.
- system.out调试记录我们程序的注意的地方,log就比system.out强大太多了.log天生能控制输出日志的地方,比如console还是文件,日志的等级,日志输出内容格式等.
(3)产生log的原因
正是由于system.out输出日志有如下方面的弊端:
system.out做为日志的不合适:
- 不能自由选择输出到文件或者控制台或者数据库等
- 不能定义标准的输出等级,虽然我们也可以在system.out中通过文本控制,约束我们输出的等级,但是这样的约束太柔性了
- 日志输出通常需要定位当前执行的线程或者类,还需要时间等等,但是system.out虽然也可以达到,但是需要自己写很多的代码,才能达到如此的功能.
- 当我们实际开发时,由于开发的人员众多,system.out不能约束我们具体哪些包输出日志等级等等.
就是由于上述的system.out的弊端所以产生了log的需求.
2.自己设计java-log框架
从1得到,我们如果自己设计的框架比需满足我们的需求.
二.java-log的常用框架
由于我们自己去开发这样的框架还是非常的花费时间,所以我们选择市面上的日志框架.
日志框架及组合
左边选一个门面(抽象层)、右边来选一个实现;
slf4j适配其他日志实现:
遗留问题:
a(slf4j+logback): Spring(commons-logging)、Hibernate(jboss-logging)、MyBatis、xxxx 统一日志记录,即使是别的框架和我一起统一使用slf4j进行输出?
1.JDKLog:日志小刀
JDKLog是JDK官方提供的一个记录日志的方式,直接在JDK中就可以使用。
public class JDKLog
{
public static void main( String[] args )
{
Logger logger = Logger.getLogger("JDKLog");
logger.info("Hello World.");
logger.warning("本行代码有可能出现问题");
logger.log(Level.ALL,"all日志输出");
}
}
运行结果:
总结:
JDKLog 的有点是使用非常简单,直接在 JDK 中就可以使用。但 JDKLog 功能比较太过于简单,不支持占位符显示,拓展性比较差,所以现在用的人也很少。
2.Log4J:日志大炮
Log4J 是 Apache 的一个日志开源框架,有多个分级(DEBUG/INFO/WARN/ERROR)记录级别,可以很好地将不同日志级别的日志分开记录,极大地方便了日志的查看。
Log4J 有 1.X 版本和 2.X 版本,现在官方推荐使用 2.X 版本,2.X 版本在架构上进行了一些升级,配置文件也发生了一些变化。但好在官方的配置说明文档非常清楚,通过查阅文档能解决大部分的问题。
步骤:
(1)导入jar包
<!-- 整合log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>
(2)配置log4j的配置文件
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
(3)编写代码
package com.gl.jdk_log;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
/**
* @ClassName Log4jLog
* @Author gl
* @Date 2020/4/18 15:17
* @Description
* Log4J Demo
* 注意这里是org.apache.log4j下的包
*/
public class Log4jLog {
public static void main(String args[]) {
Logger logger = LogManager.getLogger(Log4jLog.class);
logger.debug("Debug Level");
logger.info("Info Level");
logger.warn("Warn Level");
logger.error("Error Level");
}
}
(4)输出结果
单独使用log4j可能会导致log4j日志输出不成功
不成功:
log4j:WARN No appenders could be found for logger (com.gl.jdk_log.Log4jLog).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
成功:
10:16:08.279 [main] INFO com.chanshuyi.Log4jLog - Info Level
10:16:08.280 [main] WARN com.chanshuyi.Log4jLog - Warn Level
10:16:08.280 [main] ERROR com.chanshuyi.Log4jLog - Error
总结:
从上面的使用步骤可以看出 Log4J 的使用稍微复杂一些,但是条理还是很清晰的。而且因为 Log4J 有多个分级(DEBUG/INFO/WARN/ERROR)记录级别,所以可以很好地记
录不同业务问题。因为这些优点,所以在几年前几乎所有人都使用 Log4J 作为日志记录框架,群众基础可谓非常深厚。
但 Log4J 本身也存在一些缺点,比如不支持使用占位符,不利于代码阅读等缺点。但是相比起 JDKLog,Log4J 可以说是非常好的日志记录框架了。
3.LogBack:日志火箭
LogBack 其实可以说是 Log4J 的进化版,因为它们两个都是同一个人(Ceki Gülcü)设计的开源日志组件。LogBack 除了具备 Log4j 的所有优点之外,还解决了 Log4J 不能使用占位符的问题。
步骤:
1.导入jar包
其实这里可以和上面log4j的配置相同,因为logback是log4j的升级版
<!-- 整合log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>
2.编写配置文件
其实这里可以和上面log4j的配置相同,因为logback是log4j的升级版
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
3.代码编写
注意导入的是org.slf4j下的
package com.gl.jdk_log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @ClassName LogBack
* @Author gl
* @Date 2020/4/18 15:59
* @Description
* LogBack Demo
*/
public class LogBack {
static final Logger logger = LoggerFactory.getLogger(LogBack.class);
public static void main(String[] args) {
logger.trace("Trace Level.");
logger.debug("Debug Level.");
logger.info("Info Level.");
logger.warn("Warn Level.");
logger.error("Error Level.");
}
}
4.输出结果
DEBUG [main] - Debug Level
INFO [main] - Info Level
WARN [main] - Warn Level
ERROR [main] - Error Level
Process finished with exit code 0
LogBack 解决了 Log4J 不能使用占位符的问题,这使得阅读日志代码非常方便。除此之外,LogBack 比 Log4J 有更快的运行速度,更好的内部实现。并且 LogBack 内部集成了 SLF4J 可以更原生地实现一些日志记录的实现。
4.SLF4J:适配器
(1)产生的原因?
由于我们在开发的时候,有各种不同的相同,并且不同的项目有不同的日志框架,所以我们整合项目的时候需要把各种日志框架整合到一起,那怎么办?
一个最死板的方法就是一行行代码修改,把之前用 JDKLog 的日志代码全部修改成 Log4J 的日志接口。但是这种方式不仅效率低下,而且做的工作都是重复性的工作,这怎么能忍呢。
于是迫切需要我们有一种更加方便的框架api能够整合大部分的日志框架,就好比java的JDBC,其他的mysql,oracle只需要实现我们的api接口即可,我们只需要学习使用api就可以了,由此产生了slf4j
(2) slf4j的介绍
SLF4J(Simple Logging Facade for Java,即Java简单日志记录接口集)是一个日志的接口规范,它对用户提供了统一的日志接口,屏蔽了不同日志组件的差异。这样我们在编写代码的时候只需要看 SLF4J 这个接口文档即可,不需要去理会不同日之框架的区别。而当我们需要更换日志组件的时候,我们只需要更换一个具体的日志组件Jar包就可以了。
而整合 SLF4J 和日志框架使用也是一件很简单的事情。
(3)slf4j 整合其他log框架
由于slf4j的作用就是定义统一的api接口,所以只要是slf4j整合的log框架,不论是jdklog还是log4j还是logback,都只是导入的包不同,其配置文件和代码其实都是相同的.
步骤:
slf4j+JDKLog
- 1.导入依赖
选择一:slf4j+jdklog的依赖
<!-- slf4j-JDKLog -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.21</version>
</dependency>
选择二:slf4j+log4j的依赖
<!-- slf4j-log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>
选择三:slf4j+logback的依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<logback.version>1.2.3</logback.version>
<slf4j.version>1.7.21</slf4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
</dependencies>
- 2.书写配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- 1、Appenders -->
<!--日志输出到控制台-->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c - %m%n"/>
</layout>
</appender>
<!--日志输出到文件中-->
<!--每天产生一个日志文件,文件所在位置为项目目录下面的logs文件夹中,文件名为server.log-->
<appender name="file" class="org.apache.log4j.DailyRollingFileAppender">
<param name="File" value="logs/server.log"/>
<param name="DatePattern" value="'.'yyyy-MM-dd'.log'"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%d{yyyy-MM-dd HH:mm:ss.SSS\} %-5p] [%t] %c{3\} - %m%n"/>
</layout>
</appender>
<!--2、Application Logs-->
<logger name="com.zhoujie">
<level value="info"/>
</logger>
<!-- 3rdparty Loggers -->
<logger name="org.springframework.core">
<level value="info"/>
</logger>
<logger name="org.springframework.beans">
<level value="info"/>
</logger>
<logger name="org.springframework.context">
<level value="info"/>
</logger>
<logger name="org.springframework.web">
<level value="info"/>
</logger>
<logger name="org.springframework.jdbc">
<level value="info"/>
</logger>
<logger name="org.mybatis.spring">
<level value="info"/>
</logger>
<logger name="java.sql">
<level value="info"/>
</logger>
<logger name="java.sql">
<level value="info"/>
</logger>
<logger name="net.sf.ehcache.store.disk.Segment">
<level value="warn"/>
</logger>
<!--Root Logger-->
<root>
<priority value="debug"/>
<appender-ref ref="console"/>
<!--<appender-ref ref="file"/>-->
</root>
</log4j:configuration>
- 3.编写代码
package com.gl.jdk_log;
/**
* @ClassName Slf4jJDKLog
* @Author gl
* @Date 2020/4/18 16:25
* @Description
*
* SLF4J + JDKLog
*/
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jJDKLog {
final static Logger logger = LoggerFactory.getLogger(Slf4jJDKLog.class);
public static void main(String[] args) {
logger.trace("Trace Level.");
logger.info("Info Level.");
logger.warn("Warn Level.");
logger.error("Error Level.");
}
}
- 测试结果
2020-04-18 16:34:43 INFO com.gl.jdk_log.Slf4jJDKLog - Info Level.
2020-04-18 16:34:43 WARN com.gl.jdk_log.Slf4jJDKLog - Warn Level.
2020-04-18 16:34:43 ERROR com.gl.jdk_log.Slf4jJDKLog - Error Level.
三.使用日志的注意事项
我们选择一个日志,到使用日志是由非常大的讲究的,每一个日志框架都关系着以后程序的bug调试.所以我们考虑log有以下三个方面也要考虑.
1.选择框架
选择slf4j+logback的原因:
- LogBack 自身实现了 SLF4J 的日志接口,不需要 SLF4J 去做进一步的适配。
- LogBack 自身是在 Log4J 的基础上优化而成的,其运行速度和效率都比 LOG4J 高。
- SLF4J + LogBack 支持占位符,方便日志代码的阅读,而 LOG4J 则不支持。
从上面几点来看,SLF4J + LogBack是一个较好的选择。
LogBack 被分为3个组件:logback-core、logback-classic 和 logback-access。
- logback-core 提供了 LogBack 的核心功能,是另外两个组件的基础。
- logback-classic 则实现了 SLF4J 的API,所以当想配合 SLF4J 使用时,需要将 logback-classic
引入依赖中。 - logback-access 是为了集成Servlet环境而准备的,可提供HTTP-access的日志接口。
2.配置文件的配置
logback的配置文件(其实不仅仅是logback,所有的slf4j的配置文件都可以用)
这是配置文件的架构图.如咱们自己设想的日志框架应该满足的,这里都能满足.
LogBack配置文件可以分为几个节点,其中 Configuration 是根节点,Appender、Logger、Root是Configuration的子节点。
1)appender节点
是的子节点,是负责写日志的组件。
主要作用:
1.定义输出地方,控制台还是文件
2.控制是否异步
3.控制输出的log语句的格式
appender有两个必要属性name、class 。name指定appender的名称,class指定appender的全限定名class,主要包括:
- ch.qos.logback.core.ConsoleAppender 控制台输出
- ch.qos.logback.core.FileAppender 文件输出
- ch.qos.logback.core.RollingFileAppender 文件滚动输出
- ch.qos.logback.classic.AsyncAppender异步输出
(1)ConsoleAppender
把日志添加到控制台,有如下节点:
encoder : 对日志进行格式化。
target : 字符串System.out 或者 System.err, 默认 System.out;
pattern:定义输出的格式-详情请参考官网
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- conf consoel out -->
<appender name ="console_out" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date [%thread] %-5level %logger - %message%newline</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="console_out" />
</root>
</configuration>
(2)FileAppender
把日志添加到文件,有如下节点:
file:被写入的文件名,可以是相对目录 , 也可以是绝对目录 , 如果目录不存在则会自动创建。
append:如果是true , 日志被追加到文件结尾 , 如果是false,清空现存文件 , 默认是true。
encoder:对日志进行格式化 [具体的转换符说明请参见官网.]
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appender name="file_out" class="ch.qos.logback.core.FileAppender">
<file>logs/debug.log</file>
<encoder>
<pattern>%date [%thread] %-5level %logger - %message%newline</pattern>
</encoder>
</appender>
</configuration>
(3)rollingFileAppender
滚动纪录文件,先将日志记录到指定文件,当符合某种条件时,将日志记录到其他文件,有如下节点:
<file:被写入的文件名,可以是相对目录,也可以解决目录,如果目录不存在则自动创建。
<append:如果是true,日志被追加到文件结尾,如果是false,清空现存文件,默认是true。
<encoder:对日志进行格式化。
<rollingPolicy:当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名。
<appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d -- %msg%n</pattern>
</encoder>
<!--滚动策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--路径-->
<fileNamePattern>e:/info-%d.log</fileNamePattern>
</rollingPolicy>
</appender>
(4)AsyncAppender
<!-- 异步输出 -->
21 <appender name ="ASYNC" class= "ch.qos.logback.classic.AsyncAppender">
22 <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
23 <discardingThreshold>0</discardingThreshold>
24 <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
25 <queueSize>256</queueSize>
26 <!-- 添加附加的appender,最多只能添加一个 -->
27 <appender-ref ref ="ROLLING-FILE-1"/>
28 </appender>
2)logger节点
logger是的子节点,来设置某一个包或者具体的某一个类的日志打印级别,以及指定。
logger节点的作用: 指定特定包下或者类的日志输出级别.
logger仅有一个name属性,两个可选属性 level/addtivity。
name:用来指定受此loger约束的某一个包或者具体的某一个类。
level:用来设置打印级别,大小写无关。可选值有TRACE、DEBUG、INFO、WARN、ERROR、ALL和OFF。还有一个特俗值INHERITED 或者 同义词NULL,代表强制执行上级的级别。如果未设置此属性,那么当前logger将会继承上级的级别。
addtivity:是否向上级logger传递打印信息,默认为true;
可以包含零个或多个元素,表示这个appender将会添加到logger。
<logger name="java" additivity="false" />
<logger name="java.lang" level="DEBUG">
<appender-ref ref="STDOUT" />
</logger>
3)root节点
元素配置根logger。其实就是相当于所有路劲的根路径,指定配置全局的日志输出,如果其子节点中也就是logger中已经配置了包下的输出级别,则以logger为准,没有配置则以root为准.
该元素有一个level属性,没有name属性,因为已经被命名 为root。Level属性的值大小写无关,其值为下面其中一个字符串:TRACE、DEBUG、INFO、 WARN、ERROR、ALL 和 OFF。如果 root 元素没 有引用任何 appender,就会失去所有 appender。
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
4)filter过滤节点
级别过滤器(LevelFilter)
LevelFilter 根据记录级别对记录事件进行过滤。如果事件的级别等于配置的级别,过滤 器会根据 onMatch 和 onMismatch 属性接受或拒绝事件。下面是个配置文件例子:
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤掉非INFO级别 -->
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
临界值过滤器(ThresholdFilter)
ThresholdFilter过滤掉低于指定临界值的事件。
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!-- 过滤掉TRACE和DEBUG级别的日志 -->
<level>INFO</level>
</filter>
求值过滤器(EvaluatorFilter)
评估是否符合指定的条件
<filter class="ch.qos.logback.classic.filter.EvaluatorFilter">
<evaluator>
<!--过滤掉所有日志中不包含hello字符的日志-->
<expression>
message.contains("hello")
</expression>
<onMatch>NEUTRAL</onMatch>
<onMismatch>DENY</onMismatch>
</evaluator>
</filter>
匹配器(Matchers)
<matcher>
<Name>odd</Name>
<!-- 过滤掉序号为奇数的语句-->
<regex>statement [13579]</regex>
</matcher>
参考配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<!-- 从高到地低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL -->
<!-- 日志输出规则 根据当前ROOT 级别,日志输出时,级别高于root默认的级别时 会输出 -->
<!-- 以下 每个配置的 filter 是过滤掉输出文件里面,会出现高级别文件,依然出现低级别的日志信息,通过filter 过滤只记录本级别的日志-->
<!-- 属性描述 scan:性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 定义日志文件 输入位置 -->
<property name="log_dir" value="${sky.rest.log.dir}" />
<!-- 日志最大的历史 30天 -->
<property name="maxHistory" value="30"/>
<!-- ConsoleAppender 控制台输出日志 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- 对日志进行格式化 -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %X{threadId} %-5level %logger %msg%n</pattern>
</encoder>
</appender>
<!--<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">-->
<!--<!–<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">–>-->
<!--<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.QTracePatternLogbackLayout">-->
<!--<!– 对日志进行格式化 –>-->
<!--<pattern>%d{yyyy-MM-dd HH:mm:ss} - %app %tid %host %X{type} [%thread] %-5level %logger %msg%n</pattern>-->
<!--</layout>-->
<!--</appender>-->
<!-- ERROR级别日志 -->
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 RollingFileAppender-->
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤器,只记录WARN级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 最常用的滚动策略,它根据时间来制定滚动策略.既负责滚动也负责出发滚动 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志输出位置 可相对、和绝对路径 -->
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/error.log</fileNamePattern>
<!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件假设设置每个月滚动,且<maxHistory>是6,
则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除-->
<maxHistory>${maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{threadId} %-5level %logger %msg%n</pattern>
</encoder>
</appender>
<!-- WARN级别日志 appender -->
<appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤器,只记录WARN级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/warn.log
</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>${maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{threadId} %-5level %logger %msg%n</pattern>
</encoder>
</appender>
<!-- INFO级别日志 appender -->
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤器,只记录INFO级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/info.log
</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>${maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{threadId} %-5level %logger %msg%n</pattern>
</encoder>
</appender>
<!-- DEBUG级别日志 appender -->
<appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤器,只记录DEBUG级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/debug.log
</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>${maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{threadId} %-5level %logger %msg%n</pattern>
</encoder>
</appender>
<!-- TRACE级别日志 appender -->
<!--<appender name="TRACE" class="ch.qos.logback.core.rolling.RollingFileAppender"> -->
<!--<!– 过滤器,只记录ERROR级别的日志 –> -->
<!--<filter class="ch.qos.logback.classic.filter.LevelFilter"> -->
<!--<level>TRACE</level> -->
<!--<onMatch>ACCEPT</onMatch> -->
<!--<onMismatch>DENY</onMismatch> -->
<!--</filter> -->
<!--<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> -->
<!--<!– 按天回滚 daily –> -->
<!--<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/trace-log.log -->
<!--</fileNamePattern> -->
<!--<!– 日志最大的历史 60天 –> -->
<!--<maxHistory>${maxHistory}</maxHistory> -->
<!--</rollingPolicy> -->
<!--<encoder> -->
<!--<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{threadId} %-5level %logger %msg%n</pattern> -->
<!--</encoder> -->
<!--</appender> -->
<!-- DEBUG级别日志 appender -->
<appender name="ROOT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/root.log
</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>${maxHistory}</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{threadId} %-5level %logger %msg%n</pattern>
</encoder>
</appender>
<logger name="java.sql.PreparedStatement" level="DEBUG" />
<logger name="java.sql.Connection" level="DEBUG" />
<logger name="java.sql.Statement" level="DEBUG" />
<logger name="com.ibatis" level="DEBUG" />
<logger name="com.ibatis.common.jdbc.SimpleDataSource" level="DEBUG" />
<logger name="com.ibatis.common.jdbc.ScriptRunner" level="DEBUG"/>
<logger name="com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate" level="DEBUG" />
<logger name="com.qihoo" level="DEBUG" additivity="true">
<appender-ref ref="INFO" />
</logger>
<!-- root级别 DEBUG -->
<root level="INFO">
<!-- 控制台输出 -->
<appender-ref ref="STDOUT" />
<appender-ref ref="ROOT" />
<!-- 文件输出 -->
<appender-ref ref="DEBUG" />
<appender-ref ref="WARN" />
<appender-ref ref="ERROR" />
</root>
</configuration>
3.代码中怎样书写log语句
(1)logger对象的获取方式
- 使用继承抽象类的方式,获取logger对象
父类代码
public class AbstractLogger {
//只有自己和子类才能获取logger对象,getclass(),是调用本类的getclass()方法.
protected Logger logger = LoggerFactory.getLogger(getClass());
}
子类代码
public class Slf4jJDKLog extends AbstractLogger{
//这个logger对象就是继承自抽象类中的属性logger
@Test
public void testLogger(){
logger.info("你好中国");
}
}
- 使用@slf4j注解的方式自动生成logger对象
@Slf4j //这是生成logger对象
@Service
public class CompanyService {
@Autowired
private CompanyDao companyDao;
@Autowired
private IdWorker idWorker;
/**
* 保存企业
* 1.配置idwork到工程
* 2.在service中注入idwork
* 3.通过idwork生成id
* 4.保存企业
*/
public void add(Company company) {
//使用logger对象
log.info("这是slf4j生成的logger对象");
//基本属性的设置
String id = idWorker.nextId()+"";
company.setId(id);
//默认的状态
company.setAuditState("0");//0:未审核,1:已审核
company.setState(1); //0.未激活,1:已激活
companyDao.save(company);
}
}
(2)项目中使用log日志的注意事项
1、tomcat配置access日志,记录每次http请求相关信息
2、dubbo配置access日志,记录dubbo consumer和provider的每次请求调用和处理
3、在系统里调用外部的地方,记录请求的接口、参数、返回结果、花费时间、遇到的异常等
4、在系统里出现异常的地方,记录异常的堆栈,如果可以,尽量通过异常的日志能还原当时的情景,比如当时受影响的是哪个用户、传入的变量是什么、处理哪些核心数据引发的异常等等
5、有些代码在编写时就知道很难被执行到或者不希望被执行到、以及一些基本不会走到的else块,这些地方需要记录下核心日志
6、禁止使用 System.out 或 System.error
7、禁止使用 Apache Commons Logging , Java Util
Logging,推荐使用slf4j,推荐使用Logback,代码中使用Slf4j记录日志8、业务日志使用独立的日志配置,不能采用className。举个例子,我之前在qunar做项目时,涉及打通许多第三方管理系统,和每一个第三方的交互的日志都会单独存储到一个日志文件中
9、INFO及以上的系统日志统一输出到Console。在生产环境中如果将系统log切分成多个文件, 是非常不利于问题排查的.
不仅要同时追找多个文件, 还要匹配问题发生的时间点. 最要命的是, 有些异常是被print到 console里的。10、日志配置Rolling,可以根据实际情况配置按天、按小时进行轮转,生成新的日志文件
11、记录程序状态需要包含发生时间(一般框架里就会带上)、大数据量更新的进度、关键变量及正在做哪些重要事情。
12、不同的日志文件尽量使用同样的格式输出(logback配置同样的pattern), 这样方便脚本通用及第三方解析
四.项目中出现问题怎么使用log定义错误
一般我们的项目都是发布在linux系统的服务器上,一般log文件夹在项目的目录下,我们直接进入项目目录下就行.
步骤:
1.进入log文件目录下
[root@XX db]# cd log/
2.vim 进入文件
[root@XX db]# vim 20200419.app.txt
3.命令模式下
/全局跟踪号
根据全局跟踪号,查找我们想要查找的日志文件,根据日志查看我们的请求报文,相应报文等.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
/201213123
或者
:全局跟踪号?
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
:201213123?