Настройка логирования в production-окружении определяет, насколько быстро и эффективно вы сможете находить и устранять инциденты. В этой статье собраны проверенные на практике конфигурации для Django и Flask, которые обеспечивают разделение логов доступа, ошибок и бизнес-событий, автоматическую маскировку конфиденциальных данных и стабильную работу без переполнения диска. Вы получите готовые к внедрению блоки кода для settings.py, конфигурации Gunicorn/uWSGI и файлы для logrotate.
Главная задача логирования в продакшене - не просто записывать события, а создать систему, которая упрощает расследование проблем, обеспечивает безопасность и работает автономно. Мы разберем архитектуру, подходящую для большинства веб-приложений, и покажем ее реализацию в двух самых популярных Python-фреймворках.
Архитектура логирования в production: почему разделение и структура критически важны
Правильная организация логов начинается с четкого разделения потоков данных. Смешение всех событий в один файл усложняет анализ, увеличивает риски утечки чувствительной информации и затрудняет настройку систем мониторинга. В production каждый тип лога решает свою задачу и требует отдельного подхода к хранению и обработке.
Три потока логов, которые нужны каждому production-приложению
Разделите логи на три независимых канала:
- Логи доступа (access.log): HTTP-запросы, статусы ответов, время выполнения, IP-адреса клиентов. Этот поток используется для анализа нагрузки, поиска медленных эндпоинтов и выявления подозрительной активности. В Django за него отвечает логгер
django.request, во Flask логи доступа обычно генерирует веб-сервер (Gunicorn, uWSGI). - Логи ошибок (error.log): исключения, 404/500 ошибки, критические сбои приложения, сообщения уровня WARNING и выше. Этот файл - первое место для поиска при расследовании инцидентов. Важно обеспечить запись полного стека вызовов (traceback).
- Бизнес-логи/логи приложения (app.log): события бизнес-процессов (например, «пользователь совершил платеж», «документ сгенерирован»), INFO-сообщения для аудита и отладки бизнес-логики. Эти логи помогают отслеживать корректность работы приложения с точки зрения пользователя.
Такое разделение позволяет, например, настроить разные политики хранения: логи доступа хранить 7 дней, ошибки - 30 дней, а бизнес-события - год для аудита.
Уровни логирования: от DEBUG до CRITICAL - что включать в продакшене
Уровни логирования в Python (DEBUG, INFO, WARNING, ERROR, CRITICAL) определяют детализацию. В production среда требует баланса между информативностью и производительностью.
- DEBUG: включайте только в тестовых и staging-окружениях для отладки. В production этот уровень создает чрезмерный объем данных и может раскрывать внутреннюю логику приложения.
- INFO: основной уровень для бизнес-событий и аудита. Используйте для записи ключевых действий пользователей и системы.
- WARNING: события, которые не нарушают работу приложения, но указывают на потенциальные проблемы (например, устаревший API-вызов, приближение к лимиту ресурсов).
- ERROR и CRITICAL: сбои, требующие немедленного внимания. Настройте алертинг на эти события через интеграцию с Sentry, Telegram или Slack.
Рекомендуемая настройка для production: для access.log - уровень INFO, для error.log - WARNING и выше, для app.log - INFO и выше. Это обеспечивает достаточную детализацию без перегрузки диска.
Структурированное логирование в формате JSON стало стандартом для интеграции с системами мониторинга, такими как ELK Stack или Grafana Loki. JSON-логи автоматически парсятся, позволяют добавлять контекстные поля (correlation_id, user_id) и упрощают создание дашбордов. Например, для анализа медленных запросов в Nginx или Apache вы можете использовать готовые команды grep и awk из нашей базы знаний: Практический анализ логов Nginx и Apache.
Настройка логирования в Django: работа с словарем LOGGING в settings.py
Django предоставляет централизованную конфигурацию логирования через словарь LOGGING в settings.py. Этот механизм дает полный контроль над логгерами, обработчиками и форматерами. Приведенный ниже конфиг готов к использованию в production и включает разделение на три файла, ротацию и JSON-форматирование.
Готовый конфиг LOGGING для production: копируйте и адаптируйте
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'json': {
'()': 'pythonjsonlogger.jsonlogger.JsonFormatter',
'format': '%(asctime)s %(levelname)s %(name)s %(message)s',
},
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
},
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse',
},
# Фильтр для маскирования данных будет добавлен ниже
},
'handlers': {
'app_file': {
'level': 'INFO',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': '/var/log/django/app.log',
'when': 'midnight',
'backupCount': 30,
'formatter': 'json',
},
'error_file': {
'level': 'WARNING',
'class': 'logging.handlers.RotatingFileHandler',
'filename': '/var/log/django/error.log',
'maxBytes': 10485760, # 10 MB
'backupCount': 10,
'formatter': 'verbose',
},
'access_file': {
'level': 'INFO',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': '/var/log/django/access.log',
'when': 'midnight',
'backupCount': 7,
'formatter': 'json',
},
'console': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
},
'loggers': {
'django.request': {
'handlers': ['access_file'],
'level': 'INFO',
'propagate': False,
},
'django': {
'handlers': ['error_file', 'console'],
'level': 'INFO',
'propagate': True,
},
'myapp': { # Замените на имя вашего приложения
'handlers': ['app_file', 'console'],
'level': 'INFO',
'propagate': False,
},
},
}
Ключевые элементы конфигурации:
disable_existing_loggers: False- сохраняет стандартные логгеры Django.TimedRotatingFileHandler- выполняет ротацию логов по времени (ежедневно в полночь). Это временное решение; в production предпочтительнее использовать системныйlogrotate.RotatingFileHandler- ротирует файл по достижению размера 10 МБ.- Форматтер
jsonиспользует библиотекуpython-json-logger(установите черезpip install python-json-logger).
Для управления всей инфраструктурой, включая логи, используйте готовые скрипты автоматизации: Автоматизация инфраструктуры для DevOps и сисадминов.
Логирование запросов и ошибок: обработка django.request и исключений
Логгер django.request автоматически записывает информацию о каждом HTTP-запросе. В production его нужно направить в отдельный файл (access_file). Чтобы избежать логирования статических файлов, добавьте промежуточное ПО или настройте веб-сервер (Nginx/Apache) для их обслуживания.
Ошибки 404 и 500 обрабатываются отдельно. Django отправляет сообщения уровня WARNING для 404 и ERROR для 500 в логгер django.security и django.server (в режиме отладки). В представленном конфиге они попадут в error.log через логгер django.
Для централизованного сбора ошибок из распределенных микросервисов изучите руководство: Централизованный сбор логов Python-микросервисов в Kubernetes.
Настройка логирования в Flask: интеграция со стандартным logging и развертыванием
Flask не имеет встроенной системы логирования уровня фреймворка, как Django, и полагается на стандартный модуль logging. Основная сложность - корректная интеграция с серверами развертывания (Gunicorn, uWSGI), которые сами генерируют логи доступа и ошибок.
Конфигурация Gunicorn и uWSGI для корректного захвата логов
При запуске Flask через Gunicorn используйте параметры командной строки или конфигурационный файл:
# Командная строка
gunicorn --workers 4 --bind 0.0.0.0:8000 \
--access-logfile /var/log/flask/access.log \
--error-logfile /var/log/flask/error.log \
--log-level info \
--capture-output \
--enable-stdio-inheritance \
app:app
# Конфигурационный файл gunicorn.conf.py
accesslog = "/var/log/flask/access.log"
errorlog = "/var/log/flask/error.log"
loglevel = "info"
capture_output = True
enable_stdio_inheritance = True
Параметры capture_output и enable_stdio_inheritance гарантируют, что логи из самого приложения Flask (написанные через app.logger) также будут направлены в error.log Gunicorn.
Для uWSGI настройка выглядит так:
[uwsgi]
module = app:app
master = true
processes = 4
logto = /var/log/flask/uwsgi.log
log-format = %(addr) - %(user) [%(ltime)] "%(method) %(uri) %(proto)" %(status) %(size) %(micros)
disable-logging = true # Отключает встроенное логирование uWSGI, если используете свой формат
logger = file:/var/log/flask/error.log
Убедитесь, что каталог /var/log/flask/ существует и процесс имеет права на запись.
Структурированные логи (JSON) в Flask: настройка и преимущества
Настройте JSON-логирование в самом приложении Flask. Создайте кастомный форматер или используйте библиотеку python-json-logger.
import logging
from pythonjsonlogger import jsonlogger
from flask import Flask
app = Flask(__name__)
# Создаем обработчик для файла приложения
log_handler = logging.handlers.TimedRotatingFileHandler(
'/var/log/flask/app.log',
when='midnight',
backupCount=30
)
# Настраиваем JSON-форматтер
formatter = jsonlogger.JsonFormatter(
'%(asctime)s %(levelname)s %(name)s %(message)s',
rename_fields={'levelname': 'severity', 'asctime': 'timestamp'}
)
log_handler.setFormatter(formatter)
# Настраиваем логгер приложения
app.logger.setLevel(logging.INFO)
app.logger.addHandler(log_handler)
# Пример логирования
@app.route('/')
def index():
app.logger.info('Homepage accessed', extra={'user_ip': request.remote_addr})
return 'Hello World'
Структурированные логи позволяют легко фильтровать события по полям, например, найти все запросы от определенного IP-адреса. Для сложного анализа с применением ИИ ознакомьтесь с актуальными методами: Анализ логов с помощью ИИ и LLM в 2026.
Защита логов: маскирование конфиденциальных данных перед записью
Логи часто становятся источником утечек: пароли, API-токены, email-адреса и персональные данные могут случайно попасть в лог-файлы через параметры запроса, тела исключений или отладочные сообщения. Маскирование должно происходить автоматически, до записи в файл.
Кастомный фильтр для маскирования паролей и API-токенов в Django
Создайте фильтр, который проверяет текст лог-сообщения и заменяет конфиденциальные данные.
import logging
import re
class SensitiveDataFilter(logging.Filter):
"""Фильтр для маскирования чувствительных данных в логах."""
def __init__(self):
super().__init__()
# Паттерны для поиска конфиденциальных данных
self.patterns = {
'password': r'("password"\s*[:=]\s*)"[^"]*"',
'api_key': r'(api[_-]?key[\s=:"]+)[^\s"\',]+',
'token': r'(access[_-]?token|refresh[_-]?token[\s=:"]+)[^\s"\',]+',
'email': r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
}
self.replacement = '[MASKED]'
def filter(self, record):
if record.msg:
record.msg = self._mask_string(record.msg)
if record.args and isinstance(record.args, dict):
for key, value in record.args.items():
if isinstance(value, str):
record.args[key] = self._mask_string(value)
return True
def _mask_string(self, text):
for name, pattern in self.patterns.items():
if name in ['email']:
# Для email заменяем локальную часть
text = re.sub(pattern, self.replacement, text)
else:
text = re.sub(pattern, f'\\1{self.replacement}', text, flags=re.IGNORECASE)
return text
# Добавьте фильтр в словарь LOGGING
LOGGING = {
# ...
'filters': {
'mask_sensitive_data': {
'()': 'path.to.SensitiveDataFilter',
},
},
'handlers': {
'app_file': {
# ...
'filters': ['mask_sensitive_data'], # Применяем фильтр к обработчику
},
},
}
Этот фильтр заменит значения в полях password, api_key, token и email-адреса на [MASKED].
Процессоры для очистки структурированных логов (JSON) в Flask
При использовании структурированного логирования данные хранятся в словаре record. Добавьте процессор, который очищает конкретные поля.
import logging
def mask_sensitive_data_processor(record):
"""Процессор для маскирования данных в структурированных логах."""
sensitive_fields = ['password', 'api_key', 'token', 'credit_card']
for field in sensitive_fields:
if field in record:
record[field] = '[MASKED]'
# Маскирование в строке сообщения
if 'message' in record and isinstance(record['message'], str):
# Применяем простую замену по ключевым словам
for field in sensitive_fields:
record['message'] = record['message'].replace(field, '[MASKED]')
return record
# При использовании structlog
import structlog
structlog.configure(
processors=[
structlog.stdlib.filter_by_level,
mask_sensitive_data_processor, # Добавляем свой процессор
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.processors.JSONRenderer()
],
)
# При использовании стандартного logging с python-json-logger
# Создайте кастомный форматтер, который вызывает процессор
class SafeJsonFormatter(jsonlogger.JsonFormatter):
def process_log_record(self, log_record):
log_record = mask_sensitive_data_processor(log_record)
return super().process_log_record(log_record)
Такой подход гарантирует, что даже если конфиденциальные данные добавлены в лог через extra-параметры, они не будут сохранены в чистом виде.
Автоматизация управления логами: ротация, архивация и очистка с logrotate
Ротация логов предотвращает заполнение диска и упрощает работу с архивными данными. Системный инструмент logrotate более надежен, чем ротация средствами Python, так как корректно обрабатывает переоткрытие файлов после ротации.
Конфигурация logrotate для Django и Flask приложений: готовые файлы
Создайте файл /etc/logrotate.d/myapp со следующим содержимым:
# Ротация логов Django/Flask приложения
/var/log/django/*.log /var/log/flask/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 0640 www-data www-data # Укажите пользователя и группу вашего процесса
sharedscripts
postrotate
# Для Django/Gunicorn/Flask: отправить сигнал для переоткрытия лог-файлов
# Если приложение запущено через systemd, используйте systemctl reload
systemctl reload myapp.service 2>/dev/null || true
# Альтернатива: отправка сигнала процессу
# pkill -HUP gunicorn 2>/dev/null || true
endscript
}
# Отдельная настройка для access.log (храним меньше)
/var/log/django/access.log /var/log/flask/access.log {
daily
rotate 7
compress
delaycompress
notifempty
create 0640 www-data www-data
sharedscripts
postrotate
systemctl reload myapp.service 2>/dev/null || true
endscript
}
Ключевые директивы:
daily- ротация каждый день.rotate 30- хранить 30 архивных копий.compress- сжимать старые логи с помощью gzip.delaycompress- откладывает сжатие до следующей ротации (удобно для отладки).create- создает новый лог-файл с указанными правами и владельцем после ротации.postrotate- скрипт, выполняемый после ротации. СигналHUPили перезагрузка сервиса заставляют приложение переоткрыть лог-файл.
Проверьте конфигурацию командой logrotate -d /etc/logrotate.d/myapp (режим dry-run).
Мониторинг и реакция: как следить за размером логов и проблемами ротации
Настройте мониторинг размера лог-файлов и успешности ротации. Простой скрипт для проверки:
#!/bin/bash
# check_logs.sh
LOG_DIR="/var/log/django"
MAX_SIZE_MB=1024 # 1 GB
for logfile in $LOG_DIR/*.log; do
size_mb=$(du -m "$logfile" | cut -f1)
if [ $size_mb -gt $MAX_SIZE_MB ]; then
echo "CRITICAL: Log file $logfile is too large: ${size_mb}MB" | \
mail -s "Log alert" admin@example.com
# Автоматическая ротация в экстренном случае
/usr/sbin/logrotate -f /etc/logrotate.d/myapp
fi
done
Добавьте этот скрипт в cron для ежедневного запуска. Для комплексного мониторинга всей инфраструктуры используйте решения, описанные в сборнике: DevOps и Linux администрирование 2026.
Если ротация не работает, проверьте:
- Права на запись в каталог логов и на создание новых файлов.
- Корректность пути к лог-файлам в конфиге
logrotate. - Работу
postrotateскрипта - приложение должно переоткрыть файлы. - Достаточное место на диске для хранения архивов.
Проверка и внедрение: поэтапный план без риска для рабочего окружения
Внедряйте новую конфигурацию логирования постепенно, чтобы минимизировать риски для production-среды.
- Тестовое окружение: Разверните конфигурацию на staging или в отдельном тестовом контуре. Убедитесь, что логи всех типов создаются, данные маскируются, а ротация работает.
- Поэтапный rollout: В production сначала настройте только логирование ошибок (
error.log) с маскированием. Проверьте, что критичные ошибки записываются, а чувствительные данные скрыты. - Добавление логов доступа и приложения: Подключите
access.logиapp.log. Проверьте нагрузку на диск и производительность приложения. - Включение logrotate: Настройте ротацию и убедитесь, что приложение корректно переоткрывает файлы после ротации (отправьте тестовый сигнал
HUP). - Мониторинг: Настройте алертинг на рост логов и сбои ротации. Интегрируйте логи в систему мониторинга (ELK, Grafana).
Критически важные моменты:
- Не меняйте пути к лог-файлам в работающем приложении без предварительной остановки записи. Это может привести к потере логов.
- Используйте версионирование конфигурационных файлов (Ansible, Terraform) для отслеживания изменений и быстрого отката.
- Протестируйте конфигурацию под нагрузкой, чтобы убедиться, что логирование не создает bottleneck.
Для автоматизации развертывания подобных конфигураций в масштабе используйте агрегатор AI-инструментов AiTunnel, который предоставляет единый API для интеграции с различными моделями ИИ, что может быть полезно для последующего анализа собранных логов.
Правильно настроенная система логирования становится фундаментом для observability приложения. Она экономит часы на расследование инцидентов, защищает от утечек данных и обеспечивает стабильную работу в production. Начните внедрение с самого критичного для вашего проекта элемента - маскирования чувствительных данных или настройки ротации.