13. Логи, моніторинг і дебаггінг¶
Довідник бонус-уроку «Linux для розробника»
Розділ перевірено: кожну команду виконано в контейнерах Ubuntu, Debian і python:3.12-slim. Урок з вправами — Бонус. Linux.
Навіщо це потрібно¶
Сайт впав. Що робити? Без логів ти сліпий — не знаєш, де сталася помилка, о котрій годині, і чому. З логами — у тебе є відповіді.
Вміння читати логи і систематично шукати проблему — це один з найважливіших навичок у роботі з сервером.
Просте пояснення¶
Логи — це журнал подій. Кожен раз, коли хтось відкрив сторінку, сталася помилка або зупинився сервіс — це записується в лог. Потім ти можеш прочитати цей журнал і зрозуміти, що відбулося.
Де шукати логи¶
| Логи | Де знаходяться |
|---|---|
| Системні логи | journalctl або /var/log/syslog |
| Nginx access лог | /var/log/nginx/access.log |
| Nginx error лог | /var/log/nginx/error.log |
| Твій Django-сервіс | journalctl -u myapp або /var/log/myapp/ |
| PostgreSQL | /var/log/postgresql/ |
| Django debug | Термінал де запущено Gunicorn |
Команди для читання логів¶
tail — останні рядки¶
tail /var/log/nginx/error.log # останні 10 рядків
tail -n 50 /var/log/nginx/error.log # останні 50 рядків
tail -f /var/log/nginx/access.log # стежити в реальному часі
-f (follow) — найкорисніша опція. Термінал оновлюється і показує нові рядки в міру їх появи. Відкрий другий термінал і роби запити до сайту — бачиш їх в логах в реальному часі.
journalctl — логи systemd¶
journalctl -u myapp # всі логи сервісу
journalctl -u myapp -f # стежити в реальному часі
journalctl -u myapp -n 100 # останні 100 рядків
journalctl -u myapp --since today # за сьогодні
journalctl -u myapp --since "1 hour ago"
journalctl -u nginx -p err # тільки помилки
grep — фільтрація логів¶
grep "ERROR" /var/log/nginx/error.log
grep "500" /var/log/nginx/access.log
grep -i "traceback" /var/log/myapp/gunicorn.log
tail -f /var/log/nginx/access.log | grep "POST"
Читання логів¶
Nginx access log¶
192.168.1.1 - - [10/Jun/2026:12:34:56 +0000] "GET /api/notes/ HTTP/1.1" 200 1234 "-" "Mozilla/5.0..."
| Частина | Що означає |
|---|---|
192.168.1.1 |
IP клієнта |
10/Jun/2026:12:34:56 |
Дата і час |
GET /api/notes/ |
HTTP метод і URL |
200 |
HTTP статус-код |
1234 |
Розмір відповіді в байтах |
HTTP статус-коди¶
| Код | Що означає |
|---|---|
200 |
OK |
301/302 |
Redirect |
400 |
Bad Request (помилка клієнта) |
403 |
Forbidden (немає прав) |
404 |
Not Found |
500 |
Internal Server Error (помилка Django) |
502 |
Bad Gateway (Gunicorn недоступний) |
503 |
Service Unavailable |
Stack trace Django¶
Internal Server Error: /api/notes/
Traceback (most recent call last):
File "/var/www/myapp/.venv/lib/.../django/db/backends/base/base.py", line 279, in ensure_connection
self.connect()
File "..."
...
django.db.utils.OperationalError: connection to server at "localhost" (127.0.0.1), port 5432 failed: Connection refused
Is the server running on that host and accepting TCP/IP connections?
Читай знизу вгору: остання помилка — OperationalError: connection to server ... failed: Connection refused. Django не може підключитися до PostgreSQL.
З DEBUG=False і без власного LOGGING Django цей traceback у журнал не пише: за замовчуванням помилки запитів ідуть лише в обробник mail_admins (лист на адреси з ADMINS). Тому на production потрібне налаштування з розділу «Логування в Django» нижче.
Checklist: "сайт не відкривається"¶
flowchart TD
classDef step fill:#eceff1,stroke:#546e7a,stroke-width:1px,color:#1a1a1a;
classDef decision fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#1a1a1a;
classDef success fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1a1a1a;
classDef error fill:#ffebee,stroke:#c62828,stroke-width:3px,color:#1a1a1a;
classDef warning fill:#fff8e1,stroke:#e65100,stroke-width:2px,color:#1a1a1a;
Problem["Сайт не відкривається"] --> SSH{Сервер доступний<br>по SSH?}
SSH -->|ні| FixSSH["Проблема з сервером або мережею<br>зверніться до хостингу"]
SSH -->|так| Nginx{Nginx запущений?}
Nginx -->|ні| StartNginx["sudo systemctl start nginx"]
Nginx -->|так| Gunicorn{Gunicorn запущений?}
Gunicorn -->|ні| StartGunicorn["sudo systemctl start myapp"]
Gunicorn -->|так| Logs["Дивись логи:<br>journalctl -u myapp -n 50"]
Logs --> DB{Помилка БД?}
DB -->|так| FixDB["Перевір DATABASE_URL<br>і статус PostgreSQL"]
DB -->|ні| Static{404 для статики?}
Static -->|так| FixStatic["collectstatic<br>+ перевір Nginx alias"]
Static -->|ні| Config["Перевір ALLOWED_HOSTS і .env"]
class Problem error
class SSH,Nginx,Gunicorn,DB,Static decision
class Logs warning
class FixSSH,StartNginx,StartGunicorn,FixDB,FixStatic,Config success
Повний checklist у тексті¶
## Якщо сайт не відкривається
- [ ] Сервер доступний по SSH?
- [ ] Nginx запущений? → `systemctl status nginx`
- [ ] Nginx конфіг валідний? → `nginx -t`
- [ ] Gunicorn/Django сервіс запущений? → `systemctl status myapp`
- [ ] Є помилки в логах Nginx? → `journalctl -u nginx -n 50`
- [ ] Є помилки в логах Django? → `journalctl -u myapp -n 50`
- [ ] PostgreSQL запущений? → `systemctl status postgresql`
- [ ] Міграції виконані? → `python manage.py migrate`
- [ ] collectstatic виконаний?
- [ ] .env налаштований? (`DEBUG=False`, `ALLOWED_HOSTS`, `DATABASE_URL`)
- [ ] Порти відкриті? → `ss -tulpn | grep -E ':80|:443|:8000'`
- [ ] Firewall не блокує? → `sudo ufw status`
Логування в Django¶
# settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'ERROR',
'class': 'logging.FileHandler',
'filename': '/var/log/myapp/django.log',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'ERROR',
'propagate': True,
},
},
}
Директорія /var/log/myapp/ має існувати, і користувач сервісу має право в неї писати — інакше Django не стартує: ValueError: Unable to configure handler 'file'.
# У своєму коді
import logging
logger = logging.getLogger(__name__)
def create_note(request):
logger.info(f"User {request.user} creates a note")
try:
... # створення нотатки
except Exception as e:
logger.error(f"Failed to create note: {e}", exc_info=True)
Типові помилки початківців¶
Помилка 1: Шукати помилку "навмання" без читання логів
Завжди починай з логів. 90% проблем видно одразу.
Помилка 2: Читати логи зверху
Останні події — внизу. Читай
tail -n 50, а не весь файл.
Помилка 3: journalctl нічого не показує
Перевір ім'я сервісу:
systemctl list-units --type=service | grep myapp
Практичне завдання¶
Завдання 1¶
Поясни, що з'явилося в логах після запиту.Завдання 2¶
Спеціально зламай DATABASE_URL в .env і запусти Django. Знайди помилку в логах.
Завдання 3¶
Напиши команду, яка покаже всі рядки з кодом 500 в Nginx access log за останній день.
Самоперевірка¶
- [ ] Я знаю де знаходяться логи Nginx і systemd
- [ ] Я вмію використовувати
tail -fдля стеження за логами в реальному часі - [ ] Я можу прочитати stack trace і знайти причину помилки
- [ ] Я розумію значення HTTP статус-кодів (200, 404, 500, 502)
- [ ] Я знаю системний checklist для діагностики "сайт не відкривається"
Короткий підсумок¶
Логи — це перше місце, куди треба дивитися при проблемах. tail -f для real-time, journalctl -u для systemd-сервісів, grep для фільтрації. Систематичний checklist допомагає швидко знайти проблему. Наступний крок — Docker.