How to setup logging#

Using functions from apstools.utils.log_utils, it is easy to setup logging [5] reports to either the console or a file. Refer to the logging levels, as described in the section What are the logging levels? below.

To setup for logging in your code:

1import logging
2logger = logging.getLogger(__name__)

You can use this code in any module to create a logger object. Use one of the levels such as logger.warning("a warning message") or logger.debug("some detail") to add messages for logging. INFO and DEBUG level messages will not be reported unless you adjust the level to allow this level of detail. To set the level:

1logger.setLevel(logging.DEBUG)  # allow any log content at this level

Yet still, you may not see any output on your console unless you provide a handler which tells the logger what to do with each message. A logger has its own reporting level which determines which messages to report.


Set the logger level to the lowest level of any of the handlers you will use.

In the next sections, we show you how to add handlers. You can add multiple handlers to a logger for different types of reporting.


Logging handlers describe when, where, and how to report a logging message. The sections below show how to report to the console and to files. The logging documentation ([5]) shows how to report to more places. The bluesky documentation [1] provides debugging and logging guidance.

Reporting to the console (StreamHandler)#

Starting with the logger from How to setup logging, add a handler for reporting INFO (and higher) messages to the console. You can change the level with a keyword argument, see stream_log_handler().

1logger.addHandler(stream_log_handler())  # logger to the console

Reporting to a file (FileHandler or RotatingFileHandler)#

Starting with the logger from How to setup logging, add a handler for reporting DEBUG (and higher) messages to the file. The log file will be named .logs/my_log_file.log with up to 9 older files, each no larger than 1 MB.

 1BYTE = 1
 2kB = 1024 * BYTE
 3MB = 1024 * kB
 6    file_log_handler(  # logger to a file
 7        "my_log_file,
 8        maxBytes=1 * MB,
 9        backupCount=9,
10        level="DEBUG",
11    )

Reporting from included packages#

Some of the packages included might report via Python’s logging package. For example, bluesky [2] and ophyd [4] have several logger names that report messages. Here, we enable reporting from the top-level logger names in both these packages:

 1BYTE = 1
 2kB = 1024 * BYTE
 3MB = 1024 * kB
 5log_these_names = {
 6    "bluesky": "INFO",
 7    "ophyd": "INFO",
 9for logger_name, level in log_these_names.items():
10    _l = logging.getLogger(logger_name)
11    _l.setLevel(logging.DEBUG)  # allow any log content at this level
12    _l.addHandler(
13        file_log_handler(  # logger to a file
14            logger_name,
15            logger_name,
16            maxBytes=1 * MB,
17            backupCount=9,
18            level=level,  # filter reporting to this level
19        )
20    )

Recording IPython console Input and Output#

IPython offers its own logging process for recording the input (IN) and output (OUT) messages. When diagnosing problems, or for education reasons, it is useful to record this content to a file for later review.

This will create a file .logs/ipython_console.log (relative to the current working directory) for this log.


What are the logging levels?#






rare for any messages





default reporting level





the most verbose level

A logging handler will report any messages it receives at its current level or higher as shown in this table. With the default level (WARNING), you will only receive messages from logger.critical(), logger.error(), and logger.warning() calls.

Simply put, WARNING level only reports when something has gone wrong.

The usual practice is to set the logger (instance of logging.Logger) to the lowest level used by any handler.

The bluesky documentation ([3]) provides a flowchart illustrating how a log message will be reported by a handler.