JavaRush /Java Blog /Random EN /Java Logging. Unwind a ball of stacktrace

Java Logging. Unwind a ball of stacktrace

Published in the Random EN group
"Good afternoon, an incident was recorded on the prom today, I ask the developer to join the analysis group." Something like this may start one of your days at work, or maybe the morning - it doesn’t matter. But let's start in order. By solving problems here on CodeGym, you learn how to write code that works and does what is expected of it. If you look at the help section , it is clear that this does not always work the first time. It will be the same at work. You will not always solve the problem the first time: bugs are our eternal companions. It is important that you can restore the events of the appearance of the bug. Logging.  Unwind a ball of stacktrace - 1Let's just take an example. Imagine that you are a police officer. You were called to the scene (glass was broken in the store), you arrived, and they are waiting for answers from you about what happened. Where to begin? I don't know how the police work. Very conditional - they start looking for witnesses, evidence and all that stuff. What if the place itself could tell in detail what happened? For example, like this:
  • 21:59 the owner turned on the alarm (5 minutes before full activation)
  • 22:00 the owner closed the door
  • 22:05 full activation of the alarm
  • 22:06 the owner pulled the handle
  • 23:15 noise sensor turned on
  • 23:15 a flock of dogs ran past, loudly barking
  • 23:15 noise sensor turned off
  • 01:17 the shock sensor on the outer glass of the showcase turned on
  • 01:17 dove flew into the glass
  • 01:17 glass broke
  • 01:17 siren on
  • 01:17 the dove shook itself off and flew away
Well, you won’t have to dig into such details for a long time, it’s immediately clear what happened. Also in development. It's very cool when you can tell what happened from the records. Now you may remember debugging, because you can debug everything. And here it is not. You went home, and at night everything broke down, there is nothing to debug: you need to understand why it broke and fix it. This is where the logs come into play, the story of everything that happened during the night. I suggest that you think in the course of the article, what is one of the most famous loggers (not quite a logger, rather monitoring), which everyone who listens to (watches) the news has probably heard about? Thanks to him, some events are restored. Now seriously. Logging in Java is the process of recording any events that occur in the code. It's your responsibility as a programmer to write down what your code did, because then these logs will be given to you for analysis. If everything is done well, then any bug will be sorted out and eliminated very quickly. Here, probably, I will not delve into what loggers are. In this article, we restrict ourselves to a simplejava.util.Logger: it is more than enough for dating. Each log entry contains date-time, event level, message. Date-time is set automatically. The level of the event is chosen by the author of the message. There are several levels. The main ones are info, debug, error.
  • INFO - usually these are informational messages about what is happening, something like a history by date: 1915 - something happened, 1916 - something else.
  • DEBUG - describes the events of a particular moment in more detail. For example, the details of any battle in history is the debug level. " Commander Taikitovich advanced with his army towards the village of Selovich ."
  • ERROR - errors that occur are usually written here. You probably noticed that when you wrap something in try-catch, the block catchis substituted with e.printStacktrace(). It only outputs the entry to the console. With the help of a logger, you can send this entry to the logger (ha ha), you understand.
  • WARN - warnings are written here. For example, an overheating light in a car. This is just a warning, and it is better to change something, but this is not a breakdown yet. That's when the machine breaks down, then we will log with the ERROR level.
Got it with the levels. But don't worry: the line between them is very thin - not everyone can explain it. Plus, it may differ from project to project. The senior developer will explain to you at what level and what to log. The main thing is that these records are enough for you for future analysis. And this is understood on the go. Next is the settings. You can tell loggers where to write (to the console, file, jms or somewhere else), specify the level (info, error, debug...). An example of settings for our simple logger looks like this:
handlers = java.util.logging.FileHandler, java.util.logging.ConsoleHandler

java.util.logging.FileHandler.level     = INFO
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
java.util.logging.FileHandler.append    = true
java.util.logging.FileHandler.pattern   = log.%u.%g.txt

