As a Java programmer, you’re no stranger to logging, no matter how big or small your project is; Because a good log can easily help us locate some production problems.

What I miss is not saying anything but system.out.println (” here’s the important log “);

What I miss is dreaming together system.err. Println (” here is the error log “);

Println is useful for daily development, but why not use it for logging in real applications?

System.out.println() really doesn’t have much use except for the convenience. Where is it convenient? In Eclipse, you just need to type syso, and then press the code hint button, and this method will automatically come out. I believe this is the reason why many Java beginners love it, because I did this at first, until… [Tears omitted]. What’s the downside? This is too much, such as log print is not controlled, print time is not determined, can not add filter, log level is not differentiated…

I remember my first contact with Log4J, which is an open source project of Apache. By using Log4J, we can control the destination of the log information to the console, files, etc., where we expect it to be output. We can also control the output format of each log; By defining the level of each log message, we can control the log generation process in more detail. Importantly, these can be flexibly configured through a configuration file without the need to modify the application code.

It’s true that log4J was one of the first popular logging frameworks and brought a lot of convenience to application development and maintenance, so why did such an excellent framework finally fall off the “pedestal”? Gradually replaced by logback? Here’s an excerpt from one of the biggest names on the Internet:

Logback is a relatively large improvement over Log4J, both in design and implementation. But while it’s hard to count them all, here are some of the reasons why you chose Logback over Log4J. Keep in mind that Logback and Log4J are conceptually similar in that they were built by the same group of developers.

  • Faster execution

    Based on our previous work with Log4J, Logback has rewritten the internal implementation to be up to 10 times faster in certain scenarios. Logback components require less memory while keeping them faster.

  • Adequate testing

    Logback took years and hours and hours of testing. Although Log4J is also tested, Logback is more fully tested at a different level than Log4J. We believe this is the most important reason why people choose Logback over Log4J. People want your diary framework to be stable and reliable even under harsh conditions.

slf4j log4j logback

Slf4j :The Simple Logging Facade for Java

The short answer is that SLF4J is a set of logging interfaces. Slf4j exists as a logging abstraction but does not provide a real implementation.

Slf4j provides a unified interface for the various logging frameworks, allowing users to log with a unified interface, but dynamically determining the true implementation framework. Frameworks such as Logback, Log4J, and Common-Logging implement these interfaces.

Slf4j source analysis

There are 29 classes in the SLF4J package [version 1.7.21] and it is impossible to list them all. So in terms of this statement that we know.

private static final Logger logger =
LoggerFactory.getLogger(DemoTest.class);
Copy the code

The above code is the usual way to declare a log object in your code.

Take a look at the methods provided by the Logger interface;

package org.slf4j;

public interface Logger {
    / / the root Logger
    String ROOT_LOGGER_NAME = "ROOT";
    String getName(a);
    // Check whether the logger Trace Trace is active; After Trace tracing is enabled, detailed information is printed.
    boolean isTraceEnabled(a);
    / / trace level
    void trace(String var1);
    void trace(String var1, Object var2);
    void trace(String var1, Object var2, Object var3);
    void trace(String var1, Object... var2);
    void trace(String var1, Throwable var2);
    boolean isTraceEnabled(Marker var1);
    void trace(Marker var1, String var2);
    void trace(Marker var1, String var2, Object var3);
    void trace(Marker var1, String var2, Object var3, Object var4);
    void trace(Marker var1, String var2, Object... var3);
    void trace(Marker var1, String var2, Throwable var3);
    // Prejudge to improve system performance
    boolean isDebugEnabled(a);
    void debug(String var1);
    void debug(String var1, Object var2);
    void debug(String var1, Object var2, Object var3);
    void debug(String var1, Object... var2);
    void debug(String var1, Throwable var2);
    boolean isDebugEnabled(Marker var1);
    void debug(Marker var1, String var2);
    void debug(Marker var1, String var2, Object var3);
    void debug(Marker var1, String var2, Object var3, Object var4);
    void debug(Marker var1, String var2, Object... var3);
    void debug(Marker var1, String var2, Throwable var3);
    / / info
    boolean isInfoEnabled(a);
    void info(String var1);
    void info(String var1, Object var2);
    void info(String var1, Object var2, Object var3);
    void info(String var1, Object... var2);
    void info(String var1, Throwable var2);
    boolean isInfoEnabled(Marker var1);
    void info(Marker var1, String var2);
    void info(Marker var1, String var2, Object var3);
    void info(Marker var1, String var2, Object var3, Object var4);
    void info(Marker var1, String var2, Object... var3);
    void info(Marker var1, String var2, Throwable var3);
    / / the warn level
    boolean isWarnEnabled(a);
    void warn(String var1);
    void warn(String var1, Object var2);
    void warn(String var1, Object... var2);
    void warn(String var1, Object var2, Object var3);
    void warn(String var1, Throwable var2);
    boolean isWarnEnabled(Marker var1);
    void warn(Marker var1, String var2);
    void warn(Marker var1, String var2, Object var3);
    void warn(Marker var1, String var2, Object var3, Object var4);
    void warn(Marker var1, String var2, Object... var3);
    void warn(Marker var1, String var2, Throwable var3);
    / / error level
    boolean isErrorEnabled(a);
    void error(String var1);
    void error(String var1, Object var2);
    void error(String var1, Object var2, Object var3);
    void error(String var1, Object... var2);
    void error(String var1, Throwable var2);
    boolean isErrorEnabled(Marker var1);
    void error(Marker var1, String var2);
    void error(Marker var1, String var2, Object var3);
    void error(Marker var1, String var2, Object var3, Object var4);
    void error(Marker var1, String var2, Object... var3);
    void error(Marker var1, String var2, Throwable var3);
}

