Can basicConfig only be used on root logger and handlers/formatter only be used on namedloggers?

一世执手 提交于 2019-12-02 04:59:32

Word of advice


Firstly, simple and complex (or basic and advanced) are relative terms. You could have just the root logger with a very complex logging configuration, would you call that simple logging, because you're using the root logger ? No. You shouldn't tie the semantics (meaning) of relative terms like basic and advanced to Python objects. The semantics of language constructs is denoted either by the computation they induce or by the effect they produce, which is always the same for everybody.

Lexicon


Secondly, let's clear up a few terms.

  • logging is a Python module.

  • basicConfig & getLogger are module level functions.

  • debug(), info(), warning(), etc. are both module level functions and class methods, depending on how you call them. If you do logging.debug(msg) you're calling a module level function, if you do some_logger.debug(msg) you're calling a method. The module level function itself also calls the root method under the hood.

Flow of execution & Hierarchies


The root logger is automatically created when you import the logging machinery, i.e when you do import logging - the root logger is automatically created which, in turn, enables you to do straightforward calls such as logging.debug(), which use that root logger.

Basically, a module level function looks like this:

def debug(msg, *args, **kwargs):
    """
    Log a message with severity 'DEBUG' on the root logger. If the logger has
    no handlers, call basicConfig() to add a console handler with a pre-defined
    format.
    """
    if len(root.handlers) == 0:
        basicConfig()
    root.debug(msg, *args, **kwargs)

Loggers are organized in hierarchies, and all loggers are descendants of the root logger.

When you do a call to getLogger(name) if the name exists it will return that logger, if it doesn't, it will create that logger. The getLogger(name) function is idempotent, meaning, for subsequent calls with the same name it will just return that existing logger no matter how many times you call it.

The name is potentially a period-separated hierarchical value, like foo.bar.baz. Loggers that are further down in the hierarchical list are children of loggers higher up in the list. For example, given a logger with a name of foo, loggers with names of foo.bar, foo.bar.baz, and foo.bam are all descendants of foo.

When a logger is created, the level is set to NOTSET (which causes all messages to be delegated to the parent when the logger is a non-root logger). This means that if a logger has a level of NOTSET, its chain of ancestor loggers is traversed until either an ancestor with a level other than NOTSET is found, or the root is reached.

Without going very deep in the details, here are the relevant links: logger objects, module level functions, flow of execution.

Your questions


In simple logging, we can configure the log path and msg format using logging. basicConfig whereas in case of advanced logging we have the concept of a formatter, handler which is assigned to the logger obtained by using logging.getlogger(some_name).addhandlers..

No.

basicConfig, as we now know, is a module level function. This function sets up the basic configuration for your logging system and should be called before anything else, because if you do any kind of logging before calling that yourself, functions like debug(), info(), etc. will call basicConfig() automatically if no handlers are defined for the root logger. This function is also idempotent, meaning once you call it once, you can call it a billion times after with no effect. But this call will determine how your logging will work for all loggers not just the root (because all loggers are connected through hierarchies) and pass messages from one to another, unless you specify explicit configuration for descendant loggers.

The path is where you want your log messages to be recorded, and this is set up via handlers and it can be the console, a file, an email, whatever... see a complete list here.

The format is how you want your messages to show, what kind of information you want them to contain, and that is done via formatters, where you provide the log record attributes you want. Those attributes determine which information a logrecord knows about.

But this all works together. Handlers are attached to loggers and formatters are attached to handlers. You can set these up one time per your entire application via basicConfig or dictConfig or fileConfig or you can set these up individually, per logger.

So the only benefit of advanced logging is the possibility for us to add the logger name either to a hardcoded value or to name which is respective module value.

No.

More complex logging means that you can split your application into modules and have separate loggers for each module, and have a very refined message system, where each part of the application logs different things (you'd want sensitive parts to log very specific information and maybe send them rapidly via email or log them to a file) whereas you'd want trivial parts to log lightly and just print them via console.

Can basicConfig only be used on root logger and handlers/formatter only be used on namedloggers?

basicConfig will set the configuration for the root logger which in turn all loggers will use, unless otherwise specified.

Example


import logging

root = logging.getLogger()
print(root.handlers)  # no handlers at this point
logging.warning('hello')  # calls basicConfig
print(root.handlers)  # has handler now

# create file handler
fh = logging.FileHandler('spam.log')
fh.setLevel(logging.ERROR)

# create formatter and add it to the handlers
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)

# add the handlers to the logger
root.addHandler(fh)

print(root.handlers)  # now has 2 handlers
root.warning('whats good')  # will only show to console
root.error('whats good')  # will show to console and file

random_logger = logging.getLogger('bogus')  # another logger, descendant from root
random_logger.warning('im random')  # will use root handlers, meaning it will show to console
random_logger.error('im random error')  # same as above, both console and file

# and you can ofc add handlers and what not differently to this non root logger
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!