问题描述
一点背景:
Python docs 说:
在 {}
格式的情况下,您可以通过将它们放在属性名称之后来指定格式标志,并用冒号将其分隔。例如:{msecs:03d}
的占位符会将 4 的毫秒值格式化为 004。
然而,这不起作用,因为日志记录的 msecs
值实际上是一个 float
:
>>> record.msecs
275.38108825683594
因此尝试使用文档中的示例 {}
格式会引发 ValueError
:
>>> f'{record.msecs:03d}'
*** ValueError: UnkNown format code 'd' for object of type 'float'
使用 %
的旧式 03d
格式化适用于 float
:
>>> '%03d' % record.msecs
'275'
基于上述发现,我在日志定义中使用了 float
格式规范,如下所示:
logging.basicConfig(
level=logging.INFO,format="{asctime}.{msecs:0>3.0f} [{levelname:.1s}] {message}",datefmt="%Y-%m-%d %H:%M:%s",style="{",)
这似乎运行良好,直到我注意到这个日志条目:
2021-03-25 22:37:18.993 [I] ...
2021-03-25 22:37:18.997 [I] ...
2021-03-25 22:37:18.1000 [I] ...
2021-03-25 22:37:19.002 [I] ...
2021-03-25 22:37:19.004 [I] ...
当日志记录的 msecs
浮点值 >= 999.5 时会发生这种情况:
>>> t1 = 999.499999
>>> t2 = 999.5
>>> print(f"{t1:0>3.0f} {t2:0>3.0f}")
999 1000
显然,如果有办法也增加 {{ 1}} 时间戳的一部分。 但是我只记录 .1000
(截断为 .000
而不是四舍五入)就可以了,因为这种溢出只发生在很小比例的时间内,而且就我的目的而言此日志记录不是那关键。
除了回到基于 {asctime}
的旧式格式之外,还有什么方法可以做到这一点?其他?
解决方法
您可以覆盖 default millisecond format:
logging.basicConfig(
level=logging.INFO,format="{asctime} ...{msecs:3.1f} ...{msecs:3.0f} [{levelname:.1s}] {message}",# datefmt="%Y-%m-%d %H:%M:%S",style="{",)
logging.Formatter.default_msec_format = '%s.%03d'
我认为达到你想要的毫秒数被截断为三位数。不幸的是,指定 datefmt
会把它搞砸,毫秒不显示。
以下是自定义 converter
和 formatTime
函数,可用于替换默认的 Formatter 方法。这些将允许您在配置中指定 datefmt。它们协同工作,因此必须同时使用。
import logging
def converter(self,t):
'''Returns a tuple (time.struct_time,int)
'''
# separate seconds from fractional seconds
# round the fraction,# adjust the seconds if needed
# turn the fraction into an integer
seconds = int(t)
msec = round(t - seconds,3)
if msec > .9995:
msec = 0
seconds += 1
else:
msec = int(1000 * msec)
# parts = list(time.localtime(seconds))
# parts[-2] = msec
t = time.localtime(seconds)
return t,msec
def formatTime(self,record,datefmt=None):
"""
Return the creation time of the specified LogRecord as formatted text.
This method should be called from format() by a formatter which
wants to make use of a formatted time. This method expects the
converter to return a (time.struct_time,int) tuple where the int is
between 0 and 999.
The int is the rounded msec of the record's creation time.
"""
ct,msec = self.converter(record.created)
if datefmt:
s = time.strftime(datefmt,ct)
else:
s = time.strftime(self.default_time_format,ct)
s = f'{s}.{msec:03d}'
return s
logging.basicConfig(
level=logging.INFO,format="{asctime} | {message}",datefmt="%Y-%m-%d %H:%M:%S",)
logging.Formatter.converter = converter
logging.Formatter.formatTime = formatTime
logging.info('foo')
那个日志看起来像:
2021-03-27 16:12:05.498 | foo
如果 datefmt 参数不以秒结束,那么添加毫秒将是愚蠢的,所以也许应该检查一下。
def formatTime(self,ct)
# s = f'{s}.{msec:03d}'
# if self.default_msec_format:
# s = self.default_msec_format % (s,record.msecs)
if datefmt[-1] == 'S':
s = f'{s}.{msec:03d}'
return s