Модуль 32

Логування та трасування повідомлень

Ось готовий урок, створений за твоїм майстер-промптом. Це класичний стиль CS50: енергійний, зрозумілий і орієнтований на практику.


🎓 CS50: Логування та Трасування (Logging & Tracing)

Привіт, друзі! 👋 Радий бачити вас знову.

Сьогодні ми поговоримо про тему, яка відрізняє студентський код від професійного софту. Це те, що рятує розробників о 3-й годині ночі, коли сервер "впав", а клієнти панікують.

Тема уроку: Логування та трасування повідомлень.


1. 🔥 Вступ: Чорна скринька вашого коду

Уявіть ситуацію. Ви написали інтернет-магазин. Все працює локально. Ви запускаєте його в інтернет. І раптом... користувач пише скаргу: "Я натиснув 'Купити', гроші списало, а товару немає!".

Ви відкриваєте код. Дивитесь. Все виглядає правильно. Але що сталося саме в ту секунду? * Чи відповів банк? * Чи записалася покупка в базу даних? * Чи не відпав інтернет на сервері?

Якщо у вас немає логів (logs), ви схожі на детектива, який прийшов на місце злочину через рік, коли все вже прибрали. Жодних доказів. Тільки здогадки.

Риторичне питання: Ви б сіли в літак, у якого немає "чорної скриньки" (бортового самописця)? Думаю, ні. То чому ж ми часто запускаємо код без "чорної скриньки"?

Логування — це спосіб вашої програми вести щоденник. Трасування — це "червона нитка", яка зв'язує події між різними системами, щоб ви могли відслідкувати шлях одного конкретного запиту.

Без цього ви сліпі. З цим — ви всевидючі. Поїхали розбиратися! 🚀


2. 🧠 Теоретична база (Що під капотом?)

Давайте одразу домовимось: print("Error here") — це не логування. Це "милиці".

Що таке Лог (Log)?

Це запис про подію, що сталася в певний момент часу. Хороший лог має структуру: КОЛИ (timestamp) + ЩО (message) + ДЕ (context) + НАСКІЛЬКИ ВАЖЛИВО (level).

Рівні логування (Levels) — це треба запам'ятати!

Уявіть, що ви керуєте кораблем. Вам доповідають різні речі. Як їх фільтрувати?

  1. DEBUG 🐛: "Двигун робить оберт, температура 40, тиск 50...". Це потрібно тільки механіку (розробнику), коли він лагодить систему. В звичайному житті це "шум".
  2. INFO ℹ️: "Корабель вийшов з порту". Корисна інформація про нормальний хід подій.
  3. WARNING ⚠️: "Паливо закінчується, лишилось 20%". Нічого ще не зламалось, але зверни увагу!
  4. ERROR ❌: "Двигун №1 відмовив!". Це проблема. Потрібне втручання, але корабель ще пливе.
  5. FATAL / CRITICAL 💀: "Ми тонемо!". Система не може продовжувати роботу.

Що таке Трасування (Tracing)?

Якщо у вас моноліт (одна велика програма), логів достатньо. Але сучасний світ — це мікросервіси. Один клік "Купити" може викликати 5 різних програм: 1. Сервіс кошика 2. Сервіс складу 3. Сервіс оплати 4. Сервіс доставки

Якщо "Сервіс оплати" видав помилку, як зрозуміти, хто його викликав? Тут вмикається Trace ID (або Correlation ID). Це унікальний номер (наприклад, abc-123), який генерується на старті й передається "естафетою" всім сервісам.

Інтуїтивно: Логи — це окремі кадри фільму. Трейс — це сюжетна лінія, яка об’єднує ці кадри в історію.


3. 🧪 Приклади (Python edition)

Ми будемо використовувати Python, бо він читається як англійська, але логіка однакова і для Java, і для C++, і для JavaScript.

Приклад 1: Як роблять новачки (Погано ❌)

def process_payment(amount):
    print("Starting payment...")  # Куди це виведеться? В консоль? А якщо сервер працює у фоні?
    if amount < 0:
        print("Error: amount is negative") # І що далі? Хто про це дізнається?
        return False
    # ... логіка оплати
    print("Payment success")
    return True

Що тут не так? * Немає часу (коли це сталось?). * Немає рівнів важливості (все виглядає однаково). * Цей текст важко фільтрувати автоматично.

Приклад 2: Як роблять профі (Добре ✅)

Використовуємо стандартну бібліотеку logging.

import logging

# Налаштування "самописця"
logging.basicConfig(
    level=logging.INFO, # Показувати все, що важливіше або рівне INFO
    format='%(asctime)s - %(levelname)s - %(message)s' # Формат: Час - Рівень - Повідомлення
)

def process_payment(user_id, amount):
    logging.info(f"User {user_id} attempts to pay ${amount}")

    if amount <= 0:
        logging.error(f"Payment failed for User {user_id}: Invalid amount ${amount}")
        return False

    try:
        # Уявна логіка оплати
        logging.debug("Connecting to bank API...") # Це не виведеться, бо рівень INFO
        # ... success
        logging.info(f"Payment successful for User {user_id}")
        return True
    except Exception as e:
        logging.critical(f"Bank API is down! System halted. Error: {e}")
        raise e

