Ответы пользователя по тегу Журналирование
  • Как эффективнее работать с файлом, к которому нужен постоянный доступ?

    trapwalker
    @trapwalker
    Программист, энтузиаст
    Что вы подразумеваете под "безболезненно работать"? Правильно открытый на запись файл не мешает читать из него. Или что там у вас пользоватль собирается делать с файлом?
    Ответ написан
    3 комментария
  • Почему дублируются логи при использовании библиотеки logging?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Так вы, судя по конфигу, именно этого и хотите. У вас по факту один логгер, который жрёт всё подряд и передает без разбора трем хендлерам.
    Вы запутались в именах. Имя хендлера не фильтрует логи. Вы создаете именованные логгеры в коде, но по этим именам нигде не фильтруете.
    Для сности и прозрачности рекомендую именовать хендлеры в конфиге с префиксом "handler_" или как-то так. Чтобы не было соблазно ошибиться в именах. Явное лучше неявного
    Вам в конфиге нужно сделать три логера, каждый из которых будет фильтровать свои логи и отдавать своему хендлеру.

    Да, похоже вы не правильно поняли суть хендлеров.
    Фильтруют сообщения логгеры, хендлеры их обрабатывают.
    В вашем случае достаточно было одного хендлера. Несколько нужны на случай, если бы вам помимо stdout некоторые логи нужно было бы отправлять по почте, некоторые особенным образом форматировать и пихать в отдельный файл (например важные ошибки), некоторые гасить и никуда не выводить, некоторые отправлять через телеграм-бота в чат.

    LOGGING_CONFIG = {
        'version': 1,
        'disable_existing_loggers': True,
        'formatters': {
            'standard': {
                'format': '[%(asctime)s] %(levelname)s | %(name)s: %(message)s',
                'datefmt': '%Y-%m-%d %H:%M:%S',
            },
        },
        'handlers': {
            'std': {
                # 'level': 'INFO',
                'formatter': 'standard',
                'class': 'logging.StreamHandler',
                'stream': 'ext://sys.stdout',
            },
        },
        'loggers': {
            'init': {
                'level': 'DEBUG',
                'handlers': ['std'],
                'propagate': 1,
            },
            'parse': {
                'level': 'DEBUG',
                'handlers': ['std'],
                'propagate': 1,
            },
            'getmail': {
                'level': 'DEBUG',
                'handlers': ['std'],
                'propagate': 1,
            },
        },
    }
    Ответ написан
    3 комментария
  • Почему logging.log.warning не отдает управление вниз по стеку вызовов?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Вы к экстрасенсам обратились или к кому?
    Почему logging.log.warning не отдает управление вниз по стеку вызовов?

    Что такое у вас тут logging? Это стандартная питоновская библиотека? Так в ней log - это функция и у неё нет атрибута warning. Тот факт, что вы не привели трейс-бэка и ничего не сказали, по поводу падения на упоминании необъявленного condition наверно, вы лишь процитировали кусочек кода и где-то выше импортируется или создаётся logging, объявляется глобально condition... и вот это вот всё.
    ОК, я всегда допускаю и предполагаю, что автор вопроса РАЗУМНЫЙ ЧЕЛОВЕК И ПОСТУПАЕТ РАЗУМНО.
    Что я могу из этого понять?
    Наверно logging.log у автора никакого отношения к стандартном логгингу не имеет (или автор упрощая пример наговнякал херни).
    Наверно автор умышленно поставил return (как он мог его не заметить?) или автор просто в первый раз видит питон и... блин... не знаю.

    ЧТО ТУТ ПРОИСХОДИТ?

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

    Автор, просто приведите код, который работает не так, как вы ожидаете. ПОЛНОСТЬЮ. В том виде в котором он у вас работает не так как вы ожидаете. Тогда я подскажу где вы неправильно ожидаете.
    Ответ написан
    Комментировать
  • Как передать logging из импортированных модулей в основную программу?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Чтобы правильно использовать модуль logging, нужно во всех своих модулях содавать логгеры следующим образом:
    import logging
    log = logging.getLogger(__name__)

    В модулях, которые не запускаются сами по себе, никакого другого кода инициализации и настройки логирования не требуется.
    Если вы в своём модуле желаете различать два или более вида логов, то можно сделать как-то так:
    log = logging.getLogger(__name__)
    class MyModel:
        log = logging.getLogger(__name__ + '.MyModel')
        # ...

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

    В главном запускаемом файле кроме обычного создания логгера как во всех модулях будет ещё и инициализация и настройка системы логирования.
    В самом простом виде это что-то вроде такого:
    if __name__ == '__main__':
        logging.basicConfig(stream=sys.stderr, level='INFO', format='%(asctime)s %(levelname)-7s %(message)s')

    Тут говорится, что все логи уровня >= INFO будут в указанном формате направлены в stderr.

    В более сложном случае вы можете загрузить настройку логирования из конфигурационного файла или описать кодом:
    if __name__ == '__main__':
        # У вас может быть несколько разных способов форматировать код для разных мест:
        formatter_simple = Formatter(u'%(relativeCreated)08d %(levelname)-7s %(message)s')
        formatter_complex = Formatter(u'%(asctime)s %(levelname)-7s [%(filename)21s:%(lineno)-4d] %(message)s')
        # Несколько разных хендлеров для перехвата нужного вида сообщений и отправки в правильное место:
        handler_null      = logging.NullHandler()
        handler_screen    = handler(fmt=formatter_simple, stream=sys.stderr)
        handler_main_file = handler(
            fmt=formatter_complex,
            cls=logging.handlers.TimedRotatingFileHandler,
            when='midnight',
            backupCount=5,
            encoding='utf-8',
            filename=local_path('log/server.log'),
        )
        handler_errors_file = handler(
            fmt=formatter_complex,
            cls=logging.handlers.TimedRotatingFileHandler,
            when='midnight',
            backupCount=5,
            encoding='utf-8',
            filename=local_path('log/errors.log'),
            level='ERROR',
        )
        # А потом описываем сами логгеры:
        #   это корневой логер, пропускает все сообщения через себя насквозь и в то же отдаёт их своим хендлерам
        log_root       = logger(None, level='DEBUG', propagate=1, handlers=[handler_errors_file, handler_main_file, handler_screen])
        #   этот логер перехватывает логи торнадо-приложения, пропускает через себя и отдаёт хендлерам:
        log_app        = logger('tornado.application', level='DEBUG', propagate=1, handlers=[handler_errors_file, handler_main_file, handler_screen])
        #   этот собирает логи событий модели уровня выше или равного INFO (не отладочные) 
        #     и отдаёт соответствующим хендлерам, дальше для обработки свои сообщения не пускает
        log_events     = logger('app.model.events',     level='INFO',  propagate=0, handlers=[handler_errors_file, handler_events_file])
        #   этот логер съедает и отдаёт спец-хендлеру (он не показан выше, но должен быть) все логи http доступа
        log_websrv     = logger('tornado.access',                    level='DEBUG', propagate=0, handlers=[handler_websrv_file])
        #    этот логер глотает и гасит за ненадобностью все логи, которые генерит библиотека PIL при работе с PNG-файлами
        log_pil        = logger('PIL.PngImagePlugin',                level='INFO',  propagate=0, handlers=[handler_null])

    Если сообщение не перехвачено и не остановлено специфичным логером, оно улетает корневому логеру.
    Ну примерно как-то так.
    Само собой всю эту настройку лучше вынести в отдельный модуль и запускать при старте главного файла.
    Логеры вроде log_pil, log_websrv и прочих сохранены в отдельные переменные, но по факту в коде эти переменные нигде не используются. Их можно и не присваивать вовсе. В модуле логирования все логгеры регистрируются глобально в списке и поэтому в каждом модуле не требуется ничего импортировать, достаточно создать логгер по имени. По этому имени логер ищется среди созданных или автоматом создаётся новый. Имена логгеров используют точечную нотацию, и по ступеням имени их удобно фильтровать.

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

    P.S.
    Старайтесь не использовать CamelCase в именовании файлов проекта. Не смотря на то, что негласное это правило часто нарушается даже популярными библиотеками, всё же это часто создаёт лишнюю неразбериху и проблемы.
    В частности много геморроя можно поиметь переименовав файл сменив лишь регистр символа под контролем версий при работе с репозиторием на разных файловых системах. В винде, к примеру, регистр в именах сохраняется, но системой не различается, а в линукс различается. Таким образом винда не увидит переименования, а линукс увидит. Это может породить адский треш.
    Ответ написан
    Комментировать
  • Логгирование Python, как быстро сделать вывод ошибок в файл?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Самое первое, что нужно делать в любом более-менее полезном скрипте или большой программе - это настроить логирование.
    В самом простейшем виде его можно устроить так:
    import sys
    import logging
    if __name__ == '__main__':
        log = logging.getLogger()
        log.level = logging.DEBUG
        log.addHandler(logging.StreamHandler(sys.stderr))
    else:
        log = logging.getLogger(__name__)
    
    ## Используем так:
    log.info('any text')
    log.debug('Something about %r in %s', log, __name__)
    
    ## Здесь размещаем весь остальной код
    if __name__ == '__main__':
        pass  # А здесь, если надо, размещаем то, что будет выполняться, когда модуль запускают как скрипт

    Тут всё логирование заворачивается в sys.stderr, не настроено форматирование, вывод в файл и ротация. Это минимальный уровень, дающий возможность отказаться от print для логирования.
    Пример для питона 2.7.
    Добавлю, что при непосредственном запуске этого кода логгер создаётся с именем по умолчанию (root). Это значит, что в других модулях проекта, управление которым передаётся,
    log = logging.getLogger(__name__)

    создаст логгер с именем модуля, и в главном файле можно будет настроить роутинг логов с учетом этих имён.
    Роутинг настраивается добавлением дополнительных хендлеров и к ним разных фильтров. Если есть какие-то затруднения с этим - пишите, поясню.
    Использовать правильное управляемое, расширяемое и гибкое логирование вместо россыпи принтов очень просто. Нужно просто себя приучить к этому, как к чему-то обязательному вроде чистки зубов, использования системы контроля версий и своевременного бэкапа.
    Ответ написан
    Комментировать