Python 记录器时间使用更新的 OS 时区(替换 Windows 中的 time.tzset())
Python logger time use updated OS timezone (replacement of time.tzset() in Windows)
更新:
原始问题似乎无效,因为即使我设法强制记录器使用 datetime.now()
,它仍然没有解决我的最终目标,即在不重新启动 [=41= 的情况下使日志记录时间戳响应 OS 时区更改】 翻译。我在下面提供了我找到的答案。
如何强制记录器使用 datetime.now()
而不是 time.asctime()
?我需要日志严格遵循 Windows OS 提供的时间,但 time.asctime()
在认为需要时会尝试转换时区。如何覆盖此行为?
目前我使用格式字符串 '{asctime}: {levelname} - {message}'
的自定义 logging.format 子类
这样做的目的是因为我的python脚本在执行过程中改变了OS时区。我希望日志在脚本更改后立即跟随更新的时区。我试图在 logging.format
子类中定义一个 converter
函数,但这导致日志中的时区即使在我更改后也没有更新。
我使用的代码(来自文档https://docs.python.org/3.9/library/logging.html#logging.Formatter.formatTime):
class custom(logging.Formatter):
def converter(self, timestamp):
return datetime.now()
尝试了这里的每一个答案:,都没有用,因为它们都没有更新到我的脚本在 OS 中设置的新时区。我试了importlib.reload(tzlocal)
也没用
我找到的唯一明智的答案是使用 time.tzset()
但它显然在 Windows
上不可用
经过一些研究,我使用 ctypes
解决了我自己的问题,这是受此 answer 的启发。本质上,我通过添加对 Windows 系统 API 的调用来覆盖 logging.Formatter.formatTime
函数以获取当前系统时间。这是一种与所有现有 python 模块(time, datetime, pytz
等)完全不同的方法,因为启动 python 解释器时不会缓冲时区信息。
代码:
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())
这模仿了 python {asctime}
格式,当然您可以随意更改它,因为它只是一个 f 字符串。
更新:
原始问题似乎无效,因为即使我设法强制记录器使用 datetime.now()
,它仍然没有解决我的最终目标,即在不重新启动 [=41= 的情况下使日志记录时间戳响应 OS 时区更改】 翻译。我在下面提供了我找到的答案。
如何强制记录器使用 datetime.now()
而不是 time.asctime()
?我需要日志严格遵循 Windows OS 提供的时间,但 time.asctime()
在认为需要时会尝试转换时区。如何覆盖此行为?
目前我使用格式字符串 '{asctime}: {levelname} - {message}'
这样做的目的是因为我的python脚本在执行过程中改变了OS时区。我希望日志在脚本更改后立即跟随更新的时区。我试图在 logging.format
子类中定义一个 converter
函数,但这导致日志中的时区即使在我更改后也没有更新。
我使用的代码(来自文档https://docs.python.org/3.9/library/logging.html#logging.Formatter.formatTime):
class custom(logging.Formatter):
def converter(self, timestamp):
return datetime.now()
尝试了这里的每一个答案:importlib.reload(tzlocal)
也没用
我找到的唯一明智的答案是使用 time.tzset()
但它显然在 Windows
经过一些研究,我使用 ctypes
解决了我自己的问题,这是受此 answer 的启发。本质上,我通过添加对 Windows 系统 API 的调用来覆盖 logging.Formatter.formatTime
函数以获取当前系统时间。这是一种与所有现有 python 模块(time, datetime, pytz
等)完全不同的方法,因为启动 python 解释器时不会缓冲时区信息。
代码:
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())
这模仿了 python {asctime}
格式,当然您可以随意更改它,因为它只是一个 f 字符串。