JavaRush /Java блог /Random UA /Навіщо потрібне логування
Professor Hans Noodles
41 рівень

Навіщо потрібне логування

Стаття з групи Random UA
Вітання! При написанні лекцій я особливо наголошую, якщо якась конкретна тема обов'язково використовуватиметься у реальній роботі. Навіщо потрібне логування - 1 Так ось, УВАГА! Тема, якою ми торкнемося сьогодні, нагоді тобі на всіх твоїх проектах з першого дня роботи. Ми поговоримо про логування. Тема ця зовсім не складна (я навіть сказав би легка). Але на першій роботі і так буде достатньо стресу, щоб ще розбиратися з очевидними речами, тому краще досконально розібрати її зараз :) Отже, почнемо. Що таке логування? Логування - це запис кудись даних про роботу програми. Місце, куди ці дані записуються називається « лог». Виникає одразу два питання – куди і які дані записуються? Почнемо із «куди». Записувати дані про роботу програми можна в різні місця. Наприклад, ти під час навчання часто виводив дані у консоль за допомогою System.out.println(). Це справжнє логування, хоч і найпростіше. Звичайно, для клієнта чи команди підтримки продукту це не дуже зручно: вони явно не захочуть встановлювати IDE та моніторити консоль :) Є й звичніший людині формат запису інформації — у текстовий файл. Людям набагато зручніше читати їх у такому вигляді, і вже точно набагато зручніше зберігати! Тепер друге питання: які дані про роботу програми мають записуватися в балку? А ось тут все залежить від тебе! Система логування Java дуже гнучка. Ти можеш налаштувати її таким чином, що в ліг потрапить весь хід роботи твоєї програми. Це, з одного боку, добре. Але з іншого - уяви собі, яких розмірів можуть досягти логи Facebook або Twitter, якщо туди писати взагалі все. Такі великі компанії, напевно, мають можливість зберігати навіть таку кількість інформації. Але уяви, як складно буде шукати інформацію про одну критичну помилку в логах на 500 гігабайт тексту? Це навіть гірше, ніж голка у стозі сіна. Тому логування Java можна налаштувати так, щоб в журнал (лог) записувалися лише дані про помилки. Або навіть лише про критичні помилки! Хоча, говорити "логування в Java" не зовсім правильно. Справа в тому, що потреба ведення логів виникла у програмістів раніше, чим цей функціонал був доданий у мову. І на той час, як у Java з'явився власна бібліотека для логування, всі вже користувалися бібліотекою log4j. Історія появи логування в Java насправді дуже довга і пізнавальна, на дозвіллі можеш почитатицей пост на Хабрі . Коротше кажучи, своя бібліотека логування в Java є, але їй майже ніхто не користується :) Пізніше, коли з'явилося кілька різних бібліотек логування, і всі програмісти почали користуватися різними, виникла проблема сумісності. Щоб люди не робабо те саме за допомогою десятка різних бібліотек з різними інтерфейсами, був створений абстрагуючий фреймворк slf4j("Service Logging Facade For Java"). Абстрагуючим він називається тому, що хоч і користуєшся класами slf4j і викликаєш їх методи, під капотом у них працюють усі попередні фреймворки логування: log4j, стандартний java.util.logging та інші. Якщо тобі зараз потрібна якась специфічна фіча log4j, якої немає в інших бібліотек, але ти не хотів би при цьому жорстко прив'язувати проект саме до цієї бібліотеки, просто використовуй slf4j. А вона вже «смикне» методи log4j. Якщо ти передумаєш і вирішиш, що фічі log4j тобі більше не потрібні, тобі треба тільки переналаштувати обгортку (тобто slf4j) на використання іншої бібліотеки. Твій код не припинить працювати, адже в ньому ти викликаєш методи slf4j, а не конкретної бібліотеки. Невеликий відступ. Щоб наступні приклади запрацювали, тобі потрібно завантажити бібліотеку slf4jзвідси , і бібліотеку log4j звідси . Далі архів потрібно розпакувати і додати потрібні нам jar-файли в classpath через Intellij IDEA. Пункти меню: File -> Project Structure -> Libraries Вибираєш потрібні jar-ники та додаєш у проект (в архівах, які ми скачали, лежить багато jar'ників, подивися потрібні на картинках) Примітка Навіщо потрібне логування - 2Навіщо потрібне логування - 3— ця інструкція для тих студентів, які не вміють використовувати Maven. Якщо ти вмієш ним користуватися, краще спробуй почати з нього: це зазвичай набагато простіше Якщо використовуєш Maven , додай таку залежність:
<dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.14.0</version>
</dependency>
Добре розглянемо, як працює slf4j. Як нам зробити так, щоб хід роботи програми кудись записувався? Для цього нам потрібні дві речі — логер і апендер . Почнемо з першого. Логгер - це об'єкт, який повністю керує веденням записів . Створити логер дуже легко: це робиться за допомогою статичного методу LoggerFactory.getLogger(). Як параметр у метод потрібно передати клас, робота якого логуватиметься. Запустимо наш код:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyTestClass {

   public static final Logger LOGGER = LoggerFactory.getLogger(MyTestClass.class);

   public static void main(String[] args) {

       LOGGER.info("Test log record!!!");
       LOGGER.error("В программе возникла ошибка!");
   }
}
Виведення в консоль: ERROR StatusLogger No Log4j 2 configuration file found. Використовуючи невеликі параметри (залишення лише нагадувань на консолі), або user programmatically забезпечені параметрами. Set system property 'log4j2.debug' до show Log4j 2 internal initialization logging. Натисніть https://logging.apache.org/log4j/2.x/manual/configuration.html для інструкцій на те, як configure Log4j 2 15:49:08.907 [main] ERROR MyTestClass - У програмі виникла помилка! Що ми тут бачимо? Спочатку ми бачимо повідомлення про помилку. Вона з'явилася, бо зараз у нас не вистачає необхідних налаштувань. Тому наш логер зараз вміє виводити тільки повідомлення про помилки (ERROR) і тільки в консоль. Метод logger.info()виконано був. А ось logger.error()спрацював! У консолі з'явилася поточна дата, метод, де виникла помилка (main), слово ERROR та наше повідомлення! ERROR – це рівень логгрування. Загалом, якщо запис у лозі позначений словом ERROR, отже, у цьому місці програми сталася помилка. Якщо запис позначений словом INFO — це просто поточна інформація про нормальну роботу програми. У бібліотеці SLF4J багато різних рівнів логування, які дозволяють гнучко налаштувати ведення журналу. Управляти ними дуже легко: вся необхідна логіка вже закладена в клас Logger. Тобі досить просто викликати необхідні методи. Якщо хочеш залогувати звичайне повідомлення, викликай метод logger.info(). Повідомлення про помилку — logger.error(). Вивести попередження - logger.warn() Тепер поговоримо про апендера . Апендер це місце, куди приходять твої дані.Можна сміливо сказати, протилежність джерелу даних — «точка B». За промовчанням дані виводяться в консоль. Зверніть увагу, що в попередньому прикладі нам не довелося нічого налаштовувати: текст з'явився в консолі сам, але при цьому логер з бібліотеки log4j вміє виводити в консоль тільки повідомлення рівня ERROR. Людям, очевидно, зручніше читати логи з текстового файлу і зберігати логи в таких же файлух. Щоб змінити поведінку логера за замовчуванням, нам потрібно налаштувати свій файловий апендер . Для початку, прямо в папці src потрібно створити файл log4j.xml , або в папці resources, якщо використовуєш Maven, або в ресурсах folder, в випадку ви використовуєте Maven. З форматом xml ти вже знайомий, у нас нещодавно була лекція про нього :) Ось таким буде його вміст:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
   <Appenders>
       <File name="MyFileAppender" fileName="C:\Users\Username\Desktop\testlog.txt" immediateFlush="false" append="false">
           <PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
       </File>
   </Appenders>
   <Loggers>
       <Root level="INFO">
           <AppenderRef ref="MyFileAppender"/>
       </Root>
   </Loggers>
