Логирование в Python: FileHandler и Textual Console в TUI-приложении

Логирование в TUI-приложениях нужно не меньше, чем в обычных веб-сервисах или desktop-интерфейсах. Когда пользователь нажимает кнопки, переключает режимы, открывает экраны или запускает фоновые действия, разработчику важно видеть, что именно произошло и в каком порядке

В Textual это сделать проще, чем может показаться. Фреймворк умеет работать с обычным модулем logging из Python и предоставляет TextualHandler, который отправляет сообщения в Textual Console. В результате можно одновременно писать события в файл и смотреть их в отдельной консоли разработки

В этом материале разберем:

  • как подключить стандартный logging к Textual
  • как писать события в файл tui.log
  • как вывести сообщения в Textual Console
  • зачем запускать приложение через режим --dev
  • какие ошибки чаще всего ломают логирование в TUI

Если вы только начинаете разбираться с интерфейсами на Python, полезно сравнить этот подход с классическими GUI-приложениями. Например, в материале про создание простого GUI приложения с Python и Tkinter хорошо видно, как отличается модель обычного оконного интерфейса от TUI-подхода

Почему print не подходит для логирования в Textual

Textual-приложение занимает экран терминала. Из-за этого обычный print() быстро становится неудобным: вывод может конфликтовать с интерфейсом, теряться среди служебных событий или просто не попадать туда, где его удобно читать

Логирование решает эту проблему. Вы можете записывать события в файл, чтобы вернуться к ним после запуска, и параллельно смотреть поток событий в Textual Console во время разработки. Это особенно полезно, когда приложение реагирует на несколько пользовательских действий: клики, горячие клавиши, переключение темы, обновление виджетов, сетевые запросы или фоновые задачи

Важный плюс Textual в том, что он не требует отдельной системы логов. Вы используете привычный модуль logging, а TextualHandler становится еще одним обработчиком рядом с FileHandler, StreamHandler или любым другим handler из Python

Пример приложения с двумя кнопками

Соберем минимальное приложение. В нем будет две кнопки:

  • Toggle Dark Mode — переключает светлую и темную тему
  • Exit — закрывает приложение

Каждое действие будет попадать в лог. Создайте файл log_to_file.py и добавьте код:

import logging
from textual.app import App, ComposeResult
from textual.logging import TextualHandler
from textual.widgets import Button
class LogExample(App):
    def __init__(self) -> None:
        super().__init__()
        self.logger = logging.getLogger("log_example")
        self.logger.setLevel(logging.INFO)
        file_handler = logging.FileHandler("tui.log")
        file_handler.setFormatter(
            logging.Formatter(
                "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
            )
        )
        self.logger.addHandler(file_handler)
        textual_handler = TextualHandler()
        self.logger.addHandler(textual_handler)
    def compose(self) -> ComposeResult:
        yield Button("Toggle Dark Mode", classes="dark mode")
        yield Button("Exit", id="exit")
    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "exit":
            self.logger.info("User exited")
            self.exit()
        elif event.button.has_class("dark", "mode"):
            self.theme = (
                "textual-dark"
                if self.theme == "textual-light"
                else "textual-light"
            )
            self.logger.info("User toggled app theme to %s", self.theme)
if __name__ == "__main__":
    app = LogExample()
    app.run()

В этом примере у логгера два обработчика. Первый пишет сообщения в файл tui.log. Второй отправляет сообщения в Textual Console. Это значит, что одно и то же событие можно сохранить для последующего анализа и одновременно увидеть во время разработки

Как работает FileHandler

logging.FileHandler("tui.log") создает файл лога в рабочей директории приложения. Если файл уже существует, новые записи будут добавляться в него. Это удобно для локальной отладки, но в реальном проекте стоит заранее решить, нужно ли очищать лог при старте или хранить историю запусков

Форматтер в примере добавляет четыре поля:

  • время события
  • имя логгера
  • уровень сообщения
  • текст сообщения

Такой формат удобнее, чем просто строка текста. Когда приложение становится сложнее, по времени и уровню сообщения проще понять, что произошло до ошибки и какие действия пользователя к ней привели

