Logging in Python

Here’s the translation of the Go logging example to Python, formatted in Markdown suitable for Hugo:

Python provides several ways to handle logging through its built-in logging module. This module offers a flexible framework for generating log messages from Python programs.

import logging
import json
from io import StringIO

def main():
    # The simplest way to log is using the basic config and logging functions
    logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
    logging.info("standard logger")

    # You can set the logging level and format
    logging.basicConfig(level=logging.INFO, format='%(asctime)s.%(msecs)03d - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
    logging.info("with milliseconds")

    # Adding the filename and line number to the log output
    logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(filename)s:%(lineno)d - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
    logging.info("with file/line")

    # Creating a custom logger
    mylog = logging.getLogger("mylogger")
    mylog.setLevel(logging.INFO)
    handler = logging.StreamHandler()
    formatter = logging.Formatter('my:%(asctime)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
    handler.setFormatter(formatter)
    mylog.addHandler(handler)
    mylog.info("from mylog")

    # Changing the prefix (name) of the logger
    mylog.name = "ohmy"
    mylog.info("from mylog")

    # Logging to a buffer
    buf = StringIO()
    buflog = logging.getLogger("buflogger")
    buflog.setLevel(logging.INFO)
    handler = logging.StreamHandler(buf)
    formatter = logging.Formatter('buf:%(asctime)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
    handler.setFormatter(formatter)
    buflog.addHandler(handler)
    buflog.info("hello")
    print("from buflog:", buf.getvalue().strip())

    # For structured logging, we can use the json module
    class JSONFormatter(logging.Formatter):
        def format(self, record):
            log_record = {
                "time": self.formatTime(record, self.datefmt),
                "level": record.levelname,
                "msg": record.getMessage()
            }
            return json.dumps(log_record)

    json_logger = logging.getLogger("json_logger")
    json_logger.setLevel(logging.INFO)
    json_handler = logging.StreamHandler()
    json_formatter = JSONFormatter('%(asctime)s')
    json_handler.setFormatter(json_formatter)
    json_logger.addHandler(json_handler)

    json_logger.info("hi there")
    json_logger.info("hello again", extra={"key": "val", "age": 25})

if __name__ == "__main__":
    main()

This Python script demonstrates various logging techniques:

  1. We start with the basic logging configuration and output.
  2. We then show how to include milliseconds in the timestamp.
  3. Next, we include the filename and line number in the log output.
  4. We create a custom logger with a specific prefix.
  5. We demonstrate how to change the name (prefix) of a logger.
  6. We show logging to a buffer (StringIO in this case) instead of standard output.
  7. Finally, we create a JSON formatter for structured logging, similar to the slog package in Go.

To run this script, save it as logging_example.py and execute it with Python:

$ python logging_example.py

The output will be similar to the following (timestamps will vary):

2023-08-22 10:45:16 - standard logger
2023-08-22 10:45:16.904 - with milliseconds
2023-08-22 10:45:16 - logging_example.py:20 - with file/line
my:2023-08-22 10:45:16 - from mylog
ohmy:2023-08-22 10:45:16 - from mylog
from buflog: buf:2023-08-22 10:45:16 - hello
{"time": "2023-08-22 10:45:16,904", "level": "INFO", "msg": "hi there"}
{"time": "2023-08-22 10:45:16,904", "level": "INFO", "msg": "hello again", "key": "val", "age": 25}

This example demonstrates how to use Python’s logging module to achieve similar functionality to Go’s log and slog packages. The logging module in Python is highly configurable and can be adapted to various logging needs.