Python日志记录:以时间格式使用毫秒

Jon*_*han 141 python time logging

默认情况下logging.Formatter('%(asctime)s'),使用以下格式打印:

2011-06-09 10:54:40,638
Run Code Online (Sandbox Code Playgroud)

其中638是毫秒.我需要将逗号更改为点:

2011-06-09 10:54:40.638
Run Code Online (Sandbox Code Playgroud)

格式化我可以使用的时间:

logging.Formatter(fmt='%(asctime)s',datestr=date_format_str)
Run Code Online (Sandbox Code Playgroud)

但是文档没有指定如何格式化毫秒.我发现这个SO问题谈论微秒,但是a)我更喜欢毫秒和b)以下不适用于Python 2.6(我正在研究),因为%f:

logging.Formatter(fmt='%(asctime)s',datefmt='%Y-%m-%d,%H:%M:%S.%f')
Run Code Online (Sandbox Code Playgroud)

Cra*_*iel 290

这也应该有效:

logging.Formatter(fmt='%(asctime)s.%(msecs)03d',datefmt='%Y-%m-%d,%H:%M:%S')
Run Code Online (Sandbox Code Playgroud)

  • 这个解决方案是有缺陷的,因为如果你在`datefmt`中有`%z`或`%Z`,你希望它出现在msecs之后,而不是之前. (16认同)
  • 谢谢:以下是这些文档:https://docs.python.org/2/library/logging.html#logrecord-attributes https://docs.python.org/3/library/logging.html#logrecord-属性..有没有办法仍然包括时区(%z)?... Python日志中的ISO8601格式时间(, - >.)会很棒. (12认同)
  • @wim 这是一种解决方法,但是通过执行 `logging.Formatter.converter = time.gmtime` 来使用 UTC 而不是本地时间,那么你就不需要使用 `%z` 或 `%Z`。或者,您可以将“logging.Formatter”对象的“default_msec_format”属性更改为 %s,%03d%z 或 %s,%03d%Z (3认同)
  • @wim 作为我之前评论的后续(无法再编辑...),这是我所做的:`from time import gmtime` - `# 使用 UTC 而不是本地日期/时间` - `logging .Formatter.converter = gmtime` - `logging.basicConfig(datefmt='%Y-%m-%dT%H:%M:%S', format='%(name)s | %(asctime)s.% (msecs)03dZ | %(message)s', level=log_level)` (2认同)
  • @Mark你不能在`default_msec_format`中嵌入时区(从Python 3.7开始),因为只有时间和毫秒被替换。来自`logging`源:`self.default_msec_format % (t, record.msecs)` (2认同)

unu*_*tbu 66

请注意Craig McDaniel的解决方案显然更好.


logging.Formatter的formatTime方法如下所示:

def formatTime(self, record, datefmt=None):
    ct = self.converter(record.created)
    if datefmt:
        s = time.strftime(datefmt, ct)
    else:
        t = time.strftime("%Y-%m-%d %H:%M:%S", ct)
        s = "%s,%03d" % (t, record.msecs)
    return s
Run Code Online (Sandbox Code Playgroud)

请注意逗号"%s,%03d".这不能通过指定datefmt因为ct是a 来修复,time.struct_time并且这些对象不记录毫秒.

如果我们改变定义ct使它成为一个datetime对象而不是一个struct_time,那么(至少在现代版本的Python中)我们可以调用ct.strftime,然后我们可以%f用来格式化微秒:

import logging
import datetime as dt

class MyFormatter(logging.Formatter):
    converter=dt.datetime.fromtimestamp
    def formatTime(self, record, datefmt=None):
        ct = self.converter(record.created)
        if datefmt:
            s = ct.strftime(datefmt)
        else:
            t = ct.strftime("%Y-%m-%d %H:%M:%S")
            s = "%s,%03d" % (t, record.msecs)
        return s

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

console = logging.StreamHandler()
logger.addHandler(console)

formatter = MyFormatter(fmt='%(asctime)s %(message)s',datefmt='%Y-%m-%d,%H:%M:%S.%f')
console.setFormatter(formatter)

logger.debug('Jackdaws love my big sphinx of quartz.')
# 2011-06-09,07:12:36.553554 Jackdaws love my big sphinx of quartz.
Run Code Online (Sandbox Code Playgroud)

或者,要获得毫秒,请将逗号更改为小数点,并省略datefmt参数:

class MyFormatter(logging.Formatter):
    converter=dt.datetime.fromtimestamp
    def formatTime(self, record, datefmt=None):
        ct = self.converter(record.created)
        if datefmt:
            s = ct.strftime(datefmt)
        else:
            t = ct.strftime("%Y-%m-%d %H:%M:%S")
            s = "%s.%03d" % (t, record.msecs)
        return s

