0

UPDATE: The original question seems invalid as even if I managed to force logger to use datetime.now(), it still does not solve my ultimate goal of making the logging timestamps responsive to OS timezone changes without restarting the python interpreter. I have provided the answer I found below.

How to force the logger to use datetime.now() instead of time.asctime()? I need the log to follow strictly the Windows OS provided time, but time.asctime() attempts to convert the timezone if it thinks is needed. How do I override this behaviour?

Currently i use a custom logging.format subclass with the format string '{asctime}: {levelname} - {message}'

The purpose of doing this is because my python script changes the OS timezone during the execution. I want the log to immediately follow the updated timezone right after the script changes it. I have tried to define a converter function inside the logging.format subclass, but that resulted in the timezone in the logs not being updated even after I changed it.

The code I used (from doc https://docs.python.org/3.9/library/logging.html#logging.Formatter.formatTime):

class custom(logging.Formatter):
    def converter(self, timestamp):
        return datetime.now()

Tried every single answer from here: How to Change the time zone in Python logging?, non works as they all do not update to the new timezone set in the OS by my script. I tried to importlib.reload(tzlocal) and also no use.

The only sensible answer I found was to use time.tzset() but it apparently is not available on Windows

Billy Cao
  • 335
  • 4
  • 15
  • Do you think, you can manage with the `extras` parameter for the logger and use that in configuration? – Kris Nov 23 '21 at 12:13
  • are you sure that time.asctime and datetime.now take the time from different sources, one of which reflects time zone changes? – FObersteiner Nov 23 '21 at 12:20
  • @MrFuppes Yes. One from time.localtime(), the other from time.time(). I have seen it in my log files so I can confirm it. – Billy Cao Nov 23 '21 at 12:37
  • @Kris well I have no idea what's extras params so I guess not – Billy Cao Nov 23 '21 at 12:38
  • I think this post may answer your question: https://stackoverflow.com/questions/32402502/how-to-change-the-time-zone-in-python-logging/47104004 – yoonghm Nov 25 '21 at 02:58
  • @yoonghm this does not work because my script changes the OS timezone when it runs, and all answers provided does not update the new timezone into the log time. It is still using the old timezone when the script was launched. – Billy Cao Nov 25 '21 at 03:08

1 Answers1

0

After some research, I solved my own problem using ctypes, which is inspired by this answer. Essentially, I override the logging.Formatter.formatTime function by adding a call to Windows system API to get the current system time. This is a completely different approach than all existing python modules (time, datetime, pytz etc.) as the timezone info is not buffered when the python interpreter is launched.

Code:

class real_time_formatter(logging.Formatter):
    def formatTime(self, record, datefmt=None):
        class SYSTEMTIME(ctypes.Structure):
            _fields_ = [('wYear', ctypes.c_int16),
                        ('wMonth', ctypes.c_int16),
                        ('wDayOfWeek', ctypes.c_int16),
                        ('wDay', ctypes.c_int16),
                        ('wHour', ctypes.c_int16),
                        ('wMinute', ctypes.c_int16),
                        ('wSecond', ctypes.c_int16),
                        ('wMilliseconds', ctypes.c_int16)]

        lpSystemTime = ctypes.pointer(SystemTime)
        ctypes.windll.kernel32.GetLocalTime(lpSystemTime)
        year = '0' * (4 - len(str(SystemTime.wYear))) + str(SystemTime.wYear)
        month = '0' * (2 - len(str(SystemTime.wMonth))) + str(SystemTime.wMonth)
        day = '0' * (2 - len(str(SystemTime.wDay))) + str(SystemTime.wDay)
        hour = '0' * (2 - len(str(SystemTime.wHour))) + str(SystemTime.wHour)
        minute = '0' * (2 - len(str(SystemTime.wMinute))) + str(SystemTime.wMinute)
        second = '0' * (2 - len(str(SystemTime.wSecond))) + str(SystemTime.wSecond)
        ms = '0' * (3 - len(str(SystemTime.wMilliseconds))) + str(SystemTime.wMilliseconds)
        return f'{year}-{month}-{day} {hour}:{minute}:{second}:{ms}'  # 2003-01-23 00:29:50,411

streamHandler.setFormatter(real_time_formatter())

This mimics the python {asctime} formatting, and of course you can change it however you like, since it is just a f-string.

Billy Cao
  • 335
  • 4
  • 15