👨🎓作者:Java学术趴 🏦仓库:Github、Gitee ✏️博客:CSDN、掘金、InfoQ、云+社区 💌公众号:Java学术趴 🚫特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系小编授权。 🙏版权声明:文章里的部分文字或者图片来自于互联网以及百度百科,如有侵权请尽快联系小编。微信搜索公众号Java学术趴联系小编。
☠️每日毒鸡汤:生活是世上最罕见的事情,大多数人只是存在,仅此而已。
👋大家好!我是你们的老朋友Java学术趴,今天继续给大家分享小趴Java日志框架。对于一个应用程序来说日志记录是必不可少的一部分。线上问题追踪,基于日志的业务逻辑统计分析等都离不日志。java领域存在多种日志框架,目前常用的日志框架包括Log4j 1,Log4j 2,Commons Logging,Slf4j,Logback,Jul。
以下是SLF4J门面技术。
这个是SLF4J门面技术这个深绿色的代表网络适配器。
// 使用反射机制的这个类只要是该项目的 src下的类都可以。
Logger logger = LogManager.getLogger(入门案例.class);
为什么要使用日志门面技术:
Logger logger = Logger.getLogger("com.yunbocheng.JUL.JULTest.test01");
logger.severe("severe信息");
logger.warning("warining信息");
logger.info("info信息");
logger.config("config信息");
logger.fine("fine信息");
logger.finer("finer信息");
logger.finest("finest信息");
此时打印的结果是 :只有info级别以及比info级别高的日志信息
总结 :
以上所有的配置相关的操作,都是一Java硬编码的形式进行的。
我们可以使用更加清晰,更加专业的一种做法,就是配置文件。
如果我们没有自己添加这个配置文件,则会使用系统默认的配置文件。
这个配置文件:
java.home --> 找到jre文件夹 --> lib --> logging.properties
配置文件中的#代表的注释,可以删除掉。
# 以下是配置文件的代码信息
# 这行代码代表让这个日志执行指定的配置信息
# 这个trace代表的是输出级别,这个console是我们自定义的一个名称(见名思意)appenderName
# 这个可以设置打印到多个地方,中间用逗号隔开。
# 这个Logger是继承的根节点rootLogger。
log4j.rootLogger = trace,console
#配置appender输出方法
log4j.appender.console = org.apache.log4j.ConsoleAppender
#配置输出信息的格式
log4j.appender.console.layout = org.apache.log4j.SimpleLayout
// 以下是输出日志信息的代码
public void test01(){
Logger logger = Logger.getLogger(配置文件.class);
// 打印输出信息
logger.fatal("fatal信息");
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
#这行代码的代表打印到控制台
log4j.rootLogger = trace,console
#配置appender输出位置
log4j.appender.console = org.apache.log4j.ConsoleAppender
#配置输出信息的格式
log4j.appender.console.layout = org.apache.log4j.PatternLayout
#这行是设置自定义的日志信息打印格式
log4j.appender.console.layout.conversionPattern = [%p]%r %c%t%d{yyyy-mm-dd HH:mm:ss:SSS}
#这行代码的代表打印到控制台
log4j.rootLogger = trace,file
#配置appender输出位置
log4j.appender.file = org.apache.log4j.ConsoleAppender
#配置输出信息的格式
log4j.appender.console.file = org.apache.log4j.PatternLayout
#这行是设置自定义的日志信息打印格式
log4j.appender.file.layout.conversionPattern = [%p]%r %c%t%d{yyyy-mm-dd HH:mm:ss:SSS} %m%n
#第一个file是我们自己命名的appenderName,第二个file是用来指定文件的位置。
log4j.appender.file.file = E://log4j.log
#设置输出日志的编码格式(输出中文的日志信息)
log4j.appender.file.encoding = UTF-8
# 需要将以上输出到控制台和文件的代码都要写上
# 最主要的是修改打印到的位置代码,这是代表可以在 appenderName 为这个两个的地方输出
# 这个 file,console是我们自定义的名称,(见名思意原则就是代表控制台和文件)
log4j.rootLogger = trace,file,console
log4j.rootLogger = trace,rollingFile,console
# RollingFileAppender的配置,我们可以针对实际含义起名
log4j.appender.rollingFile = org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.conversionPattern = [%p]%r %c%t%d{yyyy-mm-dd HH:mm:ss:SSS} %m%n
log4j.appender.rollingFile.file = E://log4j.log
log4j.appender.rollingFile.encoding = UTF-8
# 指定日志文件的大小
log4j.appender.rollingFiler.maxFileSize = 1MB
# 指定日志文件的数量
log4j.appender.rollingFiler.maxBackupIndex = 5
# DailyRollingFileAppender的配置,我们可以针对实际含义起名
log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFile.layout.conversionPattern = [%p]%r %c%t%d{yyyy-mm-dd HH:mm:ss:SSS} %m%n
log4j.appender.dailyRollingFile.file = E://log4j.log
log4j.appender.dailyRollingFile.encoding = UTF-8
#不要忘记前边的那个点
appender.dailyRollingFile.datePattern = '.'yyyy-MM-dd HH:mm:ss
# 持久化日志信息 将日志信息存储到数据库
log4j.appender.logDB = org.apache.log4j.jdbc.JDBCAppender
log4j.appender.logDB.layout = org.apache.log4j.PatternLayout
log4j.appender.logDB.Driver = com.mysql.jdbc.Driver
log4j.appender.logDB.URL = jdbc:mysql://localhost/log
log4j.appender.logDB.User = root
log4j.appender.logDB.Password = 567cybtfboys
# 此时要像数据库中插入数据,使用insert语句
log4j.appender.logDB.Sql = INSERT INTO tab_log(id,name,create,level,category,fileName,message) values('project_log','%d{yyyy-MM-dd HH:mm:ss}','%p','%c','%F','%m')
Logger logger = Logger.getLogger(日志持久化_将数据存储到数据库.class);
// 输出日志信息
logger.fatal("fatal信息");
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
# 使用最高父类rootLogger配置logger,这个时候继承的是父logger(根节点)
log4j.rootLogger = trace,console
# 配置自定义logger,此时使用的是自定义的父logger (自定义)
log4j.logger.com.yunbocheng = info,file
从输出的位置来看,控制台输出了信息,日志文件也会输出信息。所以可以得出结论,如果根节点的logger和自定义父logger配置的输出位置是不同的则取二者的并集,也就是配置的位置都会进行日志的输出。
如果二者配置的日志级别不同,主要以按照我们自定义的父logger的级别输出为主。
<!--SLF4J核心配置-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.29</version>
</dependency>
<!--导入log4j适配器依赖-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<!--导入log4j依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
# 使用根节点的logger对象
log4j.rootLogger = trace,console
# 将日志信息输出到控制台
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.conversionPattern = [%p]%r %c%t%d{yyyy-mm-dd HH:mm:ss:SSS} %m%n
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// 注意此时使用的是 slf4j 中的类和接口来创建的logger对象,而不是log4j
Logger logger = LoggerFactory.getLogger(SLF4J集成log4j的方法.class);
// 打印输出信息
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
<!--SLF4J核心配置-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.29</version>
</dependency>
<!--JUL适配器依赖-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.25</version>
</dependency>
<!--因为JUL是JDK内置的,所以不需要额外导入JUL实现的依赖-->
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// 此时的这个logger对象也是通过 slf4j 获取的,完全不需要JUL参与
Logger logger = LoggerFactory.getLogger(SLF4J集成log4j的方法.class);
// 打印输出信息
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
<!--删除log4j依赖,以为此时需要使用log4j进行重写-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--SLF4J核心配置-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.29</version>
</dependency>
<!--logback日志框架-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
<!--slf4j提供的log4j桥接器-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.junit.Test;
//注意:此时使用了 log4j包下的类和方法实现了 logback日志格式的输出。
// 此时需要使用 slf4j提供的log4j-over-slf4j桥接器
Logger logger = LogManager.getLogger(日志重构.class);
logger.info("info信息");
注意:
Logback提供了3种配置文件
如果都不存在则采用默认的配置。
注意:-10代表给案例设置10个字符,左对齐。+10代表给案例设置10个字符,右对其。
<!--导入slf4j核心依赖-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.29</version>
</dependency>
<!--导入logback框架依赖-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<scope>test</scope>
</dependency>
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// 注意:以上都是使用的 slf4j 门面技术来实现的这个logback的日志框架。完全不需要使用logback包中的类和方法。使用的是 slf4j 的门面技术。
// 这里我们还是使用slf4j的门面技术来实现logback
Logger logger = LoggerFactory.getLogger(入门案列.class);
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
<!--这是第一种打印到多个位置的配置(此时打印到控制台和文件的日志级别都是info级别)-->
<root level="info">
<!--引入appender,日志记录器,使用name属性来获取指定的appender对象-->
<appender-ref ref="consoleAppender"/>
<appender-ref ref="fileAppender"/>
</root>
<!--这是第二种打印到多个位置的配置(此时虽然代码中写的控制台和文件的打印级别不同,但是此时打印出来的都是info级别,以最后一个级别为准)-->
<root level="ALL">
<!--引入appender,日志记录器,使用name属性来获取指定的appender对象-->
<appender-ref ref="fileAppender"/>
</root>
<root level="info">
<!--引入appender,日志记录器,使用name属性来获取指定的appender对象-->
<appender-ref ref="consoleAppender"/>
</root>
<property name = "pattern" value="[%-5level] %d{yyyy-MM-dd HH:mm:ss} %c %M %L %thread %m%n"></property>
<property name="logDir" value="E://test"></property>
<!-- 配置输出html文件 -->
<!-- 这个也属于输出到指定文件,还是使用FileAppender类 -->
<appender name="htmlFileAppender" class="ch.qos.logback.core.FileAppender">
<file>${logDir}/logback.html</file>
<!--设置输出格式-->
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<prttern>${pattern}</prttern>
</layout>
</encoder>
</appender>
<root level="ALL">
<!--引入appender,日志记录器,使用name属性来获取指定的appender对象-->
<appender-ref ref="htmlFileAppender"/>
</root>
<!-- 将文件拆分和归档主要使用以下的配置信息 -->
<!-- 配置文件的appender 可拆分归档的文件-->
<appender name="roll" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 输出格式(自定义格式)使用EL表达式 -->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
<!-- 设置文件位置(使用EL表达式) -->
<file>${logDir}/roll_logback.log</file>
<!-- 指定拆分规则(这里使用文件大小和时间来拆分日志信息) -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 按照时间和压缩格式来声明文件名-->
<!-- 下边的这一长串都是文件名 实际开发中,将日志设置到日即可 i是一个序号:用于区别日志文件-->
<!-- gz是一种压缩包格式,这个格式常在linux中使用 我们这里获取到的也是一个压缩文件-->
<fileNamePattern>${logDir}/roll.%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
<!-- 按照文件大小进行拆分 -->
<maxFileSize>1KB</maxFileSize>
<!-- 注意:这里只有这个文件的大小超过1KB的时候才会拆分日志文件,这个时候拆分出来的就是一个以上格式的压缩包-->
</rollingPolicy>
</appender>
<!-- 配置控制台appender 使用过滤器-->
<appender name="consoleFilterAppender" class="ch.qos.logback.core.ConsoleAppender">
<!-- 设置控制台输出信息形式 -->
<target>
System.err
</target>
<!-- 设置打印日志信息格式(自定义)-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>${pattern}</pattern>
</encoder>
<!-- 配置过滤器 -->
<fileter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 设置日志的输出级别 -->
<level>WARN</level>
<!-- 高于level中设置的级别,则打印日志 -->
<onMatch>ACCEPT</onMatch>
<!-- 低于level中设置的级别,则屏蔽日志 -->
<onMisMatch>DENY</onMisMatch>
</fileter>
为什么要使用异步日志
异步日志的作用
配置异步日志的方式
<!-- 配置异步日志 -->
<appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="consoleAppender"/>
</appender>
<!--
日志记录器
配置root logger
level:配置日志级别
-->
<root level="ALL">
<appender-ref ref="asyncAppender"/>
</root>
异步日志的原理:
在实际的我们使用异步日志的方式来实现业务。
<!-- 以上使用的是root(根节点的logger),我们此时使用自定义的logger-->
<!-- 这里需要使用name属性来指定父类路径(这个路径必须是输出日志文件文件的父路径)-->
<logger name="com.yunbocheng" level="info" additivity="false">
<!-- 在自定义logger中配置appender -->
<appender-ref ref="consoleAppender"/>
</logger>
<!-- 添加log4j2核心 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
<!-- 添加log4j2核心 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.12.1</version>
</dependency>
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;
// 以上没有使用 SLF4J门面技术,完全使用的log4j中的类和方法进行实习的。
public class 入门案例 {
@Test
public void test01(){
/*
* 这个案例中我们单纯的使用Log4j2 门面+实现
* 不使用 SLF4J 实现。
* Log4j2和log4j提供了相同的输出级别。
* Log4j2的默认输出级别是 error
* */
Logger logger = LogManager.getLogger(入门案例.class);
logger.fatal("fatal信息");
logger.error("error信息");
logger.warn("warn信心");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
}
<!-- log4j2日志实现-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.12.1</version>
</dependency>
<!-- log4j2日志门面 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.12.1</version>
</dependency>
<!-- 导入slf4j日志门面依赖 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- log4j适配器 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.12.1</version>
</dependency>
<!--注意:xml配置文件中的标签是固定的,不可以随意修改,否则会报错-->
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration>
<Appenders>
<!-- 控制台输出 这个name属性不可以改变 -->
<Console name="consoleAppender" target="SYSTEM_ERR">
</Console>
</Appenders>
<!-- 配置logger -->
<Loggers>
<!-- 配置rootlogger -->
<Root level="trace">
<!-- 引用Appender -->
<AppenderRef ref="consoleAppender"/>
</Root>
</Loggers>
</Configuration>
// 注意:我们使用的都是 slf4j门面技术中的类与方法实现的日志信息的打印,没有适应到log4j2.
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// 注意:此时使用的 slf4j 的门面技术,输出的是 slfj的日志级别(有5个级别)
Logger logger = LoggerFactory.getLogger(Log4j2与SLF4J联合使用.class);
// slf4j 中存在5种日志输出级别,此时使用是slf4j的记录器,而不是log4j2的,所以只能输出slf4j中的五种级别。
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
<!--
配置全局通用属性
注意:在log4j2中的标签都是首字母大写,只有全局通用属性的标签首字母不大写。
-->
<properties>
<property name="logDir">D://test</property>
</properties>
<!-- 配置appender -->
<Appenders>
<!-- 配置文件输出 -->
<File name="fileAppender" fileName="${logDir}/log4j2.log">
<!-- 配置文件输出格式(这里设置输出格式的语法和logback中是一样的。) -->
<!-- -5表示左对齐,+5表示右对齐 -->
<PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss:SSS} %m%n"/>
</File>
</Appenders>
<!--修改logger配置信息-->
<!-- 配置logger -->
<Loggers>
<!-- 配置rootlogger -->
<Root level="trace">
<!-- 引用Appender -->
<AppenderRef ref="fileAppender"/>
</Root>
</Loggers>
<!-- 异步日志依赖 -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.7</version>
</dependency>
<!-- 配置异步日志(AsyncAppender)-->
<Async name="myAsync">
<!-- 将控制台输出做异步的操作 -->
<AppenderRef ref="consoleAppender"/>
</Async>
<!-- 配置logger -->
<Loggers>
<!-- 配置rootlogger -->
<Root level="trace">
<!-- 引用Appender -->
<AppenderRef ref="myAsync"/>
</Root>
</Loggers>
//日志的记录
Logger logger = LoggerFactory.getLogger(Log4j2与SLF4J联合使用.class);
// slf4j 中存在5种日志输出级别,此时使用是slf4j的记录器,而不是log4j2的,所以只能输出slf4j中的五种级别。
for (int i = 0; i < 100; i++) {
logger.error("error信息");
logger.warn("warn信息");
logger.info("info信息");
logger.debug("debug信息");
logger.trace("trace信息");
}
// 系统业务逻辑
for (int i = 0; i < 100; i++) {
System.out.println("-----------");
}
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
<Loggers>
<!-- 配置rootlogger -->
<Root level="trace">
<!-- 引用Appender -->
<AppenderRef ref="consoleAppender"/>
</Root>
</Loggers>
注意:在做测试前,一定要将全局的异步配置注释掉(resources下的properties),使用 # 注释。
#Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
<!-- 配置logger -->
<Loggers>
<!--
测试(混合异步) 自定义logger,让自定义的logger为异步logger
includeLocation="false"
表示去除日志记录中的行号信息,这个行号信息非常的影响日志记录的效率(生产中都不加这个行号)
严重的时候可能记录的比同步的日志效率还要低。
additivity="false"
表示不继承 rootlogger
-->
<AsyncLogger name="com.yunbocheng" level="trace"
includeLocation="false" additivity="false">
<!-- 将控制台的输出,设置为异步打印 -->
<AppenderRef ref="consoleAppender"/>
</AsyncLogger>
<!-- 配置rootlogger -->
<Root level="trace">
<!-- 引用Appender -->
<AppenderRef ref="consoleAppender"/>
</Root>
</Loggers>
注意:
各种语言的异步日志强度比较
以上项目的源代码,点击星球🌍进行免费获取 星球(Github地址)如果没有Github的小伙伴儿。可以搜索🔍微信公众号:Java学术趴,📭发送Java日志,免费给发给大家项目源码,代码是经过小编亲自测试🔧的,绝对可靠,免费拿去使用。
-------💘看完的大佬们可以关注一下小编,会一直更新小技巧,免费分享给大家呦!!!💝-----
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。