...
formatter = MyFormatter(fmt='%(asctime)s %(message)s')
...
logger.debug('Jackdaws love my big sphinx of quartz.')
# 2011-06-09 08:14:38.343 Jackdaws love my big sphinx of quartz.
Run Code Online (Sandbox Code Playgroud)

  • 如果可以,我会再次给你+1,谢谢你的更新:) (3认同)
  • 所以 %f 实际上会给出微秒,而不是毫秒,对吗? (2认同)
  • 实际上,我认为这是最好的答案,因为它使您重新使用了STANDARD格式选项。我实际上想要微秒,而这是唯一可以做到的方法! (2认同)

Mas*_*mes 16

添加msecs是更好的选择,谢谢.以下是我在Blender中使用Python 3.5.3的修正案

import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s.%(msecs)03d %(levelname)s:\t%(message)s', datefmt='%Y-%m-%d %H:%M:%S')
log = logging.getLogger(__name__)
log.info("Logging Info")
log.debug("Logging Debug")
Run Code Online (Sandbox Code Playgroud)

  • 到目前为止,最简单和最干净的选择。不知道为什么当您可以调用 logging.info(msg) 等时会得到记录器,但格式正是我想要的。寻找所有可用属性的任何其他人都可以在这里查看:https://docs.python.org/3.6/library/logging.html#logrecord-attributes (3认同)

小智 12

我发现最简单的方法是覆盖default_msec_format:

formatter = logging.Formatter('%(asctime)s')
formatter.default_msec_format = '%s.%03d'
Run Code Online (Sandbox Code Playgroud)

  • @nealmcb 根据 [docs](https://docs.python.org/3.7/library/logging.html#logging.Formatter.formatTime),直到 Python 3.3 才可用 (2认同)

not*_*bit 6

这里有许多过时、过于复杂和奇怪的答案。原因是文档不够,简单的解决办法就是直接使用basicConfig()和设置如下:

logging.basicConfig(datefmt='%Y-%m-%d %H:%M:%S', format='{asctime}.{msecs:0<3.0f} {name} {threadName} {levelname}: {message}', style='{')
Run Code Online (Sandbox Code Playgroud)

这里的技巧是您还必须设置datefmt参数,因为默认设置会将其弄乱,而不是(当前)在操作方法 python 文档中显示的内容。所以宁可看看这里


另一种可能更简洁的方法是使用以下方法覆盖default_msec_format变量:

formatter = logging.Formatter('%(asctime)s')
formatter.default_msec_format = '%s.%03d'
Run Code Online (Sandbox Code Playgroud)

但是,由于未知原因,这不起作用

附注。我正在使用 Python 3.8。


小智 5

一个不需要datetime模块并且不像其他解决方案那样有障碍的简单扩展是使用简单的字符串替换,如下所示:

import logging
import time

class MyFormatter(logging.Formatter):
    def formatTime(self, record, datefmt=None):
        ct = self.converter(record.created)
        if datefmt:
            if "%F" in datefmt:
                msec = "%03d" % record.msecs
                datefmt = datefmt.replace("%F", msec)
            s = time.strftime(datefmt, ct)
        else:
            t = time.strftime("%Y-%m-%d %H:%M:%S", ct)
            s = "%s,%03d" % (t, record.msecs)
        return s
Run Code Online (Sandbox Code Playgroud)

通过这种方式,可以根据需要编写日期格式,甚至可以通过使用%F毫秒来考虑区域差异。例如:

log = logging.getLogger(__name__)
log.setLevel(logging.INFO)

sh = logging.StreamHandler()
log.addHandler(sh)

fm = MyFormatter(fmt='%(asctime)s-%(levelname)s-%(message)s',datefmt='%H:%M:%S.%F')
sh.setFormatter(fm)

log.info("Foo, Bar, Baz")
# 03:26:33.757-INFO-Foo, Bar, Baz
Run Code Online (Sandbox Code Playgroud)


jrc*_*jrc 5

我想出了一个两行代码来让 Python 日志模块以 RFC 3339(符合 ISO 1801 标准)格式输出时间戳,同时具有格式正确的毫秒和时区,并且没有外部依赖:

import datetime
import logging

# Output timestamp, as the default format string does not include it
logging.basicConfig(format="%(asctime)s: level=%(levelname)s module=%(module)s msg=%(message)s")

# Produce RFC 3339 timestamps
logging.Formatter.formatTime = (lambda self, record, datefmt: datetime.datetime.fromtimestamp(record.created, datetime.timezone.utc).astimezone().isoformat())
Run Code Online (Sandbox Code Playgroud)

例子:

>>> logging.getLogger().error("Hello, world!")
2021-06-03T13:20:49.417084+02:00: level=ERROR module=<stdin> msg=Hello, world!
Run Code Online (Sandbox Code Playgroud)

或者,最后一行可以写成如下:

def formatTime_RFC3339(self, record, datefmt=None):
    return (
        datetime.datetime.fromtimestamp(record.created, datetime.timezone.utc)
        .astimezone()
        .isoformat()
    )

logging.Formatter.formatTime = formatTime_RFC3339
Run Code Online (Sandbox Code Playgroud)

该方法也可以用于特定的格式化程序实例,而不是在类级别覆盖,在这种情况下,您需要self从方法签名中删除。