Java logging system could be so complicated? - architecture articles

This article is a series, welcome attention

Log in the end is the story behind? Why use a logging framework?

Surely we have had to use System.outto output debugging, developed under a development environment to do so of course, very convenient, but doing so is in trouble online:

  1. The system has been running, the output more and more disk space is gradually filled
  2. Different business want to log output in a different location
  3. For some applications better performance, try to reduce the control output log, the log needs to dynamically adjust the output
  4. Auto output log information, such as: date, thread, method name, etc.

Obviously System.outnot solve our problems, but there will be problems we encountered predecessors encountered, the log is no exception, where there is a large cattle Ceki , almost the entire Java logging system or have been involved in Ceki of Ceki impact depth. Of course, the complexity of Java logging system is also part of the reason is thanks to the great gift of cattle.

Java logging pains and sorrows

  • In early 1996, the European electronics market security team decided to write its own program to track API (Tracing API). Through continuous improvement, the API has finally become a very popular Java logging package that Log4j (created by Ceki).
  • Log4j later became the Apache Foundation project in one, Ceki also joined the Apache organization. Log4j log later became almost a standard Java community. Apache Foundation also said to have suggested to Log4j Sun introduced Java standard library, but Sun refused.
  • In 2002 Java1.4 release, Sun launched its own logging library JUL (Java Util Logging), which mimics the basic Log4j implementation. In JUL out before, Log4j has become a mature technology, making Log4j occupy a certain advantage in the choice.
  • Then, Apache launched the Jakarta Commons Logging, JCL only defines a set of log interfaces (internal Simple Log also provides a simple implementation), to achieve dynamic loading logging component supports run-time, that is to say, in your application code, simply call the Commons Logging interface, the underlying implementation can be Log4j, it can also be a Java Util Logging.
  • Later (in 2006), Ceki suited Apache works, left the Apache. Then have created Slf4j (log facade interface, similar to Commons Logging) and Logback (Slf4j implementation) two projects, and back to Sweden created the QOS company, QOS official website described it this way Logback of: The Generic, Reliable Fast & Flexible Logging Framework (a common, reliable, fast and flexible logging framework).
  • Java logging areas are divided into two camps: Commons Logging camp and Slf4j camp.
  • Apache Commons Logging in the shadow of the trees, there is a large user base. But there is evidence that the form is changing. By the end of 2013 was analyzed on GitHub 30000 projects, the statistics of the most popular 100 Libraries, we can see trends Slf4j better.
  • Apache has been seeing overtake Logback momentum in 2012-07 rewrite Log4j 1.x, set up a new project Log4j 2, Log4j 2 has all the characteristics of Logback.
  • Now logging framework has been developed for: Slf4j as API, and the implementation is divided into logback log4j (Commons Logging API because efficiency and design issues, is now gradually fade out the stage)

Let us bask in the great God, ha ha:

So how elegant and how to use it in the chaotic log Java logging system?

In fact, in Ceki design system, log in as Java JDBC, Servelt and so as to achieve a good standard definitions can be switched with each other, the problem is that the standard set of people to engage in their own way out of a lot of standards, JCL, SLF4j etc., official (Sun Company) and not to force late, has finally been developed to SLF4j in a clever way (bridge, bindings, see below) unified, standard use in the following figure:

The view taken from slf4j Manual , simplifies the excess part, clearly expressed use:

Application references SLF4j-API (SLF4j interface used when encoding org.slf4j.Logger, or not implemented logback the log4j)

  • logbak: slf4j will automatically find logback achieve (logback default implementation slf4j)
  • log4j: use basically the same, but more than an adapter layer, cited slf4j-log4j12.jar, officially known as the binding (concrete-bindings), it is to SLF4j-API bind to log4j final output log

DETAILED following dependent

logback

<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.3</version>
</dependency>

log4j2

<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-slf4j-impl</artifactId>
  <version>2.12.1</version>
</dependency>
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.12.1</version>
</dependency> 

得益于maven的依赖传递机制,我们不需要显示声明依赖SLF4j-API.jar。

