"Oh, there you are! Did you remember that we have another lesson today?"

"No, I was just looking for you. Almost…"

"Excellent, then let's start. Today I want to tell you about logging."

"The log is a list of the events that have occurred. Almost like a ship's log or a diary. Or Twitter — maybe you can relate better to that. Unsurprisingly, a logger is an object that you use for logging."

"In programming, it's customary to log almost everything. And in Java, we log everything and even a little more."

"The fact is that Java programs are very often large server applications without a UI, console, etc. They process thousands of user requests at the same time, and there are often various errors. Especially when different threads start interfering with each other."

"In fact, the only way to search for rarely reproducible bugs and failures in these circumstances is to log everything that happens on each thread."

"Most often, the log contains information about method arguments, any caught errors, and a lot of intermediate information."

"The more complete the log, the easier it is to reproduce a sequence of events and track the causes of failures or bugs."

"Sometimes logs reach several gigabytes per day. This is normal."

"A few gigabytes? O_o"

"Yep. Most often, log files are automatically archived, with an indication of the relevant date."

"Whoa."

"Uh-huh. Initially, Java didn't have its own logger. As a result, several independent loggers were written. The most common of these was log4j."

"A few years later, Java got a logger of its own, but its functionality was far inferior and it wasn't widely used."

"It's a fact that Java has an official logger, but the entire community of Java programmers prefers to use other loggers."

"Later, several more loggers were written based on log4j."

"Then the special universal logger slf4j, which is now widely used, was written for all of them. It is very similar to log4j, so I'll use it as an example when explaining logging."

"The entire logging process consists of three parts."

"First, collect information."

"Second, filter the collected information."

"Third, record the selected information."

"Let's start with collection. Here is a typical example of a class that logs:"

Class with logging
class Manager
{
 private static final Logger logger = LoggerFactory.getLogger(Manager.class);

 public boolean processTask(Task task)
 {
  logger.debug("processTask id = " + task.getId());
  try
  {
   task.start();
   task.progress();
   task.complete();
   return true;
  }
  catch(Exception e)
  {
   logger.error("Unknown error", e);
   return false;
  }
 }
}

"Pay attention to the words highlighted in red."

"Line 3 – Create the logger object. Such a static object is created in almost every class! Well, except for classes that don't do anything other than store data."

"LoggerFactory is a special class for creating loggers, and getLogger is one of its static methods. The current object is usually passed, but various options are possible."

"Line 7 – Information about the method call is written to the logger. Note that this is the first line of the method. As soon as the method is called, we immediately write information to the log."

"We call the debug method, which means the information's importance is DEBUG level. This is used for filtering. I'll tell you about that in a couple of minutes."

"Line 17 – We catch an exception and... immediately write it to the log! This is exactly what needs to be done."

"This time we call the error method, which immediately indicates that the information is ERROR level"

Logger - 1

"Everything seems clear for now. Well, as far as it can be clear in the middle of our conversation."

"Great, then let's move on to message filtering."

"Usually, each log message has its own importance level, which you can use to discard some of the messages. Here are importance levels I mentioned:"

Importance level Description
ALL All messages
TRACE Fine-grained debug messages
DEBUG Important debug messages
INFO Informational messages
WARN Warnings
ERROR Errors
FATAL Fatal errors
OFF No messages

These levels are also used when filtering messages.

Suppose you set the logging level to WARN. Then all messages that are less important than WARN will be discarded: TRACE, DEBUG, INFO.

If you set the filtering level to FATAL, then even ERROR messages will be discarded.

"There are two more importance levels used when filtering: OFF, which discards all messages; and ALL, which shows all messages (nothing is discarded)."

"How and where do I set up filtering?"

"I'll tell you without further ado."

"Usually, log4j logger settings are specified in the log4j.properties file."

You can specify multiple appender objects in this file. Data is written to these objects. There are data sources, and there are appenders — objects that have opposite purposes. Objects that data flows into like water.

