Logging Concept

Python’s logging system is highly complex. For example:

  • each module/package can have its own Logger.

  • each Logger could be equipped with multiple Handler.

  • each Handler could have its own Formatter and Filter.

  • log message propagate through its parent logger chain for processing.

To put it simply, Python logging is like rocket science, running pure steroids as fuel, with Tony Stark at the controls…

../_images/tonystark.jpg

Tip

hence why it is highly beneficial for everyone to read & understand logging fully.

In an effort to preserve this behavior, as well as minimizing usage complexity, pyATS logging was engineered to provide just enough for logs to be backwards compatible to existing tools & infrastructure (using CiscoLog format), offer the same ease of use, whilst maintaining the full flexibility and power of native python logging.

Behavior

The overall pyATS logging behavior (applicable to the infrastructure, end-user libraries and scripts) can be conceptually summarized as follows:

  1. Logging is always done through using Python logging module.

  2. Logger should always be named the same as the current module name (using magic variable __name__), following the logger hierarchy.

    # Example
    # -------
    #
    #   creating a logger object
    
    import logging
    
    logger = logging.getLogger(__name__)
    
  3. log module features Formatter and Handler class that implements CiscoLog compatible log message format & storage behavior, and should be used to configure Python logging.

  4. Under most circumstances, the pyATS test infrastructure configures logging for the user (in easypy and aetest). However, when needed, the user scripts/modules/libraries is also able to configure their own logging behavior/format (such as when used in another peer, non-pyATS infra)

  5. Unless otherwise configured, there is only one active TaskLog per process where, by default, all log messages propagate to. If any additional handlers are processing log messages, the initiator is responsible for tracking/handling/closing them.

Adhering to this concept enables the following benefits:

  • Logging hierarchy is preserved; root logger by default handles the logging to TaskLog.

  • Scripts and libraries do not care about how log outputs are configured, and their only requirement is to use Python logging module and its functionalities.

  • If needed, scripts can configure their own logging handlers and formatters for additional log files/output.

TaskLog

The log file storing script-run log messages in pyATS is called the TaskLog. This file is only generated when executing pyATS testscripts through easypy, or when logging is manually configured to use TaskLogHandler.

The TaskLog file is no different than any other python logging log files in the sense that it is also a text-based file, containing log messages. It is referred to as the TaskLog for the sake of name reference consistency, and to highlight the fact that its log messages adhere to the Cisco Log Format.

Logging Levels

Python logging natively offers 5 logging levels:

  • critical

  • error

  • warning

  • info

  • debug

At any time, logging level can be set using setLevel API:

# Example
# -------
#
#   setting logger level
#
#   available levels:
#       logging.INFO
#       logging.CRITICAL
#       logging.ERROR
#       logging.WARNING
#       logging.DEBUG

import logging

logger = logging.getLogger(__name__)

logger.setLevel(logging.INFO)

When configuring your Python logger, use the actual Python levels and follow the Pythonic logging concept where if a level’s numeric value is bigger than the current set level, then it would be displayed. This should be self-intuitive.

Logging provides a set of convenience functions for simple logging usage. These are critical(), error(), warning(), info(), debug() and exception(). To determine when and which logging level API to use, refer to the table below:

Task you want to perform

Suggested API

Report event that occurred during

normal operation of a program

(e.g. for status monitoring or

fault investigation)

logger.info() or logger.debug()

debug for very detailed output for

diagnostic purposes

Issue a warning regarding a

particular runtime event.

Script/program continues.

logger.warning()

  • In library code if the issue is

    avoidable and the client application

    should be modified to eliminate the

    warning.

  • If there is nothing the client

    application can do the situation,

    but the event should still be noted.

  • In user script, abnormal situation

    happens, but don’t want the script

    to quit.

Report suppression of an error

particular runtime event

logger.error()

do not raise an exception

Report an error regarding a

particular runtime even.

Raising an exception and

terminate the program.

logger.critical() or

logger.error()

Raise an exception

Report an error with exception

information/stack added

automatically to the messages

logger.exception()

This can only be called from an exception

handler.

For more information on Python logging, refer to its documentation and tutorial.