2.1 First logger - log4j
As you already know, the history of logs began with System.err.println()
the output of a record to the console. It is still actively used for debugging, for example, Intellij IDEA uses it to display error messages to the console. But this option does not have any settings, so let's move on.
The first and most popular logger was called Log4j
. It was a good and highly customizable solution. Due to various circumstances, this decision never got into the JDK, which greatly upset the entire community.
This logger was not just able to log, it was created by programmers for programmers and allowed them to solve problems that constantly arose in connection with logging.
As you already know, logs are written in the end so that some person reads them and tries to understand what happened during the program's operation - what and when went wrong as expected.
There log4j
were three things for this:
- subpackage logging;
- set of appenders (results);
- hot reload settings.
Firstly, the settings log4j
could be written in such a way as to enable logging in one package and disable it in another. For example, it was possible to enable logging in the com.codegym.server
, but disable it in com.codegym.server.payment
. This made it possible to quickly remove unnecessary information from the log.
Secondly, log4j
it allowed writing logging results to several log files at once. And the output to each could be configured individually. For example, in one file it was possible to write only information about serious errors, in another - logs from a specific module, and in a third - logs for a certain time.
Each log file was thus tuned to a particular type of expected problem. This greatly simplifies the life of programmers who do not enjoy looking through gigabyte log files manually.
And finally, thirdly, log4j
it allowed changing the log settings directly while the program was running, without restarting it. This was very handy when it was necessary to correct the work of the logs in order to find additional information on a specific error.
Important! There are two versions of the log log4j
: 1.2.x and 2.xx , which are incompatible with each other .
You can connect the logger to the project using the code:
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.17.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.2</version>
</dependency>
</dependencies>
2.2 First official logger - JUL: java.util.logging
After the zoo of loggers appeared in the Java community, the developers JDK
decided to make one standard logger that everyone would use. This is how the logger appeared JUL
: package java.util.logging
.
However, during its development, the creators of the logger took as a basis not log4j
, but a variant of the logger from IBM, which influenced its development. The good news is that the logger JUL
is included JDK
, the bad news is that few people use it.
Not only did the developers JUL
make “another universal standard” , they also made their own logging levels for it, which differed from those accepted by popular loggers at that time.
And that was a big problem. After all, products are Java
often collected from a large number of libraries, and each such library had its own logger. So it was necessary to configure all the loggers that are in the application.
Although the logger itself is pretty good. Creating a logger is more or less the same. To do this, you need to import:
java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());
The class name is specially passed in order to know where the logging is coming from.
Only with the release, the developers solved important problems, after which it JUL
is really convenient to use. Before that, it was some kind of second-rate logger.
This logger also supports lambda expressions and lazy evaluation. Starting with Java 8
, you can pass Supplier<String>
. This helps to read and create a string only at the moment when it is really needed, and not every time, as it was before.
Methods with an argument Supplier<String> msgSupplier
look like this:
public void info(Supplier msgSupplier) {
log(Level.INFO, msgSupplier);
}
2.3 First logger wrapper - JCL: jakarta commons logging
For a long time there was no single standard among loggers, it JUL
should have become one, but it was worse log4j
, so a single standard never appeared. But a whole zoo of loggers appeared, each of which wanted to become the same.
However, ordinary Java developers did not like that almost every library has its own logger and needs to be configured somehow in a special way. Therefore, the community decided to create a special wrapper over other loggers - this is howJCL: jakarta commons logging
And again, the project, which was created to be a leader, did not become one. You can't create a winner, you can only become a winner. The functionality JCL
was very poor and no one wanted to use it. The logger, designed to replace all loggers, met the same fate as it JUL
was not used.
Although it has been added to many libraries released by the Apache community, the zoo of loggers has only grown.
2.4 First last logger - Logback
But that's not all. The developer log4j
decided that he was the smartest (well, after all, most people used his logger) and decided to write a new improved logger that would combine the advantages log4j
of other loggers.
The new logger was called Logback
. It was this logger that was supposed to become the future single logger that everyone would use. It was based on the same idea as in log4j
.
You can connect this logger to the project using the code:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.6</version>
</dependency>
The differences were in Logback
:
- improved performance;
- added native support
slf4j
; - expanded filtering option.
Another advantage of this logger was that it had very good default settings. And you had to configure the logger only if you wanted to change something in them. Also, the settings file was better adapted to corporate software - all its configurations were set as xml/
.
By default, Logback
it does not require any settings and records all logs from the level DEBUG
and above. If you need different behavior, it can be configured via xml
configuration:
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>app.log</file>
<encoder>
<pattern>%d{HH:mm:ss,SSS} %-5p [%c] - %m%n</pattern>
</encoder>
</appender>
<logger name="org.hibernate.SQL" level="DEBUG" />
<logger name="org.hibernate.type.descriptor.sql" level="TRACE" />
<root level="info">
<appender-ref ref="FILE" />
</root>
</configuration>
2.5 Latest universal logger - SLF4J: Simple Logging Facade for Java
How long can it be to find the golden mean...
In 2006, one of the creators log4j
left the project and decided to try again to create a universal logger. But this time it was not a new logger, but a new universal standard (wrapper) that allowed different loggers to interact together.
This logger was called slf4j — Simple Logging Facade for Java
, it was a wrapper around log4j
, JUL
, common-loggins and logback
. This logger solved a real problem - managing a zoo of loggers, so everyone immediately started using it.
We heroically solve the problems we create for ourselves. As you can see, progress has reached the point that we have created a wrapper over the wrapper ...
The wrap itself consists of two parts:
API
, which is used in applications;- Implementations that are added as separate dependencies for each logger.
You can connect the logger to the project using the code:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.17.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.17.2</version>
</dependency>
It is enough to connect the correct implementation and that's it: the whole project will work with it.
2.6 Optimization in slf4j
Slf4j
supports all new features such as string formatting for logging . Before this there was such a problem. Let's say you want to print a message to the log:
log.debug("User " + user + " connected from " + request.getRemoteAddr());
There is a problem with this code. Suppose your application works on production
and does not write any to the log DEBUG-messages
, however, the method log.debug()
will still be called, and when it is called, the following methods will also be called:
user.toString();
request.getRemoteAddr();
Calling these methods slows down the application. Their call is needed only during debugging, but they are called anyway.
From the point of view of logic, this problem had to be solved in the logging library itself. And in the first version of log4j the solution came up:
if (log.isDebugEnabled()) {
log.debug("User " + user + " connected from " + request.getRemoteAddr());
}
Instead of one line for the log, now it was necessary to write three. Which dramatically worsened the readability of the code, and lowered the popularity of log4j
.
The logger slf4j
was able to slightly improve the situation by offering smart logging. It looked like this:
log.debug("User {} connected from {}", user, request.getRemoteAddr());
where {}
denote the insertion of arguments that are passed in the method. That is, the first {}
corresponds to user, the second {}
to request.getRemoteAddr()
.
These parameters will be concatenated into a single message only if the logging level allows logging. Not perfect, but better than all the other options.
After that, SLF4J
it began to grow rapidly in popularity, at the moment this is the best solution.
Therefore, we will consider logging using the example of a bundle slf4j-log4j12
.
GO TO FULL VERSION