This is the second day of my participation in Gwen Challenge

Java Logging System

Log history

Log frameworks in Java are different and varied. Log facade and log implementation are used to implement logging. Log facade defines a set of standard API specifications, similar to interfaces. The logging implementation is the implementation of the interface specification.

Logging facade The logging implementation
Slf4j Log4j

Logback

Log4j2

JUL(Java Util logging)
JCL(Jakarta Commons Logging)

Search the Java log history from the web

  • In early 1996, the European Security Electronic Markets Project team decided to write its own Tracing API. The API has evolved into a very popular Java logging package, Log4j (created by Ceki). Log4j became part of the Apache Foundation project, and Ceki joined the Apache organization. Log4j has since become a near-standard for logging in the Java community. The Apache Foundation is also said to have suggested that Sun introduce Log4j into the Java standard library, but Sun declined.

  • In 2002, With the release of Java1.4, Sun introduced its own Logging library, JUL (Java Util Logging), which basically mimics Log4j’s implementation. Log4j was already a mature technology before JUL came out, giving it an advantage in terms of choice. Later, Apache introduced Jakarta Commons Logging. JCL simply defines a Logging interface (and a simple implementation of SimpleLog inside) that supports dynamically loading Logging components at runtime. That is, in your application code, Simply invoke the Commons Logging interface, and the underlying implementation can be either Log4j or Java Util Logging.



  • Later (in 2006), Ceki was not comfortable with the way Apache worked and left the company. Then I created Slf4j (a Logging portal interface similar to Commons Logging) and Logback (an implementation of Slf4j), and went back to Sweden to create QOS company Logback. The QOS website describes Logback as follows: The Generic, Reliable Fast&Flexible Logging Framework is a Generic, Reliable, fast and flexible Logging Framework.

The JCL logging facade has been phased out due to API design and performance issues, leaving Slfj4 alone. Although Slfj4 defines the log facade, the other log implementations do not use the Slfj4 API interface except Logbak, which is implemented by the Slfj4 author. This leaves Slf4j with two big problems:

  1. For a new project, how to switch between different logging implementations using the Slf4j logging facade?
  2. Before old projects with Log4j, now have the demand, need to change the log unity to Slf4j specification, and logback realize how to do?

Log binding

SPI solves log binding problems

So let’s look at the first question, isn’t that the problem of how to load multiple implementations of an interface, which is simply calledLog bindingIf you look at the Dubbo Public Contract SPI in the previous chapter, you can easily think of ways to load using SPI, and you only need to load one, not multiple. However, some logging implementations are not willing to comply with the Slf4j logging facade, so they have to write an additional third-party package to use the Slf4j binding logging implementation, as detailed below.



As summarized in the figure above, blue represents the JAR packages that the project depends on, and red represents the project source code developed using Slf4j’s log facade interface. If you want to use log4j as a logging implementation, you can use Maven to import the log4j implementation JAR (the right-most JAR), but since log4j does not use the Slf4j logging facade, you need to import an additional JAR package (the middle slf4J-log4j-impl). The content is to use log4j to implement Slf4j logging facade. But how do you get Slf4j to load this implementation? The answer is SPI.

**Logger log = logFactory.getLogger (xxxx.class); ** In slf4J-log4j-impl you can implement log4j’s LogFactory, and then load the implementation through the SPI mechanism in Slf4j. Finally, don’t forget to define the SPI interface configuration file in slf4J-log4j-impl. This enables logging binding and dynamic switching of logging implementations without modifying the source code.

Slf4j addresses log binding issues

Will the Slf4j authors use this approach? Of course not pull. Create a new Maven project and introduce the SLF4J-API package

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>
Copy the code

From the entry method org.slf4j.LoggerFactory#getLogger(java.lang.Class<? > < div style = “box-sizing: border-box;



