Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Конфигурация логирования
configuration

Конфигурация логирования

dictConfig, fileConfig, конфигурация через YAML/JSON

Конфигурация логирования в Python

Production-приложения требуют гибкой конфигурации логирования. basicConfig() не хватает — нужен dictConfig(). Правильная конфигурация — это разница между часами отладки и минутами.

#1. Проблемы basicConfig

Проблема: basicConfig() не подходит для production.

Ограничения basicConfig:

  • Настраивает только root logger
  • Нельзя настроить несколько logger'ов с разными настройками
  • Нельзя настроить кастомные фильтры
  • Работает только один раз (повторные вызовы игнорируются)
  • Нельзя указать разные handlers для разных logger'ов
# ❌ basicConfig не подходит для production logging.basicConfig( level=logging.INFO, format='%(levelname)s: %(message)s' ) # Проблемы: # 1. Все логи идут в root logger # 2. Нельзя настроить разные уровни для разных модулей # 3. Нельзя добавить кастомный filter # 4. Нельзя легко переключиться между dev/prod конфигурациями

Решение: dictConfig() для полной конфигурации.

#2. dictConfig — рекомендуемый способ

#2.1. Базовая структура dictConfig

import logging import logging.config LOGGING_CONFIG = { 'version': 1, # Обязательно! Только значение 1 поддерживается 'disable_existing_loggers': False, # Не отключать существующие logger'ы # Форматтеры 'formatters': { 'simple': { 'format': '%(levelname)s: %(message)s' }, 'verbose': { 'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s' } }, # Handlers 'handlers': { 'console': { 'class': 'logging.StreamHandler', 'level': 'INFO', 'formatter': 'simple', 'stream': 'ext://sys.stdout' }, 'file': { 'class': 'logging.FileHandler', 'level': 'DEBUG', 'formatter': 'verbose', 'filename': 'app.log' } }, # Logger'ы 'loggers': { 'app': { 'level': 'DEBUG', 'handlers': ['console', 'file'], 'propagate': False }, 'app.api': { 'level': 'INFO', 'handlers': ['console'], 'propagate': False } }, # Root logger 'root': { 'level': 'WARNING', 'handlers': ['console'] } } # Применение конфигурации logging.config.dictConfig(LOGGING_CONFIG)

#2.2. Ключевые секции dictConfig

СекцияНазначениеОбязательность
versionВерсия схемы (всегда 1)✅ Обязательно
disable_existing_loggersНе отключать logger'ы из других модулей❌ Рекомендуется
formattersФорматтеры для handlers❌ Опционально
handlersОбработчики: куда отправлять логи❌ Опционально
loggersНастройки logger'ов❌ Опционально
rootНастройки root logger❌ Опционально
filtersКастомные фильтры❌ Опционально

#2.3. Специальные префиксы

ПрефиксНазначениеПример
ext://Импорт объекта из Pythonext://sys.stdout
cfg://Значение из другой секции configcfg://handlers.file.filename
()Кастомный класс'()': 'myapp.CustomHandler'

Примеры использования префиксов:

handlers: console: class: logging.StreamHandler # ext:// — импортируем sys.stdout вместо хардкодинга stream: ext://sys.stdout file: class: logging.FileHandler # cfg:// — берём значение из другой секции config filename: cfg://handlers.file.base_path base_path: /var/log/app custom: # () — создаём экземпляр кастомного класса '()': myapp.logging.CustomHandler level: DEBUG

#2.4. disable_existing_loggers

Проблема: После dictConfig logger'ы из сторонних библиотек перестают работать.

Решение: disable_existing_loggers: False

# ❌ Плохо: logger'ы библиотек отключаются LOGGING_CONFIG = { 'version': 1, # disable_existing_loggers по умолчанию True # Все logger'ы, созданные до dictConfig, отключаются } # ✅ Хорошо: logger'ы библиотек сохраняются LOGGING_CONFIG = { 'version': 1, 'disable_existing_loggers': False, # Важно! # ... }

Когда использовать True:

  • Тесты, где нужна изоляция
  • Полная перезагрузка конфигурации

#3. Конфигурация из файла

#3.1. JSON конфигурация

Проблема: Нужно хранить конфигурацию в JSON файле.

Решение: Загрузить JSON и передать в dictConfig.

// logging.json { "version": 1, "disable_existing_loggers": false, "formatters": { "simple": { "format": "%(levelname)s: %(message)s" }, "verbose": { "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s" } }, "handlers": { "console": { "class": "logging.StreamHandler", "level": "INFO", "formatter": "simple", "stream": "ext://sys.stdout" }, "file": { "class": "logging.handlers.RotatingFileHandler", "level": "DEBUG", "formatter": "verbose", "filename": "app.log", "maxBytes": 10485760, "backupCount": 5 } }, "loggers": { "app": { "level": "DEBUG", "handlers": ["console", "file"], "propagate": false } }, "root": { "level": "WARNING", "handlers": ["console"] } }
# Загрузка конфигурации import json import logging.config with open('logging.json', 'r') as f: config = json.load(f) logging.config.dictConfig(config) logger = logging.getLogger(__name__) logger.info("Application started")

#3.2. YAML конфигурация

Проблема: JSON неудобен для комментариев и сложной структуры.

Решение: YAML более читаем и поддерживает комментарии.

# logging.yaml version: 1 disable_existing_loggers: false formatters: simple: format: '%(levelname)s: %(message)s' verbose: format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s' datefmt: '%Y-%m-%d %H:%M:%S' handlers: console: class: logging.StreamHandler level: INFO formatter: simple stream: ext://sys.stdout file: class: logging.handlers.RotatingFileHandler level: DEBUG formatter: verbose filename: app.log maxBytes: 10485760 # 10 MB backupCount: 5 mail: class: logging.handlers.SMTPHandler level: ERROR formatter: verbose mailhost: smtp.example.com fromaddr: app@example.com toaddrs: - admin@example.com subject: Application Error credentials: - username - password secure: () loggers: app: level: DEBUG handlers: - console - file propagate: false app.api: level: INFO handlers: - console propagate: false app.db: level: WARNING # Меньше шума от DB логов handlers: - file propagate: false root: level: WARNING handlers: - console
# Загрузка YAML конфигурации import yaml import logging.config with open('logging.yaml', 'r') as f: config = yaml.safe_load(f) logging.config.dictConfig(config)

#3.3. INI конфигурация (fileConfig)

Проблема: Нужна простая конфигурация в INI формате.

Решение: fileConfig() для INI файлов.

# logging.ini [loggers] keys=root,app,app_api [handlers] keys=consoleHandler,fileHandler,emailHandler [formatters] keys=simpleFormatter,verboseFormatter [logger_root] level=WARNING handlers=consoleHandler,emailHandler [logger_app] level=DEBUG handlers=consoleHandler,fileHandler qualname=app propagate=0 [logger_app_api] level=INFO handlers=consoleHandler qualname=app.api propagate=0 [handler_consoleHandler] class=StreamHandler level=INFO formatter=simpleFormatter args=(sys.stdout,) [handler_fileHandler] class=handlers.RotatingFileHandler level=DEBUG formatter=verboseFormatter args=('app.log', 'a', 10485760, 5) [handler_emailHandler] class=handlers.SMTPHandler level=ERROR formatter=verboseFormatter args=(('smtp.example.com', 587), 'app@example.com', ['admin@example.com'], 'Application Error') [formatter_simpleFormatter] format=%(levelname)s: %(message)s [formatter_verboseFormatter] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s datefmt=%Y-%m-%d %H:%M:%S
# Загрузка INI конфигурации import logging.config logging.config.fileConfig('logging.ini') logger = logging.getLogger('app')

Ограничения fileConfig:

  • Не поддерживает кастомные handlers/filters через '()'
  • Не поддерживает disable_existing_loggers
  • Меньше гибкости чем dictConfig
  • INI формат менее читаем чем YAML

#4. Кастомные компоненты

#4.1. Кастомный handler

# myapp/logging.py import logging from datetime import datetime class CustomHandler(logging.Handler): def __init__(self, log_dir='/var/log'): super().__init__() self.log_dir = log_dir def emit(self, record): # Кастомная логика filename = f"{self.log_dir}/{datetime.now().strftime('%Y%m%d')}.log" with open(filename, 'a') as f: f.write(self.format(record) + '\n')
# logging.yaml handlers: custom: '()': myapp.logging.CustomHandler level: DEBUG formatter: verbose log_dir: /var/log/myapp

#4.2. Кастомный formatter

# myapp/logging.py import logging import json class JsonFormatter(logging.Formatter): def format(self, record): log_data = { 'timestamp': self.formatTime(record), 'level': record.levelname, 'logger': record.name, 'message': record.getMessage(), } # Добавляем extra поля for key, value in record.__dict__.items(): if key not in ('name', 'msg', 'args', 'created', 'filename', 'funcName', 'levelname', 'levelno', 'lineno', 'module', 'msecs', 'pathname', 'process', 'processName', 'relativeCreated', 'stack_info', 'exc_info', 'exc_text', 'thread', 'threadName', 'message', 'asctime'): log_data[key] = value return json.dumps(log_data)
# logging.yaml formatters: json: '()': myapp.logging.JsonFormatter

#4.3. Кастомный filter

# myapp/logging.py import logging class HealthCheckFilter(logging.Filter): def filter(self, record): # Исключаем health check логи return 'health check' not in record.getMessage().lower() class SensitiveDataFilter(logging.Filter): def filter(self, record): # Маскируем чувствительные данные msg = record.getMessage() if 'password' in msg.lower(): return False return True
# logging.yaml filters: healthcheck: '()': myapp.logging.HealthCheckFilter sensitive: '()': myapp.logging.SensitiveDataFilter handlers: console: class: logging.StreamHandler filters: - healthcheck - sensitive

#5. Incremental конфигурация

Проблема: Нужно изменить уровень логирования без перезапуска приложения.

Решение: incremental: True в конфигурации.

import logging.config # Начальная конфигурация logging.config.dictConfig({ 'version': 1, 'loggers': { 'app': {'level': 'INFO'} } }) # ... приложение работает ... # Динамическое изменение уровня logging.config.dictConfig({ 'version': 1, 'incremental': True, # Важно! 'loggers': { 'app': {'level': 'DEBUG'} } }) # Теперь app logger выводит DEBUG сообщения

Use case: Admin endpoint для изменения уровня логирования.

# app.py from flask import Flask, request, jsonify import logging.config app = Flask(__name__) @app.post('/admin/logging') def update_logging(): data = request.json level = data.get('level', 'INFO').upper() logging.config.dictConfig({ 'version': 1, 'incremental': True, 'loggers': { 'app': {'level': level} } }) return jsonify({'status': 'ok', 'level': level})

Ограничения incremental:

  • Можно менять только уровни logger'ов
  • Нельзя добавлять/удалять handlers
  • Нельзя менять formatters

#6. Environment-specific конфигурация

Проблема: Разные настройки для development, staging, production.

Решение: Загрузка конфигурации в зависимости от окружения.

# config.py import os import logging.config import yaml def setup_logging(): env = os.getenv('APP_ENV', 'development') config_file = f'logging.{env}.yaml' with open(config_file, 'r') as f: config = yaml.safe_load(f) logging.config.dictConfig(config) # main.py from config import setup_logging setup_logging() logger = logging.getLogger(__name__)
# logging.development.yaml version: 1 disable_existing_loggers: false handlers: console: class: logging.StreamHandler level: DEBUG formatter: simple loggers: app: level: DEBUG handlers: [console] root: level: DEBUG handlers: [console]
# logging.production.yaml version: 1 disable_existing_loggers: false handlers: console: class: logging.StreamHandler level: INFO formatter: json file: class: logging.handlers.RotatingFileHandler level: DEBUG formatter: json filename: /var/log/app/app.log maxBytes: 10485760 backupCount: 10 sentry: '()': sentry_sdk.integrations.logging.SentryHandler level: ERROR loggers: app: level: INFO handlers: [console, file] propagate: false root: level: WARNING handlers: [console, file, sentry]

#7. Валидация конфигурации

Проблема: Нужно проверить конфигурацию перед применением.

Решение: Тестовый скрипт для валидации.

# validate_logging.py import logging.config import yaml import sys def validate_config(config_file): with open(config_file, 'r') as f: config = yaml.safe_load(f) try: logging.config.dictConfig(config) print(f"✅ Конфигурация {config_file} валидна") # Тестовое логирование logger = logging.getLogger('test') logger.info("Test message") return True except Exception as e: print(f"❌ Ошибка в {config_file}: {e}") return False if __name__ == '__main__': config_file = sys.argv[1] if len(sys.argv) > 1 else 'logging.yaml' sys.exit(0 if validate_config(config_file) else 1)

Использование в CI:

# Локальная проверка python validate_logging.py logging.yaml # Проверка всех конфигов в проекте python validate_logging.py logging.development.yaml python validate_logging.py logging.staging.yaml python validate_logging.py logging.production.yaml

#8. Production-ready конфигурация

Полный пример для production:

# logging.production.yaml version: 1 disable_existing_loggers: false formatters: json: '()': pythonjsonlogger.jsonlogger.JsonFormatter format: '%(asctime)s %(name)s %(levelname)s %(message)s %(user_id)s %(request_id)s' console: format: '%(levelname)s: %(name)s - %(message)s' filters: sensitive: '()': myapp.logging.SensitiveDataFilter healthcheck: '()': myapp.logging.HealthCheckFilter handlers: console: class: logging.StreamHandler level: INFO formatter: console filters: [healthcheck, sensitive] stream: ext://sys.stdout file: class: logging.handlers.RotatingFileHandler level: DEBUG formatter: json filename: /var/log/app/app.log maxBytes: 104857600 # 100 MB backupCount: 10 encoding: utf-8 error_file: class: logging.handlers.RotatingFileHandler level: ERROR formatter: json filename: /var/log/app/errors.log maxBytes: 52428800 # 50 MB backupCount: 5 encoding: utf-8 loggers: app: level: INFO handlers: [console, file, error_file] propagate: false app.api: level: INFO propagate: true # Использует handlers от app app.db: level: WARNING # Меньше шума от DB propagate: true # Сторонние библиотеки urllib3: level: WARNING propagate: false requests: level: WARNING propagate: false sqlalchemy: level: WARNING propagate: false root: level: WARNING handlers: [console]

#9. Best Practices

#9.1. Используйте dictConfig для production

# ✅ Хорошо import logging.config import yaml with open('logging.yaml') as f: config = yaml.safe_load(f) logging.config.dictConfig(config) # ❌ Избегайте для production logging.basicConfig(level=logging.INFO)

#9.2. Всегда указывайте disable_existing_loggers: False

# ✅ Хорошо LOGGING_CONFIG = { 'version': 1, 'disable_existing_loggers': False, # Важно! # ... } # ❌ Плохо: logger'ы библиотек будут отключены LOGGING_CONFIG = { 'version': 1, # disable_existing_loggers по умолчанию True }

#9.3. Используйте propagate=False для application logger'ов

# ✅ Хорошо loggers: app: level: DEBUG handlers: [console, file] propagate: false # Не дублировать в root # ❌ Плохо: логи дублируются loggers: app: level: DEBUG handlers: [console, file] propagate: true # По умолчанию

#9.4. Разделяйте конфигурацию по окружениям

logging.yaml          # Базовая конфигурация
logging.development.yaml   # Development
logging.staging.yaml       # Staging
logging.production.yaml    # Production

#9.5. Настройте уровни для сторонних библиотек

loggers: urllib3: level: WARNING # Иначе слишком много DEBUG логов requests: level: WARNING sqlalchemy: level: WARNING

#Резюме

#Сравнение методов конфигурации

МетодФорматГибкостьКастомные компонентыРекомендация
basicConfigPython dictНизкая❌ НетТолько для скриптов
dictConfigPython dict, JSON, YAMLВысокая✅ ДаProduction
fileConfigINIСредняя❌ НетLegacy проекты

#Ключевые поля dictConfig

ПолеЗначение
versionВсегда 1
disable_existing_loggersFalse для production
propagateFalse для application logger'ов
'()'Для кастомных классов
ext://Для импорта объектов
incrementalTrue для динамического изменения

#Best Practices

  1. Используйте dictConfig для production
  2. disable_existing_loggers: False для сохранения logger'ов библиотек
  3. propagate: False для предотвращения дублирования
  4. Разделяйте конфигурации по окружениям
  5. Настраивайте уровни для сторонних библиотек
  6. Используйте YAML для читаемости
  7. Валидируйте конфигурацию в CI

Далее: Продвинутые Handlers