# Запускаємо
process_payment("user_123", 50)
process_payment("user_456", -10)

Результат у консолі:

2023-10-27 10:00:01 - INFO - User user_123 attempts to pay $50
2023-10-27 10:00:02 - INFO - Payment successful for User user_123
2023-10-27 10:00:03 - INFO - User user_456 attempts to pay $-10
2023-10-27 10:00:03 - ERROR - Payment failed for User user_456: Invalid amount $-10

Бачите різницю? Ми бачимо час, ми бачимо, що саме сталось, і ми можемо легко знайти всі ERROR у файлі.

Приклад 3: Магія Trace ID 🕵️‍♂️

Уявіть, що функція process_payment викликає іншу функцію send_email. Як зв'язати їх логи?

import uuid # Генератор унікальних ID

def request_handler():
    # 1. Прийшов запит - генеруємо паспорт запиту (Trace ID)
    trace_id = str(uuid.uuid4())[:8] 
    logging.info(f"[{trace_id}] New request received")

    process_payment(trace_id, 100)
    send_email(trace_id)

def process_payment(trace_id, amount):
    # Ми всюди тягнемо цей trace_id
    logging.info(f"[{trace_id}] Processing payment...")

def send_email(trace_id):
    logging.info(f"[{trace_id}] Sending receipt...")

# Виклик
request_handler()

Результат:

INFO - [a1b2c3d4] New request received
INFO - [a1b2c3d4] Processing payment...
INFO - [a1b2c3d4] Sending receipt...

Тепер, навіть якщо тисячі користувачів клікають одночасно, ви можете відфільтрувати логи за [a1b2c3d4] і побачити повну історію саме ЦЬОГО користувача.


4. 🛠 Практична частина

Час кодити! Відкрийте ваш IDE або онлайн-редактор.

Завдання 1: "Ремонт"

У вас є код із print(). Замініть їх на logging. * Успішні дії — це INFO. * Помилки валідації — це WARNING. * Критичні збої — це ERROR.

Завдання 2: "Налаштування фільтру"

Змініть налаштування basicConfig так, щоб виводилися повідомлення рівня DEBUG. Додайте в код повідомлення logging.debug("Variable x = 5"). Переконайтеся, що ви його бачите. Потім змініть рівень назад на INFO і перевірте, що debug зник.

Завдання 3: "Симуляція розслідування"

Напишіть функцію divide(a, b). * Додайте лог перед діленням. * Додайте обробку try-except для ділення на нуль. * У блоці except запишіть лог рівня ERROR з текстом "Спроба ділення на нуль!".

Завдання 4: Міні-кейс "Trace ID"

Створіть дві функції: login(username) та upload_photo(username). Зробіть так, щоб обидві функції приймали ще й request_id і виводили його в кожному лозі. Просимулюйте роботу: згенеруйте ID, викличте логін, потім завантаження фото. Подивіться на красу консолі.

Завдання 5: Питання "А що, якщо..."

Що станеться, якщо у вас закінчиться місце на диску, а програма пише гігабайти логів щодня? Як це вирішити? (Гуглити за запитом: log rotation).


5. 💡 Мислення як у розробника

Як відрізнити новачка від сеньйора за логами?

  1. Новачок: Логує "I am here", "Test 1", "Test 2".
    • Чому це погано: Це нічого не говорить про стан системи.
  2. Новачок: Логує паролі або кредитні картки (logging.info(f"User pass: {password}")).

    • Чому це погано: ЦЕ КАТАСТРОФА. Логи читають адміни, вони зберігаються у файлах. Ви щойно злили дані користувача. Ніколи так не робіть. 🚫
  3. Досвідчений розробник: Думає: "Якщо мене розбудять о 4-й ранку через помилку, чи достатньо мені буде цього повідомлення, щоб зрозуміти причину, не відкриваючи код?"

Порада: Краще мати надлишкові логи рівня DEBUG (які можна вимкнути), ніж не мати логів взагалі, коли все зламається.


6. 🧩 Підсумок

Отже, що ми сьогодні дізнались?

  1. Логування — це не просто текст у консолі, це інструмент для діагностики та "машина часу".
  2. Є рівні (DEBUG, INFO, WARN, ERROR), щоб фільтрувати важливе від неважливого.
  3. Trace ID дозволяє склеїти історію запиту в хаосі мікросервісів.

Тепер ви вмієте: не просто писати код, який працює, а код, який розповідає, як він працює.

👀 Тизер наступного уроку: Ми навчилися писати логи у файли. Але що, якщо серверів 100? Читати 100 файлів? Ні! На наступному уроці ми поговоримо про ELK Stack (Elasticsearch, Logstash, Kibana) — систему, яка збирає логи звідусіль і малює красиві графіки.

А поки — до практики! Удачі! 💻