D
D
domanskiy2020-03-10 13:38:03
Python
domanskiy, 2020-03-10 13:38:03

How to pass logging from imported modules to the main program?

There is a conMySQL module that connects to MySQL and generates an SQL query. dictionary output.
In the main MyProg program, I import conMySQL and use it.
In the main MyProg program, for logging errors, I use the logging module with writing to a file.
Both MyProg and conMySQL import the logging module
How can the main program MyProg get error logs from the conMySQL module... using logging in conMySQL?

Answer the question

In order to leave comments, you need to log in

3 answer(s)
S
Sergey Pankov, 2020-03-10
@domanskiy

To properly use the logging module, you need to create loggers in all your modules as follows:

import logging
log = logging.getLogger(__name__)

In modules that do not run by themselves, no other initialization code or logging setup is required.
If you want to distinguish between two or more types of logs in your module, you can do something like this:
log = logging.getLogger(__name__)
class MyModel:
    log = logging.getLogger(__name__ + '.MyModel')
    # ...

In this case, this class will have its own logger with its own name.
You can also create a logger in a metaclass and give it the class name automatically, then each child will have its own logger with its own name.
However, all these loggers do not know anything about the files and streams where their logs will end up. All filtering and redirection will be done in a single logging configuration in the main program.
In the main executable file, in addition to the usual creation of a logger, as in all modules, there will also be initialization and configuration of the logging system.
In its simplest form, it's something like this:
if __name__ == '__main__':
    logging.basicConfig(stream=sys.stderr, level='INFO', format='%(asctime)s %(levelname)-7s %(message)s')

It says here that all >= INFO level logs will be sent to stderr in the specified format.
In a more complex case, you can load the logging setting from a configuration file or describe it in code:
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])

If the message is not intercepted and stopped by a specific logger, it flies to the root logger.
Well, something like that.
Of course, it is better to put all this setting into a separate module and run it at the start of the main file.
Loggers like log_pil, log_websrvand others are stored in separate variables, but in fact these variables are not used anywhere in the code. They may or may not be assigned at all. In the logging module, all loggers are registered globally in a list, and therefore nothing needs to be imported in each module, it is enough to create a logger by name. By this name, the logger is searched among the created loggers or a new one is created automatically. Logger names use dot notation, and it is convenient to filter them by name steps.
This approach has its drawbacks (singleton, implicit description, error tolerance), its advantages are weighty: the absence of dependency hell (when it is difficult or impossible to determine the acyclic order for creating loggers), customization flexibility, isolation (the ability to overlap and filter without getting into the logging code) .
PS
Try not to use CamelCase in naming project files. Despite the fact that this unspoken rule is often violated even by popular libraries, it often creates unnecessary confusion and problems.
In particular, a lot of hemorrhoids can be caused by renaming a file by changing only the case of the character under version control when working with the repository on different file systems. In Windows, for example, the case in names is preserved, but the system does not differ, but in Linux it differs. Thus, Windows will not see the renaming, but Linux will. This can lead to hellish trash.

E
evgs89, 2020-03-10
@evgs89

In the main program, you create a logger, configure it as you need.
In all imports:
import logging
...
log = logging.get_logger('module name') # you can __name__
log.debug("logging ok")
All messages will be processed by the root logger.

D
Dr. Bacon, 2020-03-10
@bacon

You don’t need to transfer it in any way, it’s not clear what the problem is, if you don’t redefine anything in the module in a tricky way, then everything should work without problems.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question