Copy the code

isXXXXEnabled

Use isDebugEnabled to show how this isXXXXEnabled method works.

logger.debug(“the debug msg is ” +doMethod());

Assuming our log level is set to INFO, this sentence will not be logged, but this method will be called. To call this method, you must provide parameters. The result returned by the doMethod() method is part of the argument. DoMethod () is executed for n seconds. After n seconds, the debug() method is entered.

However, the log level is INFO. The result is that the log takes n seconds to construct the parameters, even though there is no output. Clearly there’s more to be gained than lost. Although it is practically impossible to construct such a parameter in n seconds, it can affect the performance of the system if the number of concurrent entries is large. In this case, it should be written as:

if(logger.isDebugEnabled()){
    logger.debug("the debug msg is " + doMethod());
} 
Copy the code

Next, the LoggerFactory class; Let’s start with the getLogger method:

public static Logger getLogger(String name) {
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    return iLoggerFactory.getLogger(name);
}

public static Logger getLogger(Class
        clazz) {
    Logger logger = getLogger(clazz.getName());
    if(DETECT_LOGGER_NAME_MISMATCH) { Class<? > autoComputedCallingClass = Util.getCallingClass();if(autoComputedCallingClass ! =null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
            Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(), autoComputedCallingClass.getName()));
            Util.report("See http://www.slf4j.org/codes.html#loggerNameMismatch for an explanation"); }}return logger;
}
Copy the code

The getLogger method provides two ways to overload. One is to pass in a name that marks the name of the current log. The other is to provide a Class object that also uses clazz.getName() as the name of the log.

The ILoggerFactory interface is obvious from the above code.

package org.slf4j;

public interface ILoggerFactory {
    Logger getLogger(String var1);
}
Copy the code

The ILoggerFactory interface provides a unified top-level type for different log implementations. Each logging framework needs to implement the ILoggerFactory interface to explain how it provides Loggers. Loggers such as Log4j and Logback, which provide parent-child relationships, are implemented in the ILoggerFactory implementation class. They also need to implement the Logger interface for logging.

Implementation in Logback

public class LoggerContext extends ContextBase implements
ILoggerFactory.LifeCycle
Copy the code

As mentioned above, implementations of different logging frameworks implement the ILoggerFactory interface.

 @Override
public final Logger getLogger(final String name) {

    if (name == null) {
        throw new IllegalArgumentException("name argument cannot be null");
    }

    // if we are asking for the root logger, then let us return it without
    // wasting time
    if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
        return root;
    }

    int i = 0;
    Logger logger = root;

    // check if the desired logger exists, if it does, return it
    // without further ado.
    Logger childLogger = (Logger) loggerCache.get(name);
    // if we have the child, then let us return it without wasting time
    if(childLogger ! =null) {
        return childLogger;
    }

    // if the desired logger does not exist, them create all the loggers
    // in between as well (if they don't already exist)
    String childName;
    while (true) {
        int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
        if (h == -1) {
            childName = name;
        } else {
            childName = name.substring(0, h);
        }
        // move i left of the last point
        i = h + 1;
        synchronized (logger) {
            childLogger = logger.getChildByName(childName);
            if (childLogger == null) {
                childLogger = logger.createChildByName(childName);
                loggerCache.put(childName, childLogger);
                incSize();
            }
        }
        logger = childLogger;
        if (h == -1) {
            returnchildLogger; }}}Copy the code