"Here are some examples:"

Logging to the console
# Root logger option
log4j.rootLogger = INFO, stdout

# Direct log messages to stdout
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss}

Lines 1 and 4 – These are comments

Line 2 – We indicate the logging level we want. All less important levels (DEBUG, TRACE) will be discarded.

In the same place, we add a comma and then indicate the name of the object (which we come up with ourselves) that the log will be written to. Lines 5-9 contain its settings.

Line 5 – We specify the type of appender (ConsoleAppender).

Line 6 – We indicate exactly where we are writing (System.out.).

Line 7 – We set the class that will manager conversion patterns (PatternLayout).

Line 8 – We set the conversion pattern that will be used for writing. In the example above, it's the date and time.

"And here's what writing to a file looks like:"

Logging to a file
# Root logger option
log4j.rootLogger = INFO, file

# Direct log messages to a log file
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File = C:\\loging.log
log4j.appender.file.MaxFileSize = 1MB
log4j.appender.file.MaxBackupIndex = 1
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %-5p %c{1}:%L - %m%n

"Line 2 sets the message filtering level and the name of the appender object (sink)."

"Line 5 – We specify the file appender type (RollingFileAppender)."

"Line 6 – We specify the name of the file that the log will be written to."

"Line 7 – We specify the maximum log size. When this size limit is exceeded, a new file is created."

"Line 8 – We specify the number of old log files to be stored."

"Lines 9-10 – Set the conversion pattern."

"I don't know what's happening here, but I can guess. That's encouraging."

"That's great. Then here's an example of how to write a log to a file and the console:"

Logging to the console and a file
# Root logger option
log4j.rootLogger = INFO, file, stdout

# Direct log messages to a log file
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File = C:\\loging.log
log4j.appender.file.MaxFileSize = 1MB
log4j.appender.file.MaxBackupIndex = 1
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %-5p %c{1}:%L - %m%n

# Direct log messages to stdout
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss}

"Ah, so you can do that? That's great!"

"Yep. You can declare as many appenders as you want and customize each one."

Furthermore, each appender can have very flexible settings for message filtering. Not only can we assign an individual message filtering level to each appender, but we can also filter messages by package! That's why you need to specify a class when creating a logger (I'm talking about LoggerFactory.getLogger).

"For example:"

Logging to the console and a file
# Root logger option
log4j.rootLogger = INFO, file, stdout

# Direct log messages to a log file
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.threshold = DEBUG
log4j.appender.file.File = C:\\loging.log
log4j.appender.file.MaxFileSize = 1MB
log4j.appender.file.MaxBackupIndex = 1
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %-5p %c{1}:%L - %m%n

# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.threshold = ERROR
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss}

log4j.logger.org.springframework = ERROR
log4j.logger.org.hibernate = ERROR
log4j.logger.com.codegym = DEBUG
log4j.logger.org.apache.cxf = ERROR

"Lines 6 and 15 – We set our own filtering level for each appender."

"Lines 20-23 – We specify the package name and the filtering level for its messages. Log4j.logger is a prefix: the package name is highlighted in orange."

"Really? You can even do that. Well, cool!"

"By the way, neither log4j nor slf4j are included in the JDK. You'll need to download them separately. You can do that here. But there's another way:"

"Step 1.Add imports to the class:"

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

"Step 2. Put the cursor on these lines and press Alt+Enter in IntelliJ IDEA"

"Step 3. Choose the 'File jar on web» menu item.'

"Step 4. Choose 'slf4j-log4j13.jar'"

"Step 5. Specify where to download the library (jar)"

"Step 6. Use the classes you need."

"Whoa! What a day this has been. So much that is new and so much that is cool!"

"Here's another good article on logging: https://docs.oracle.com/javase/10/core/java-logging-overview.htm#JSCOR-GUID-48004124-2C00-49F7-A640-0C0DDA271DBC"

"Alright, that's enough. Go relax, programmer."