Yes, you really read that correctly. In this JAR, the place will be red because it can’t find the class, because it does exist. The authors of Slf4j did not use SPI to load the implementation, but instead created a package name, class name, and method nameStaticLoggerBinderLog binding implemented. When Slf4j and slf4J-log4j-impl are introduced together, the App classloader is loaded from slf4J-log4j-implStaticLoggerBinderClass to load the log4j implementation.

Introduce log binding implementation package

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.30</version>
        </dependency>
Copy the code



You can see that there is an implementation with the same package name as the class name, and the logging implementation uses Log4j.

Log bridge

What about an old project that used the Log4J logging implementation without introducing any logging facade, and one day the company’s base platform group required all business parties to use the Slf4j logging facade and logback logging implementation based on performance and log integration issues? The most intuitive idea is to remove the logback dependency, introduce Slf4j and Logback, and replace everything using log4j with Slf4j. This is not a problem, but it is quite a lot of work. So is there an alternative that doesn’t require one change at a time? Yes, create a third party library with the same package name, common class name, and method name for all the log4j code used in the third party library. However, the implementation of the method is done using Slf4j. Then, remove log4J’s dependencies and introduce this third-party library. The Slf4j job also uses this method to bridge logs.

What is log adaptation

Dubbo uses logback to print logs, while log4j is used to implement logging. The two can co-exist, but both log configurations need to be maintained simultaneously. And the use of different logging apis for log printing, very difficult to maintain. So third-party packages are usually written to keep the logging implementation in the third-party library consistent with the logging implementation in the consumer project.

How does Dubbo do log adaptation

If you were developing this as Dubbo, how would you solve the log adaptation problem? For example, if a consumer project uses logback as the logging implementation, how can we ensure that the logging implementation in Dubbo also uses Logback?

The idea is that a third-party library encapsulates its own logging interface, implements it with all of the current popular logging implementations, and then, when it gets the implementation, adds each logging implementation to the list, if thrownClassNotFoundExceptionWhen an implementation can be loaded, it indicates that the user is also using this logging implementation, so as to achieve log adaptation. Finally, the third party library must introduce the dependency of log implementation when carrying out all log implementation, and the scope of this dependency should be set as provider(indicating that only the compilation and integration in Dubbo source code will not be transmitted, that is, the use of this person to introduce Dubbo does not need to introduce the dependency of log implementation).



You can see the Provider-level scope used in the POM.

In dubbo source org.apache.dubbo.com mon. Logger. LoggerFactory class load can be found in the logging implementation.

static {
        // Logging can be specified with the properties parameter
        String logger = System.getProperty("dubbo.application.logger"."");
        switch (logger) {
            case "slf4j":
                setLoggerAdapter(new Slf4jLoggerAdapter());
                break;
            case "jcl":
                setLoggerAdapter(new JclLoggerAdapter());
                break;
            case "log4j":
                setLoggerAdapter(new Log4jLoggerAdapter());
                break;
            case "jdk":
                setLoggerAdapter(new JdkLoggerAdapter());
                break;
            case "log4j2":
                setLoggerAdapter(new Log4j2LoggerAdapter());
                break;
            default:
                // Load all implementations once when not specified
                List<Class<? extends LoggerAdapter>> candidates = Arrays.asList(
                        Log4jLoggerAdapter.class,
                        Slf4jLoggerAdapter.class,
                        Log4j2LoggerAdapter.class,
                        JclLoggerAdapter.class,
                        JdkLoggerAdapter.class
                );
                for (Class<? extends LoggerAdapter> clazz : candidates) {
                    try {
                        // Try to use some logging implementation, and if a ClassNotFound exception is run, keep looking
                        setLoggerAdapter(clazz.newInstance());
                        break;
                    } catch (Throwable ignored) {
                    }
                }
        }
    }
Copy the code



conclusion

Java log is quite chaotic. I have used Python before, and the log is provided by Python official, so there are not so many problems. Logging adaptation is done in almost all third-party libraries, and the way it is implemented is basically the same.