java.util.logging.ConsoleHandler.level     = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
In this case, everything is set up so that the logger writes to the file and to the console at the same time. For that case? if something is erased in the console, plus it's easier to search by file. INFO level for both. The file name pattern is also set. This is the minimum config that allows you to write both to the console and to the file at once. java.util.logging.FileHandler.appendset to true so that old records are not erased from the file. An example of using this (without comments, the logger comments on itself):
public class Main {
    static Logger LOGGER;
    static {
        try(FileInputStream ins = new FileInputStream("C:\\log.config")){ \\полный путь до file с конфигами
            LOGGER = Logger.getLogger(Main.class.getName());
        }catch (Exception ignore){
    public static void main(String[] args) {
        try {
            LOGGER.log(Level.INFO,"Начало main, создаем лист с типизацией Integers");
            List<Integer> ints = new ArrayList<Integer>();
            LOGGER.log(Level.INFO,"присваиваем лист Integers листу без типипзации");
            List empty = ints;
            LOGGER.log(Level.INFO,"присваиваем лист без типипзации листу строк");
            List<String> string = empty;
            LOGGER.log(Level.WARNING,"добавляем строку \"бла бла\" в наш переприсвоенный лист, возможна ошибка");
            string.add("бла бла");
            LOGGER.log(Level.WARNING,"добавляем строку \"бла 23\" в наш переприсвоенный лист, возможна ошибка");
            string.add("бла 23");
            LOGGER.log(Level.WARNING,"добавляем строку \"бла 34\" в наш переприсвоенный лист, возможна ошибка");
            string.add("бла 34");

            LOGGER.log(Level.INFO,"выводим все элементы листа с типизацией Integers в консоль");
            for (Object anInt : ints) {

            LOGGER.log(Level.INFO,"Размер equals " + ints.size());
            LOGGER.log(Level.INFO,"Получим первый элемент");
            Integer integer = ints.get(0);
            LOGGER.log(Level.INFO,"выведем его в консоль");

        }catch (Exception e){
            LOGGER.log(Level.WARNING,"что-то пошло не так" , e);
This is not the best example, I took the one that was at hand. Sample output:
апр 19, 2019 1:10:14 AM generics.Main main
INFO: Начало main, создаем лист с типизацией Integers
апр 19, 2019 1:10:14 AM generics.Main main
INFO: присваиваем лист Integers листу без типипзации
апр 19, 2019 1:10:14 AM generics.Main main
INFO: присваиваем лист без типипзации листу строк
апр 19, 2019 1:10:14 AM generics.Main main
WARNING: добавляем строку "бла бла" в наш переприсвоенный лист, возможна ошибка
апр 19, 2019 1:10:14 AM generics.Main main
WARNING: добавляем строку "бла 23" в наш переприсвоенный лист, возможна ошибка
апр 19, 2019 1:10:14 AM generics.Main main
WARNING: добавляем строку "бла 34" в наш переприсвоенный лист, возможна ошибка
апр 19, 2019 1:10:14 AM generics.Main main
INFO: выводим все элементы листа с типизацией Integers в консоль
апр 19, 2019 1:10:14 AM generics.Main main
INFO: Размер equals 3
апр 19, 2019 1:10:14 AM generics.Main main
INFO: Получим первый элемент
апр 19, 2019 1:10:14 AM generics.Main main
WARNING: что-то пошло не так
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
	at generics.Main.main(
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(
	at java.lang.reflect.Method.invoke(
	at com.intellij.rt.execution.application.AppMain.main(
Here I want to focus on the records:
апр 19, 2019 1:10:14 AM generics.Main main
INFO: Размер equals 3
апр 19, 2019 1:10:14 AM generics.Main main
INFO: Получим первый элемент
Such a record is rather useless, not informative. Like the error entry:
WARNING: что-то пошло не так
You should not write this: this is a log for the sake of a log, it will only interfere. Try to always write meaningful things. I think this is enough to stop using System.out.printlnand move on to adult toys. We java.util.logginghave flaws. For example, levels, those that I described above, are not here, but they are in most used loggers. For the article, I chose java.util.loggingbecause it does not require additional connection manipulations. I also note that it can be used LOGGER.infoinstead . LOGGER.log(Level.INFO... One of the drawbacks pops up already here: LOGGER.log(Level.WARNING,"что-то пошло не так" , e);- allows you to send a message and an object Exception, the logger will write it down beautifully. At the same time asLOGGER.warning("");only accepts a message, i.e. You cannot pass an exception, you must translate it into a string yourself. I hope this example is enough to get acquainted with Java logging. Then you can connect other loggers (log4j, slf4j, Logback ...) - there are many of them, but the essence is the same ^ to record the history of actions. Official tutorial