Для учебного примера достаточно INFO. В продакшен-коде я обычно разделяю уровни так: DEBUG для подробной диагностики, INFO для нормальных пользовательских действий, WARNING для подозрительных ситуаций, ERROR для сбоев, которые нужно разбирать отдельно

Как подключить TextualHandler и запустить Textual Console

Чтобы видеть сообщения в отдельной консоли Textual, нужны devtools:

pip install textual-dev

После установки откройте отдельную вкладку терминала и запустите:

textual console

Затем в другой вкладке запустите приложение в dev-режиме:

textual run --dev log_to_file.py

Режим --dev важен: без него вы можете не увидеть ожидаемый поток событий в Textual Console. После запуска в консоли будут появляться служебные события Textual и ваши собственные сообщения, которые отправляются через TextualHandler

При этом файл tui.log будет содержать только те записи, которые вы явно отправили через self.logger.info() или другие методы логгера. Это нормально: Textual Console полезна для живой диагностики, а файл — для компактной истории пользовательских действий

Logger vs print в TUI: почему стоит использовать logging

На раннем этапе проще написать print("clicked"), но для TUI это быстро становится проблемой. Приложение рисует интерфейс в терминале, а прямой вывод может смешиваться с UI, ломать читаемость и мешать отладке

logger дает больше контроля. Вы можете менять уровень детализации, добавлять несколько обработчиков, писать в файл, отправлять сообщения в консоль разработки и позже заменить обработчик на более сложный вариант. Например, можно добавить ротацию файлов, отправку ошибок в отдельный канал или разные форматы для dev/prod-режимов

Еще одно отличие — структура. Строка User toggled app theme to textual-dark сразу говорит, что произошло. Если добавить время и уровень, можно восстановить последовательность действий без ручного просмотра всего интерфейса

Как не получить дубли логов

В Python один и тот же логгер может жить дольше, чем конкретный объект приложения. Поэтому при повторном запуске в dev-режиме или при пересоздании компонентов легко случайно добавить второй такой же handler. В результате одно действие пользователя начнет появляться в файле дважды, потом трижды, и проблема будет выглядеть как ошибка бизнес-логики

Простой защитный прием — вынести настройку логгера в отдельную функцию и проверять, были ли обработчики уже добавлены. В небольшом учебном примере это необязательно, но в рабочем проекте такая проверка экономит время

Например, можно договориться, что конфигурация логгера выполняется один раз при старте приложения, а виджеты только получают готовый logger и пишут в него события. Тогда кнопки, экраны и фоновые задачи не знают, куда именно отправляется лог: в файл, Textual Console или оба места сразу

Типичные ошибки при логировании в Textual

Первая ошибка — создать логгер, но не задать уровень через setLevel(). В таком случае часть сообщений может не попасть в вывод, и будет казаться, что handler не работает

Вторая ошибка — добавлять обработчики повторно при каждом создании экрана или виджета. Если один и тот же handler добавлен несколько раз, сообщения начнут дублироваться. Поэтому базовую настройку логгера лучше делать один раз: в приложении, фабрике логирования или отдельной функции конфигурации

Третья ошибка — писать в файл без понятного формата. Когда в логе есть только сообщение, трудно понять, когда оно произошло и из какого компонента пришло. Минимальный формат с временем, уровнем и именем логгера сильно облегчает разбор

Четвертая ошибка — логировать слишком много пользовательских данных. Для диагностики обычно достаточно факта действия и технического контекста. Не стоит писать в лог пароли, токены, персональные данные или длинные пользовательские сообщения

Если вы дальше будете усложнять интерфейс Textual, рядом пригодится материал Textual: создание пользовательского чекбокса в Python. Там уже важнее не только обработать событие, но и понимать, какие действия компонента стоит логировать

Textual хорошо встраивается в стандартное логирование Python. Для базовой диагностики достаточно создать обычный logger, добавить FileHandler для файла и TextualHandler для Textual Console

Главное — не смешивать логирование с интерфейсным выводом. print() может помочь в маленьком эксперименте, но для приложения лучше сразу использовать logging: так вы получите историю действий, понятные уровни сообщений и возможность расширять диагностику без переписывания кода

Оцените статью
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x