</Configuration>
Виглядає не надто й складно :) Але давай таки пройдемося по вмісту.
<Configuration status="INFO">
Це так званий status-logger. Він не має відношення до нашого логера і використовується у внутрішніх процесах log4j. Можеш встановити status="TRACE" замість status="INFO", і в консоль буде виводитися вся інформація про внутрішню роботу log4j (status-logger виводить дані саме в консоль, навіть якщо наш апендер для програми буде файловим). Нам це зараз не потрібно, тож залишимо все як є.
<Appenders>
   <File name="MyFileAppender" fileName="C:\Users\Евгений\Desktop\testlog.txt" append="true">
       <PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
   </File>
</Appenders>
Тут ми створюємо наш апендер. Тег <File>показує, що він буде файловим. name="MyFileAppender"- Ім'я нашого апендера. fileName="C:\Users\Username\Desktop\testlog.txt"— шлях до лог-файлу, куди записуватимуться всі дані. append="true"— чи потрібно дозаписувати дані до кінця файлу. У нашому випадку так буде. Якщо встановити значення false , при кожному новому запуску програми старий вміст лога буде видалено. <PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>- Це налаштування форматування. Тут ми за допомогою регулярних виразів можемо налаштовувати формат тексту у нашому лозі.
<Loggers>
       <Root level="INFO">
           <AppenderRef ref="MyFileAppender"/>
       </Root>