For the source code for Logback, refer to the article in this series: Logback Source code series

The pits I’ve encountered

One of the most common pits I’ve encountered myself in the logging area is dependency conflicts with log jars; Because many of the frameworks or tripartite dependencies used by the project may have their own integrated logging framework, sometimes our own projects will also have their own set of logging scheme, so this will largely lead to the occurrence of dependency conflict. Fortunately, conflict resolution dependencies on logging packages in a project are also easy to eliminate.

Another is the introduction of different framework implementations after slF4J is used; For example, log4J and Logback were introduced together.

As you can see from the getLogger method above, we need to retrieve the ILoggerFactory before fetching a Logger:

ILoggerFactory iLoggerFactory = getILoggerFactory();

public static ILoggerFactory getILoggerFactory(a) {
    if (INITIALIZATION_STATE == 0) {
        Class var0 = LoggerFactory.class;
        synchronized(LoggerFactory.class) {
            if (INITIALIZATION_STATE == 0) {
                INITIALIZATION_STATE = 1;
                / / initializationperformInitialization(); }}}switch(INITIALIZATION_STATE) {
    case 1:
        return SUBST_FACTORY;
    case 2:
        throw new IllegalStateException("org.slf4j.LoggerFactory could not be successfully initialized. See also http://www.slf4j.org/codes.html#unsuccessfulInit");
    case 3:
        return StaticLoggerBinder.getSingleton().getLoggerFactory();
    case 4:
        return NOP_FALLBACK_FACTORY;
    default:
        throw new IllegalStateException("Unreachable code"); }}Copy the code

In getILoggerFactory, init is done through performInitialization methods. In performInitialization, the bind method is called, and the bind method prints an exception or error message in many cases:

private static final void bind() {
    String msg;
    try {
        Set<URL> staticLoggerBinderPathSet = null;
        if(! isAndroid()) { staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet(); reportMultipleBindingAmbiguity(staticLoggerBinderPathSet); } StaticLoggerBinder.getSingleton(); INITIALIZATION_STATE = 3; reportActualBinding(staticLoggerBinderPathSet); fixSubstituteLoggers(); replayEvents(); SUBST_FACTORY.clear(); } catch (NoClassDefFoundError var2) { msg = var2.getMessage();if(! messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) { failedBinding(var2); throw var2; } INITIALIZATION_STATE = 4; Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
        Util.report("Defaulting to no-operation (NOP) logger implementation");
        Util.report("See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.");
    } catch (NoSuchMethodError var3) {
        msg = var3.getMessage();
        if(msg ! = null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
            INITIALIZATION_STATE = 2;
            Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
            Util.report("Your binding is version 1.5.5 or earlier.");
            Util.report("Upgrade your binding to version 1.6.x.");
        }

        throw var3;
    } catch (Exception var4) {
        failedBinding(var4);
        throw new IllegalStateException("Unexpected initialization failure", var4); }}Copy the code

ReportMultipleBindingAmbiguity method is used to prompt multiple bindings, and through the log tell you specific is that there are several binding.

Class path contains multiple SLF4J bindings.

ReportActualBinding Indicates that when the binding is successful, the log will show which logging framework is successfully bound

Actual binding is of type [XXXX]

About StaticLoggerBinder

Start with the following exception:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for
further details.
Copy the code

This problem is because the class file loading org. Slf4j. Impl. StaticLoggerBinder failure; Why is that? Here’s why:

This error is reported when the org.slf4j.impl.StaticLoggerBinder class could not be loaded into memory. This happens when no appropriate SLF4J binding could be found on the class path. Placing one (and only one) of slf4j-nop.jar, slf4j-simple.jar, slf4j-log4j12.jar, slf4j-jdk14.jar or logback-classic.jar on the class path should solve the problem.

This error occurs because the StaticLoggerBinder class cannot be loaded into memory. When this happens, the appropriate SLF4J binding classpath cannot be found. Slf4j-nop places one (and only one). Slf4j – simple jar. Slf4j – log4j12 jar. Slf4j – jdk14 jar. The jar or logback – classic. The classpath of the JAR should solve this problem.

That is, no specific logging framework was found to bind the implementation. So we need to introduce a specific log implementation JAR.

Online to see an article about SLF4J, feel good, share to everyone: SLF4J source analysis

I am still adapting to the new environment, and the frequency of blog updates has dropped sharply. Learn and write slowly. I hope you will forgive me!