Use logging module

Usage

import logging

logging.basicConfig(
    # filename="foo.log",
    encoding="utf-8",
    level=logging.DEBUG,
    format='%(levelname)s %(asctime)s %(module)s %(message)s',
)

logger = logging.getLogger(__name__)
logger.debug("foo bar baz")

Log Level Explain

Level When it’s used
DEBUG Detailed information, typically of interest only when diagnosing problems.
INFO Confirmation that things are working as expected.
WARNING An indication that something unexpected happened, or indicative of some problem in the near future (e.g. ‘disk space low’). The software is still working as expected.
ERROR Due to a more serious problem, the software has not been able to perform some function.
CRITICAL A serious error, indicating that the program itself may be unable to continue running.

Loggers, Handlers, Filters and Formatters

The logging library takes a modular approach and offers several categories of components: loggers, handlers, filters, and formatters.

Module Function
Loggers expose the interface that application code directly uses.
Handlers send the log records (created by loggers) to the appropriate destination.
Filters provide a finer grained facility for determining which log records to output.
Formatters specify the layout of log records in the final output.

More on Loggers

getLogger() returns a reference to a logger instance with the specified name if it is provided, or root if not. The names are period-separated hierarchical structures. Multiple calls to getLogger() with the same name will return a reference to the same logger object. 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.

Loggers have a concept of effective level. If a level is not explicitly set on a logger, the level of its parent is used instead as its effective level. If the parent has no explicit level set, its parent is examined, and so on - all ancestors are searched until an explicitly set level is found. The root logger always has an explicit level set (WARNING by default). When deciding whether to process an event, the effective level of the logger is used to determine whether the event is passed to the logger’s handlers.

Child loggers propagate messages up to the handlers associated with their ancestor loggers. Because of this, it is unnecessary to define and configure handlers for all the loggers an application uses. It is sufficient to configure handlers for a top-level logger and create child loggers as needed. (You can, however, turn off propagation by setting the propagate attribute of a logger to False.)

If no configuration is used

If no logging configuration is used, a special handler lastResort is used, which writes log to stderr with level WARNING with formatting like level:module:message.

import logging

logging.debug("abc")
logging.info("abc")
logging.warning("abc")
logging.error("abc")

# Output
# WARNING:root:abc
# ERROR:root:abc

logger = logging.getLogger(__name__)
logger.debug("abc")
logger.info("abc")
logger.warning("abc")
logger.error("abc")

# Output
# WARNING:__main__:abc
# ERROR:__main__:abc

See also