Ось готовий урок, створений спеціально за твоїм майстер-промптом.
🎓 Урок: Налаштування кастомних логів
(Стиль: CS50 / David Malan)
1. 🔥 Вступ: Чому print() — це шлях в нікуди?
Уявіть ситуацію. Ви написали інтернет-магазин. Все працює, ви запускаєте його в п'ятницю ввечері ("реліз у п'ятницю" — це вже початок трилеру, але про це згодом). Ви лягаєте спати.
У суботу вранці ви прокидаєтеся, а телефон розривається від дзвінків: "Сайт лежить! Клієнти не можуть оплатити!". Ви відкриваєте термінал, а там… пустота. Або, ще гірше, тисячі рядків тексту, який ви виводили через print("тут помилка"), print("тест 1").
Риторичне питання: Як ви дізнаєтеся, що саме зламалося о 3:14 ночі, якщо у вас немає запису подій? Як ви зрозумієте, це помилка в базі даних, чи у платіжній системі, якщо всі повідомлення виглядають однаково?
Ось тут на сцену виходить Логування.
Думайте про логи як про "Чорну скриньку" літака. Коли літак летить нормально — ми її не чіпаємо. Але якщо стається аварія, записи чорної скриньки — це єдине, що дозволить нам відтворити події посекундно і зрозуміти причину.
Сьогодні ми навчимося створювати власні "чорні скриньки", які писатимуть те, що нам треба, туди, куди нам треба, і в тому форматі, який ми зможемо прочитати.
2. 🧠 Теоретична база: Анатомія логування
Давайте заглянемо "під капот". Більшість мов програмування (Python, Java, JS) мають схожу архітектуру логування. Щоб налаштувати кастомний лог, не достатньо просто викликати функцію. Треба розуміти три ключові компоненти.
Уявіть собі роботу великої редакції новин:
- Logger (Логер / Журналіст): Це той, хто створює повідомлення. Ви кажете йому: "Запиши цю подію". Це точка входу.
- Handler (Хендлер / Кур'єр): Це механізм доставки. Куди ця новина піде?
- На екран редактору (Console)?
- В архів (File)?
- Терміновою блискавкою на пошту (Email)?
- Інтуїтивно: Один логер може мати кілька хендлерів (писати і в файл, і на екран одночасно).
- Formatter (Форматер / Верстальник): Як це повідомлення виглядає?
- Просто текст: "Помилка бази даних".
- Детальний звіт:
[2023-10-27 14:00:05] [ERROR] [user_id: 42] -> Помилка бази даних.
- Level (Рівень важливості): Фільтр.
DEBUG— для розробника (багато сміття).INFO— загальний хід подій.WARNING— щось дивне, але працюємо.ERROR— щось зламалось.CRITICAL— все пропало, будіть адміна.
Що треба запам'ятати залізобетонно:
Логування — це потік. Ви створюєте подію (LogRecord), вона проходить через фільтри (Level), форматується (Formatter) і відправляється у пункт призначення (Handler).
3. 🧪 Приклади: Від простого до професійного
Будемо використовувати Python як приклад (бо це стандарт індустрії для бекенду), але логіка універсальна.
Приклад 1: Базовий (Те, що роблять усі спочатку)
import logging
logging.warning("Обережно, це попередження!")
logging.info("Це просто інформація.")
Питання до студента: Як ви думаєте, що виведе цей код на екран? Обидва рядки чи тільки один?
... (пауза для роздумів) ...
Відповідь: Виведеться тільки WARNING. Чому? Бо за замовчуванням "поріг чутливості" стоїть на рівні WARNING. Все, що нижче (INFO, DEBUG), ігнорується. Це як фільтр новин — дрібниці не пропускаємо.
Приклад 2: Кастомний Логер (Реальний проект)
Давайте зробимо систему, яка пише логи у файл і додає час.
import logging
# 1. Створюємо Логер (Журналіста)
logger = logging.getLogger("MySuperApp")
logger.setLevel(logging.DEBUG) # Встановлюємо поріг чутливості на мінімум
# 2. Створюємо Хендлер (Архів у файл)
file_handler = logging.FileHandler("app_log.txt")
# 3. Створюємо Форматер (Верстка)
# Формат: Час - Ім'я логера - Рівень - Повідомлення
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# 4. Збираємо конструктор LEGO
file_handler.setFormatter(formatter) # Даємо хендлеру інструкцію по форматуванню
logger.addHandler(file_handler) # Прикріплюємо хендлер до логера
# Тестуємо
logger.info("Користувач зайшов на сайт")
logger.error("Не вдалося підключитися до БД")
Що ми отримаємо у файлі app_log.txt?
2023-10-27 10:00:01 - MySuperApp - INFO - Користувач зайшов на сайт
2023-10-27 10:00:02 - MySuperApp - ERROR - Не вдалося підключитися до БД
Ми бачимо КОЛИ це сталося і ХТО (MySuperApp) про це повідомив. Це вже можна читати.
4. 🛠 Практична частина
Час забруднити руки кодом. Виконуйте завдання по черзі.
Завдання 1: Повторення — мати навчання
Скопіюйте Приклад 2, запустіть його і переконайтеся, що файл app_log.txt створився. Відкрийте файл і прочитайте його вміст.
Завдання 2: Зміна умов
Додайте до Прикладу 2 ще один хендлер — StreamHandler (він виводить дані в консоль, як print).
Мета: Зробити так, щоб логи писалися і у файл, і на екран одночасно.
Завдання 3: Фільтрація
Налаштуйте хендлери так:
* У консоль виводяться всі повідомлення (DEBUG і вище).
* У файл записуються тільки помилки (ERROR і CRITICAL).
* Підказка: Метод .setLevel() є і у самого логера, і у окремих хендлерів.
Завдання 4: Міні-кейс "Банківська транзакція"
Напишіть функцію process_payment(amount), яка приймає суму.
* Якщо сума > 0, логуємо INFO: "Оплата пройшла успішно".
* Якщо сума <= 0, логуємо ERROR: "Невалідна сума транзакції!".
* Налаштуйте формат логу так, щоб він виглядав як JSON (просто імітація): {"level": "INFO", "msg": "..."}.
Завдання 5: А що, якщо…
Спробуйте створити FileHandler для файлу у папці, якої не існує (наприклад, logs/app.log, якщо папки logs немає). Що станеться? Як досвідчений розробник має це обробити?
5. 💡 Мислення як у розробника
Ви тепер вмієте налаштовувати логи. Але знати синтаксис — це половина справи. Важливо знати, як не треба робити.
🚫 Типова помилка новачка:
Логувати персональні дані.
logger.info(f"User login with password: {password}")
😱 Ніколи так не робіть! Логи часто зберігаються в простих текстових файлах. Якщо хакер отримає доступ до логів — він отримає паролі всіх користувачів.
🚫 Ще одна помилка:
Використання print у великому проекті.
Коли ви запускаєте програму на сервері (через Docker, systemd), print може губитися, буферизуватися або просто змішуватися з іншим сміттям. Тільки logger.
🧠 Як думає Senior: "Я пишу логи не для себе зараз, а для себе о 3-й ночі через півроку, коли все зламається. Чи зрозумію я з цього повідомлення, що сталося, без відкривання коду?"
- Порада: Краще використовувати структурне логування (JSON формат). Машинам (системам типу ELK, Datadog) легше читати
{"event": "login", "user": 42}, ніж розбирати текст регулярними виразами.
6. 🧩 Підсумок
Отже, що ми маємо?
- Ми відмовилися від
print()на користь Logger. - Ми зрозуміли, що логи можна направляти в різні місця через Handlers.
- Ми навчилися надавати логам читабельний вигляд через Formatters.
Тепер ви можете контролювати, що відбувається у вашій програмі, навіть коли вас немає поруч. Ви перетворили "чорну діру" помилок на прозору систему подій.
🤔 Тизер наступного уроку: Логи — це добре. Але що робити, якщо помилка критична, і ми хочемо дізнатися про неї миттєво, а не читати файл потім? На наступному занятті ми підключимо логування до Telegram-бота, щоб ваш код сам писав вам в особисті повідомлення, коли йому "боляче".
А поки — Happy Logging! 🚀