Why log?

What is a log? The log is some data and records that you print out while the code is running. It is a good helper for troubleshooting problems quickly. It is a sharp tool for quarreling and dumping the pot!


Before you do something, think about why. Why do we need to log in development? The reason is simple: no one can guarantee that a program you write is bug-free, and even if you do enough testing, you can only reduce the probability of bugs.

Especially in today’s distributed environment, localization becomes more and more complex. So we want to get some information about the “run time” of a program, and logs are the most convenient.

So, this kind of good things after the blessing, of course, to use up ~

Java Logging Framework

Java logging frameworks go back to the days before JDK 1.3. Back then, people printed logs directly to the STDOUT or STDERR stream.

System.out.println()
System.err.println()
e.printStackTrace()
Copy the code

So log4J emerged in Ceki, after a series of development, as well as Ceki and Apache eating incident, gradually developed into SLF4J, Logback, log4J2 three of the most mainstream logging framework.

  • Slf4j: A “facade” framework for logging that allows users to hide the logging implementation by using the interface provided by SLF4J.
  • Logback: Like SLf4J, it was created for Ceki, so slF4J is implemented natively, the default logging framework for Spring Boot.
  • Log4j2: A top-level Apache project that performs better than logback, especially for asynchronous output.

PS: 4J stands for Java and is a common naming method in Java open source projects.

So the mainstream framework is slf4J + Logback or SLF4J + log4j2.

In the Alibaba development manual, the first log protocol is: Instead of using apis from Logging systems (Log4j, Logback), applications should rely on apis from Logging frameworks (SLF4J, JCL–Jakarta Commons Logging). Using a facade Logging framework is beneficial for maintenance and consistency of log processing for each class.

How do I select a log level?

Different standards have different definitions of log levels. Since SLF4J has pretty much taken over the world, we’ll focus on the five logging levels defined by SLF4J.

There are five log levels

The levels of ERROR, WARN, INFO, DEBUG, and TRACE are in descending order. The higher the configuration level, the less logs are generated.

When we configure logs of a certain level, it will also output logs of a higher level than it. For example, if we configure logs at the INFO level, it will output ERROR, WARN, and INFO logs.

As the name implies, these levels of logs output different levels of information:

  • ERROR: indicates ERROR logs. Serious errors affect normal services.
  • WARN: warning logs. Common errors have little impact on services.
  • INFO: Information log, recording routine things such as call time, input and output parameters, business information, etc.
  • DEBUG: Runtime data in key logic used for debugging;
  • TRACE: Indicates the most detailed information. Generally, this information is only recorded in a log file.

PS: According to the author’s experience, TRACE level is rarely used. Log4j recommends only the first four levels.

Level of inheritance

Sometimes, we may output different levels of logs depending on the business. For example, logs at INFO level are generated for some important services, logs at WARN level are generated for others, and logs of all libraries and frameworks, such as Spring, are disabled.

Log Level Inheritance

In general, we can set the root level. For certain services, we can set the specific package level. It is worth mentioning here that it is prudent to enable low-level logging, especially for root. I have heard about a fault before. In order to Debug a problem, Debug logs are enabled in the production environment at the root level. As a result, a large number of logs are printed instantly.

logging:
  level:
    org:
      springframework:
        orm: DEBUG
        transaction: DEBUG
    root: INFO
Copy the code

Matters needing attention

Switch to judge

In Ali’s Java development manual, there is this stipulation:

[Mandatory] : Enable or disable the output of trace, debug, or INFO logs.

That is, at low logging levels, a judgment should be added to “reduce unnecessary method call overhead” before logging. Here’s an example:

// Log the business code
User user = new User(1L."username"."pwd");
log.debug("userId is: {}", user.getId());
return user;

/ / user getId () implementation
public long getId(a) {
    System.out.println("GetId () called");
    return id;
}
Copy the code

In this code, the user.getid () method is called when I am typing the debug log. Even if we are configured to log only at the INFO level, the user.getid () method is still called when running this code, causing unnecessary overhead.

We can solve this problem if we add a judgment in front:

User user = new User(1L."username"."pwd");
if (log.isDebugEnabled()) {
    log.debug("userId is: {}", user.getId());
}
return user;
Copy the code

Of course, it depends on your project. If info-level logging must be enabled for your project deployment, then info-level logging is optional.

Use parameter placeholders

In the example above, we used curly braces {} as placeholders in the log. Using placeholders makes our code more elegant and concise than concatenating strings using the + operator.

In addition, String concatenation uses The StringBuilder append() method, which has a performance penalty. Using placeholders is just a substitution action, which can significantly improve performance.

The more logs, the better?

Logging is actually part of the code. We all know how important the readability of code is. In fact, there is no need to log everywhere, which makes it difficult to quickly locate key information when analyzing logs.

One thing we need to understand is, “We don’t need logs, we need valid logs.” What’s more, invalid log hit more, cost disk ~

Just print the key information where we care. More commonly used are unique ids, such as Gids, that we can usually quickly locate data.

Synchronous vs Asynchronous

As we know, logs will eventually be exported to files or other output streams, heavily using IO. Asynchronous output can significantly improve I/O performance. Therefore, you are advised to output logs asynchronously unless special requirements are met.

Using logback as an example, asynchrony is easy to configure using AsyncAppender:

<! -- Configure asynchronous logging -->
<appender name="FILE_ASYNC" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="ASYNC"/>
</appender>
Copy the code

trace Id

Today’s systems are getting bigger and more complex. The separation of the front and back ends and the distributed architecture make troubleshooting problems and tracing call links more and more complicated. To solve this problem, we can use a Trace Id to identify a complete invocation link.

Mainstream logging frameworks already support this. For example, logback provides an MDC mechanism called “Mapped Diagnostic Context”, which is implemented in the org.sl4j.mdc class. This article does not explain how to use it in detail, but those who are interested can refer to the official documentation.

Log file separation

We print logs to get some information we need. So we can separate out different types of logs, such as access_logs, or ERROR logs, and print them in a separate file.

It can also be printed to different log files according to different service modules, so that it will be more convenient for us to troubleshoot problems and do data statistics.

Obtaining log Instances

Those of you who have used the logging framework have more or less defined logging instances in your classes. But according to my observations, the way people define their journals varies. There’s a log, there’s a logger, there’s a logger. They are arbitrary, for example, we don’t add static, we don’t add final, we pass this, we pass.class.


There is a good Lombok note to mention: @slf4j. It can automatically inject a log instance based on your class, which is handy if your project uses Lombok. It doesn’t matter if you don’t use Lombok, see how it is defined:

@Slf4j
public class Demo {
Copy the code

} // This is equivalent to: public class Demo { private static final Logger log = LoggerFactory.getLogger(Demo.class); }

Above, we can happily hit the log ~

About the author