可以看到,log4j 多依赖了 log4j-slf4j-impl.jar,其实就是上图所示的适配器层,读者可能好奇,为什么使用 log4j 会有适配器层?其原因在于,slf4j 并不是官方规范,所以没人遵守(也就是自己的日志框架中没有原生实现org.slf4j.Logger接口,如 log4j ),而绑定层( log4j-slf4j-impl.jar)的作用就是通过静态查找的方式将使用log4j作为实现(具体原理请关注后续文章),这样就是实现了不依赖log4j而使用log4j输出日志(面向接口编程的最佳实践,Ceki 大神就是用这套思想将 slf4j 做成了 Java 日志的标准,烂牌翻盘的典范)。

上面这一段讲解了绑定(concrete-bindings)思想,是本文的精髓,读者一定要理解这里,后面还有桥接思想与之类似,请继续阅读。

小结

至此我们已经完成了日志的整合,但是事情真的这么简单吗?

先梳理一下,如此混乱的日志体系下(slf4j,jul,jcl,logback,log4j)会不会会产生什么问题?答案是一定的,各种第三方库使用了不同的日志框架,如果我们依赖 Spring ,Spring(非boot)的默认日志实现是JCL、又或者我们已有项目已经使用了Log4j,想使用logback的话,难道要逐个类改代码吗(官方有迁移工具)?我们能不能只用一种框架来处理JUL(java.util.logging)、JCL(Jakarta Commons Logging)、Log4j1、Log4j2 呢?

答案是肯定的,Ceki 的 Slf4j 给出了解决方案,就是上文所说的桥接( Bridging legacy),简单来说就是劫持以上所以第三方日志输出并重定向至 SLF4j,最终实现统一日志上层 API(编码) 与下层实现(输出日志位置、格式统一)。我们来看一下图示

上图左侧就是前一张图的 logback 日志实现,为了兼容其他日志,我们需要引用右侧的桥接包:xxx-over/to-slf4j.jar ,xxx对应日志框架,使用 logback 的情况下,除了上文的 logback 依赖,还需要引入以下依赖才能保证所有日志都被桥接至slf4j。

如何桥接?

logback 如下

<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.2.3</version>
</dependency>
<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>jul-to-slf4j</artifactId>
</dependency>
<!-- log4j 桥接包,slf4j官方实现,另有log4j官方实现,二选一即可 log4j-to-slf4j-->
<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>log4j-over-slf4j</artifactId>
</dependency>

 

log4j2 如下

<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-slf4j-impl</artifactId>
  <version>2.12.1</version>
</dependency>
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.12.1</version>
</dependency>
<!-- 以下是桥接包,使用了log4j作为底层实现,
     不能再桥接log4j,否则会出现无限递归的情况(具体原因请关注后续文章) -->
<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>jcl-over-slf4j</artifactId>
</dependency><dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>jul-to-slf4j</artifactId>
</dependency>

SpringBoot 项目引用了一部分依赖,所以使用起来略微有些不同:

logback 如下

<!-- logback作为内置实现,使用相对简单 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency><!-- 引入缺少的桥接包 --><dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
</dependency>

log4j2 如下

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter</artifactId>
   <!-- 使用log4j2要排除logback依赖 -->
   <exclusions>
      <exclusion>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-logging</artifactId>
      </exclusion>
   </exclusions>
</dependency>
<!-- Spring已经写好了一个log4j2-starter但缺少桥接包 -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- 引入缺少的桥接包 -->
<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>jcl-over-slf4j</artifactId>
</dependency>

结束语

以上两种才是项目中的最佳使用方式,其他笔者不推荐使用。

最后来看一下 slf4j 如何使用:

 

static final org.slf4j.Logger logger = 
                    LoggerFactory.getLogger(TestLog.class);
logger.trace("A TRACE Message");
logger.debug("A DEBUG Message");
logger.info("An INFO Message");
logger.warn("A WARN Message");
logger.error("An ERROR Message");

这样使用我们就可以随意切换日志实现而无需改动代码,操作起来也简单,只需要按照上文切换依赖即可。至于其他使用细节本文不在赘述,关注后续文章(最佳实践、配置文件、原理、扩展等)。

 

如果觉得写的不错,求关注、求点赞、求转发,如果有问题或者文中有错误,欢迎留言讨论。

扫描关注公众号,第一时间获得更新

参考:

Java-日志的江湖
http://www.slf4j.org/manual.html
http://www.slf4j.org/legacy.html
www.baeldung.com/spring-boot…

 转载请注明出处。

Guess you like

Origin www.cnblogs.com/xuningfans/p/12151851.html