Java日志体系一本通

主要内容

1·学习java日志体系及日志工具的演进

2·了解日志采集、处理、分析等各阶段的常用中间件

3·学会搭建完整的elk日志平台

4·学习日志打点,切面,日志文件等输出手段

5·项目实战,完成一访问日志链路的跟踪

1、Java日志体系

1.1 体系概述

file

1.1.1 日志接口

  • JCL:Apache基金会所属的项目,是一套Java日志接口,之前叫Jakarta Commons Logging,后更名为Commons Logging,简称JCL
  • SLF4J:Simple Logging Facade for Java,缩写Slf4j,是一套简易Java日志门面,只提供相关接口,和其他日志工具之间需要桥接

1.1.2 日志实现

  • JUL:JDK中的日志工具,也称为jdklog、jdk-logging,自Java1.4以来sun的官方提供。
  • Log4j:隶属于Apache基金会的一套日志框架,现已不再维护
  • Log4j2:Log4j的升级版本,与Log4j变化很大,不兼容
  • Logback:一个具体的日志实现框架,和Slf4j是同一个作者,性能很好

1.2 发展历程

1.2.1 上古时代

  在JDK 1.3及以前,Java打日志依赖System.out.println(), System.err.println()或者e.printStackTrace(),Debug日志被写到STDOUT流,错误日志被写到STDERR流。这样打日志有一个非常大的缺陷,非常机械,无法定制,且日志粒度不够细分。

代码:

System.out.println("123");
System.err.println("456");

1.2.2 开创先驱

  于是,Ceki Gülcü 于2001年发布了Log4j,并将其捐献给了Apache软件基金会,成为Apache 基金会的顶级项目。后来衍生支持C, C++, C#, Perl, Python, Ruby等语言。   Log4j 在设计上非常优秀,它定义的Logger、Appender、Level等概念对后续的 Java Log 框架有深远的影响,如今的很多日志框架基本沿用了这种思想。Log4j 的性能是个问题,在Logback 和 Log4j2 出来之后,2015年9月,Apache软件基金会宣布,Log4j不再维护,建议所有相关项目升级到Log4j2

pom:

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>apache-log4j-extras</artifactId>
    <version>1.2.17</version>
</dependency>

配置:

log4j.rootLogger=debug

#console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender 
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 
log4j.appender.stdout.layout.ConversionPattern= log4j:[%d{yyyy-MM-dd HH:mm:ss a}]:%p %l%m%n

