Creating a basic log

There are three steps to producing a log. The two necessary steps are the following:

  1. Get a logging.Logger instance with the logging.getLogger() function; for example, logger=logging.getLogger("demo").
  2. Create messages with that Logger. There are a number of methods, with names such as warn(), info(), debug(), error(), and fatal(), that create messages with different levels of importance. For example, logger.info("hello world").

These two steps are not sufficient to give us any output, however. There's a third, optional step that we take when we want to see logged messages. The reason for having a third step is because seeing a log isn't always required. Consider a debugging log that is generally left silent. The optional step is to configure the logging module's handlers, filters, and formatters. We can use the logging.basicConfig() function for this; for example, logging.basicConfig(stream=sys.stderr, level=logging.INFO).

It's technically possible to skip the first step. We can use the default logger, which is part of the logging module's top-level functions. We showed you this in Chapter 9, Decorators and Mixins – Cross-Cutting Aspects, because the focus was on decoration, not logging. It is advisable not to use the default root logger and suggest that it's universally more configurable to use named loggers that are children of the root logger.

Instances of the Logger class are identified by a name attribute. The names are dot-separated strings that form a hierarchy. There's a root logger with the name "", the empty string. All other Logger instances are children of this root Logger instance. A complex application named foo might have an internal package named services with a module named persistence and a class named SQLStore. This could lead to loggers named "", "foo", "foo.services", "foo.services.persistence", and "foo.services.persistence.SQLStore"

We can often use the root Logger to configure the entire tree of Logger instances. When we choose names to form a proper hierarchy, we can enable or disable whole subtrees of related instances by configuring an appropriate parent Logger object. In our preceding example, we might enable debugging for "foo.services.persistence" to see messages from all of the classes with a common prefix for their logger name.

In addition to a name, each Logger object can be configured with a list of Handler instances that determines where the messages are written and a list of Filter objects to determine which kinds of messages are passed or rejected. These Logger instances have the essential API for logging; we use a Logger object to create LogRecord instances. These records are then routed to Filter and Handler objects; the passed records are formatted and eventually wind up getting stored in a local file, or transmitted over a network.

The best practice is to have a distinct logger for each of our classes or modules. As Logger object names are dot-separated strings, the Logger instance names can parallel class or module names; our application's hierarchy of component definitions will have a parallel hierarchy of loggers. We might have a class that starts like the following code:

import logging 
class Player:

def __init__(self, bet: str, strategy: str, stake: int) -> None:
self.logger = logging.getLogger(
self.__class__.__qualname__)
self.logger.debug(
"init bet %r, strategy %r, stake %r",
bet, strategy, stake
)

The unique value of self.__class__.__qualname__ will ensure that the Logger object used for this class will have a name that matches the qualified name of the class.

As a general approach to logging, this works well. The only downside of this approach is that each logger instance is created as part of the object, a tiny redundancy. It would be a somewhat better use of memory to create the logger as part of the class instead of each instance of the class.

In the next section, we'll look at a few ways to create a class-level logger shared by all instances of the class.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset