Microsecond do not work in Python logger format

Question:

For some reason my Python logger does not want to recognize microseconds format.

import logging, io

stream = io.StringIO()
logger = logging.getLogger("TestLogger")
logger.setLevel(logging.INFO)
logger.propagate = False
log_handler = logging.StreamHandler(stream)
log_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s',"%Y-%m-%d %H:%M:%S.%f %Z")
log_handler.setFormatter(log_format)
logger.addHandler(log_handler)

logger.info("This is test info log")
print(stream.getvalue())

It returns:

2023-01-06 18:52:34.%f UTC - TestLogger - INFO - This is test info log

Why are microseconds missing?

Update

I am running
Python 3.10.4
Distributor ID: Debian
Description: Debian GNU/Linux 11 (bullseye)
Release: 11
Codename: bullseye

Asked By: user1700890

||

Answers:

The issue is that the formatTime method uses time.strptime to format the current time.time(), but since struct_time has no information about milliseconds and microseconds the formatter ignores the %f.

Also, note that the LogRecord calculates the milliseconds separately and stores them in another variable named msecs

To get what you’re looking for we need a custom version of the Formatter class that uses a different converter than time.localtime and is able to interpret the microseconds:


from datetime import datetime

class MyFormatter(logging.Formatter):
    def formatTime(self, record, datefmt=None):
        if not datefmt:
            return super().formatTime(record, datefmt=datefmt)

        return datetime.fromtimestamp(record.created).astimezone().strftime(datefmt)


...
log_format = MyFormatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s', "%Y-%m-%d %H:%M:%S.%f %Z")
...

Should output:

2023-01-06 17:47:54.828521 EST - TestLogger - INFO - This is test info log

Answered By: Ashwini Chaudhary

I found that the answer given by Ashwini Chaudhary to not be applicable for me and found a slightly simpler solution in creating a function with the formatting as done with datetime and changing the value of the logging.Formatter.formatTime method to this instead i.e.:

def _formatTime(self, record,  datefmt: str = None) -> str:
    return datetime.datetime.fromtimestamp(record.created).astimezone().strftime(datefmt)

log_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s',"%Y-%m-%d %H:%M:%S.%f %Z")

logging.Formatter.formatTime = _formatTime

And then generating your logger as normal

Answered By: Benjamin Carpenter
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.