#dailyfile
log4j.appender.dailyfile=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.dailyfile.DatePattern='_'yyyy-MM-dd'.log'
log4j.appender.dailyfile.File=./log4j.log
log4j.appender.dailyfile.Append=true
log4j.appender.dailyfile.Threshold=INFO
log4j.appender.dailyfile.layout=org.apache.log4j.PatternLayout 
log4j.appender.dailyfile.layout.ConversionPattern=log4j:[%d{yyyy-MM-dd HH:mm:ss a}] [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n

代码:

import org.apache.log4j.Logger;

Logger logger = Logger.getLogger(Demo.class);
logger.info("xxxx");

1.2.3 搞事情的JUL

  sun公司对于log4j的出现内心隐隐表示嫉妒。于是在jdk1.4版本后,开始搞事情,增加了一个包为java.util.logging,简称为JUL,用以对抗log4j ,但是却给开发造成了麻烦。相互引用的项目之间可能使用了不同的日志框架,经常将代码搞得一片混乱。

代码:

import java.util.logging.Logger;

Logger loggger = Logger.getLogger(Demo.class.getName()); 
logger.finest("xxxx");

配置路径:

$JAVA_HOME/jre/lib/logging.properties

  JUL功能远不如log4j完善,自带的Handlers有限,性能和可用性上也一般,JUL在Java1.5以后才有所提升。

1.2.4 JCL应运而生

  从上面可以看出,JUL的api与log4j是完全不同的(参数只接受string)。由于日志系统互相没有关联,彼此没有约定,不同人的代码使用不同日志,替换和统一也就变成了比较棘手的一件事。假如你的应用使用log4j,然后项目引用了一个其他团队的库,他们使用了JUL,你的应用就得使用两个日志系统了,然后其他团队又使用了simplelog……这个时候如果要调整日志的输出级别,用于跟踪某个信息,简直就是一场灾难。

  那这个状况该如何解决呢?答案就是进行抽象,抽象出一个接口层,对每个日志实现都适配或者转接,这样这些提供给别人的库都直接使用抽象层即可 ,以后调用的时候,就调用这些接口。(面向接口思想)

  于是,JCL(Jakarta Commons Logging)应运而生,也就是commons-logging-xx.jar组件。JCL 只提供 log 接口,具体的实现则在运行时动态寻找。这样一来组件开发者只需要针对 JCL 接口开发,而调用组件的应用程序则可以在运行时搭配自己喜好的日志实践工具。

  那接口下真实的日志是谁呢?参考下图:

file   JCL会在ClassLoader中进行查找,如果能找到Log4j 则默认使用log4j 实现,如果没有则使用JUL(jdk自带的) 实现,再没有则使用JCL内部提供的SimpleLog 实现。(代码验证)

pom:

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>

代码:

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

Log log =LogFactory.getLog(Demo.class);
log.info('xxx');

  JCL缺点也很明显,一是效率较低,二是容易引发混乱,三是JCL的机制有很大的可能会引发内存泄露。

  同时,JCL的书写存在一个不太优雅的地方,典型的场景如下:

  假如要输出一条debug日志,而一般情况下,生产环境 log 级别都会设到 info 或者以上,那这条 log 是不会被输出的。于是,在代码里就出现了

logger.debug("this is a debug info , message :" + msg);

  这个有什么问题呢?虽然生产不会打出日志,但是这其中都会做一个字符串连接操作,然后生成一个新的字符串。如果这条语句在循环或者被调用很多次的函数中,就会多做很多无用的字符串连接,影响性能。

  所以,JCL推荐的写法如下:

if (logger.isDebugEnabled()) {
    logger.debug("this is a debug info , message :" + msg);
}

  虽然解决了问题,但是这个代码实在看上去不怎么舒服...

1.2.5 再起波澜

  于是,针对以上情况,log4j的作者再次出手,他觉得JCL不好用,自己又写了一个新的接口api,就是slf4j,并且为了追求更极致的性能,新增了一套日志的实现,就是logback,一时间烽烟又起……

坐标:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.30</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>1.2.3</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

配置:

<?xml version="1.0" encoding="UTF-8"?>

<configuration scan="true" scanPeriod="60 seconds" debug="false">

    <property name="logPattern" value="logback:[ %-5level] [%date{yyyy-MM-dd HH:mm:ss.SSS}] %logger{96} [%line] [%thread]- %msg%n"></property>

    <!-- 控制台的标准输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <charset>UTF-8</charset>
            <pattern>${logPattern}</pattern>
        </encoder>
    </appender>

    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
    </root>

</configuration>

  logback-core 提供基础抽象,logback-classic 提供日志实现,并且直接就是基于Slf4j API。所以slf4j配合logback来完成日志时,不需要像其他的日志框架一样提供适配器。

  slf4j本身并没有实际的日志输出能力,它底层还是需要去调用具体的日志框架API,也就是它需要跟具体的日志框架结合使用。由于具体日志框架比较多,而且互相也大都不兼容,日志门面接口要想实现与任意日志框架结合就需要额外对应的桥接器。

file

  有了新的slf4j后,上面的字符串拼接问题,被以下代码所取代,而logback也提供了更高级的特性,如异步 logger,Filter等。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

final Logger logger = LoggerFactory.getLogger(Demo.class);

logger.debug("this is a debug info , message : {}", msg);

  

  事情结束了吗?没有,log4j的粉丝们并不开心,于是下一代诞生了……

1.2.6 再度青春

  前面提到,log4j由apache宣布,2015年后,不再维护。推荐大家升级到log4j2,虽然log4j2沿袭了l

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值