I decided to take a look into Python Logging and start using it instead of print().
Everything is here: Logging HOWTO.
Task you want to perform
The best tool for the task
Display console output for ordinary usage of a command line script or program
Report events that occur during normal operation of a program (e.g. for status monitoring or fault investigation)
logging.info()
(or logging.debug()
for very detailed output for diagnostic purposes)
Issue a warning regarding a particular runtime event
warnings.warn()
in library code if the issue is avoidable and the client application should be modified to eliminate the warning
logging.warning()
if there is nothing the client application can do about the situation, but the event should still be noted
Report an error regarding a particular runtime event
Raise an exception
Report suppression of an error without raising an exception (e.g. error handler in a long-running server process)
logging.error()
, logging.exception()
or logging.critical()
as appropriate for the specific error and application domain
- 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 functions
- CRITICAL - A serious error, indicating that the program itself may be unable to continue running
Logging BasicConfig
import logging
logging.basicConfig(level=logging.DEBUG)
logging.debug("debug message")
logging.info("info message")
logging.warning("warning message")
logging.error("error message")
logging.critical("critical message")
BasicConfig does a basic configuration for the logging system by creating a StreamHandler with a default Formatter and adding it to the root logger:
Get more from Logging
Create a logger for the script itself. Level is the highest possible for the script:
import logging
logger = logging.getLogger('logger')
logger.setLevel(logging.DEBUG)
Create a StreamHandler for console output
console = logging.StreamHandler()
console.setLevel(logging.DEBUG)
formatter = logging.Formatter("{name} - {levelname} - {message}", style="{")
console.setFormatter(formatter)
Create a StreamHandler for logfile:
logfile = logging.FileHandler("logfile.log", "a")
logfile.setLevel(logging.DEBUG)
formatter_file = logging.Formatter(
"{asctime} - {name} - {levelname} - {message}",
datefmt="%Y-%m-%d %H:%M:%S",
style="{",
)
logfile.setFormatter(formatter_file)
Add Handlers to the logger:
logger.addHandler(console)
logger.addHandler(logfile)
For console we can use simpler formatter and level, but logging into the file is better with data and time. So we can use different formatters for different Handlers. More options and more flexible.
import logging
logger = logging.getLogger('logger')
logger.setLevel(logging.DEBUG)
console = logging.StreamHandler()
console.setLevel(logging.DEBUG)
formatter = logging.Formatter("{name} - {levelname} - {message}", style="{")
console.setFormatter(formatter)
logfile = logging.FileHandler("logfile.log", "a")
logfile.setLevel(logging.DEBUG)
formatter_file = logging.Formatter(
"{asctime} - {name} - {levelname} - {message}",
datefmt="%Y-%m-%d %H:%M:%S",
style="{",
)
logfile.setFormatter(formatter_file)
logger.addHandler(console)
logger.addHandler(logfile)
logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")
Logging Tree
We can use logging configuration from one file while import functions from another.
As in the example, lets import functions from another file:
from test2 import test2_func
In the test2.py we have just a simple logging config. Name of the logger if “logger.test2”:
import logging
logger = logging.getLogger('logger.test2')
def test2_func():
logger.debug("Debug message from Test2 file")
Since the logger has “logger” in the name it gets all logging configuration from the main file. logging_more.py:
logger.debug('Before function')
test2_func()
logger.debug('After function')
Logging and Exceptions
We can use logging in try/except. How we usually do this - exception message only:
>>> try:
... 2/0
... except ZeroDivisionError as err:
... print(err)
...
division by zero
Using logging.exception: it uses ERROR level and includes the exception message itself:
>>> import logging
>>> try:
... 2/0
... except ZeroDivisionError as err:
... logging.exception("Your exception message")
...
ERROR:root:Your exception message
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero
We can also control the log level via the command line using an additional argument and argparse.
Another way to configure logging - import from another file (ini, yaml etc)