</Loggers>
Тут ми вказуємо рівень логування (root level). У нас встановлено рівень INFO: тобто всі повідомлення рівнів вище INFO (за таблицею, яку ми розглядали вище) в лог не потраплять. У нас у програмі буде 3 повідомлення: одне INFO, одне WARN та одне ERROR. З поточною конфігурацією всі три повідомлення будуть записані в лог. Якщо ти зміниш значення root level на ERROR, у лог потрапить лише останнє повідомлення з LOGGER.error(). Крім того, сюди ж міститься посилання на апендер. Щоб створити таке посилання, потрібно <Root>створити тег усередині тега <ApprenderRef>і додати йому параметр ref=”ім'я твоего аппендера”. Ім'я апендера ми створабо ось тут, якщо ти забув: <File name="MyFileAppender" А ось і код нашої програми!
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyTestClass {

   public static final Logger LOGGER = LoggerFactory.getLogger(MyTestClass.class);

   public static void main(String[] args) {

       LOGGER.info("Начало работы программы!!!");

       try {
           LOGGER.warn("Внимание! Программа пытается разделить одно число на другое");
           System.out.println(12/0);
       } catch (ArithmeticException x) {

           LOGGER.error("Ошибка! Произошло деление на ноль!");
       }
   }
}
Він, звичайно, трохи кривуватий (перехоплення RuntimeException - ідея так собі), але для наших цілей відмінно підійде :) Давай запустимо наш метод main()4 рази поспіль і подивимося на наш файл testlog.txt. Створювати його наперед не потрібно: бібліотека зробить це автоматично. Все запрацювало! :) Тепер у тебе є налаштований логер. Ти можеш погратися з якимись написаними тобою раніше програмами, додавши виклики логера у всі методи, і подивитися на журнал, що вийшов:) В якості додаткового читання дуже рекомендую тобі ось цю статтю. Там тема логування розглянута поглиблено і за один раз прочитати її буде непросто. Але в ній міститься багато корисної додаткової інформації. Наприклад, ти навчишся конфігурувати логер так, щоб він створював новий текстовий файл, якщо наш файл testlog.txt досяг певного розміру:) А наше заняття на цьому завершено! Ти сьогодні познайомився з дуже важливою темою, і ці знання знадобляться тобі в подальшій роботі. До нових зустрічей! :)
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