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

发布于 2024-11-14 09:14:14 字数 716 浏览 3 评论 0原文

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

2011-06-09 10:54:40,638

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

2011-06-09 10:54:40.638

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

logging.Formatter(fmt='%(asctime)s',datestr=date_format_str)

但是 文档没有指定如何格式化毫秒。我发现这个所以问题讨论了微秒,但是a)我更喜欢毫秒,b)由于%f,以下内容在Python 2.6(我正在研究)上不起作用:

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

By default logging.Formatter('%(asctime)s') prints with the following format:

2011-06-09 10:54:40,638

where 638 is the millisecond. I need to change the comma to a dot:

2011-06-09 10:54:40.638

To format the time I can use:

logging.Formatter(fmt='%(asctime)s',datestr=date_format_str)

however the documentation doesn't specify how to format milliseconds. I've found this SO question which talks about microseconds, but a) I would prefer milliseconds and b) the following doesn't work on Python 2.6 (which I'm working on) due to the %f:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(18

心房敞 2024-11-21 09:14:14

这也应该有效:

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

This should work too:

logging.Formatter(
    fmt='%(asctime)s.%(msecs)03d',
    datefmt='%Y-%m-%d,%H:%M:%S'
)
青衫负雪 2024-11-21 09:14:14

请注意 Craig McDaniel 的解决方案是更好的选择,除非您需要支持所有 ISO 8601 格式代码。


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

注意 "%s,%03d" 中的逗号。这无法通过指定 datefmt 来解决,因为 cttime.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.

或者,要获取毫秒,将逗号更改为小数点,并省略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.

Please note Craig McDaniel's solution is preferable, unless you need support for all ISO 8601 format codes.


logging.Formatter's formatTime method looks like this:

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

Notice the comma in "%s,%03d". This can not be fixed by specifying a datefmt because ct is a time.struct_time and these objects do not record milliseconds.

If we change the definition of ct to make it a datetime object instead of a struct_time, then (at least with modern versions of Python) we can call ct.strftime and then we can use %f to format microseconds:

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.

Or, to get milliseconds, change the comma to a decimal point, and omit the datefmt argument:

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.
白鸥掠海 2024-11-21 09:14:14

添加毫秒是更好的选择,谢谢。
这是我在 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")

Adding msecs was the better option, Thanks.
Here is my amendment using this with Python 3.5.3 in Blender

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")
一身骄傲 2024-11-21 09:14:14

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

formatter = logging.Formatter('%(asctime)s')
formatter.default_msec_format = '%s.%03d'

The simplest way I found was to override default_msec_format:

formatter = logging.Formatter('%(asctime)s')
formatter.default_msec_format = '%s.%03d'
爱的故事 2024-11-21 09:14:14

我想出了一个双行代码,让 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=None: datetime.datetime.fromtimestamp(record.created, datetime.timezone.utc).astimezone().isoformat())

示例:

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

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

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

logging.Formatter.formatTime = formatTime_RFC3339

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

I figured out a two-liner to get the Python logging module to output timestamps in RFC 3339 (ISO 1801 compliant) format, with both properly formatted milliseconds and timezone and without external dependencies:

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=None: datetime.datetime.fromtimestamp(record.created, datetime.timezone.utc).astimezone().isoformat())

Example:

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

Alternatively, that last line could be written out as follows:

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

logging.Formatter.formatTime = formatTime_RFC3339

That method could also be used on specific formatter instances, rather than overriding at the class level, in which case you will need to remove self from the method signature.

不气馁 2024-11-21 09:14:14

这里有许多过时的、过于复杂的和奇怪的答案。原因是文档不充分,简单的解决方案是仅使用 basicConfig() 并按如下方式设置:

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

这里的技巧是您还必须设置 datefmt 参数,因为默认把它弄乱了,并且不是(当前)显示在Python 操作方法文档。因此,请查看此处


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

formatter = logging.Formatter('%(asctime)s')
formatter.default_msec_format = '%s.%03d'

但是,由于未知原因,该方法不起作用

<子>PS。我正在使用 Python 3.8。

Many outdated, over-complicated and weird answers here. The reason is that the documentation is inadequate and the simple solution is to just use basicConfig() and set it as follows:

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

The trick here was that you have to also set the datefmt argument, as the default messes it up and is not what is (currently) shown in the how-to python docs. So rather look here.


An alternative and possibly cleaner way, would have been to override the default_msec_format variable with:

formatter = logging.Formatter('%(asctime)s')
formatter.default_msec_format = '%s.%03d'

However, that did not work for unknown reasons.

PS. I am using Python 3.8.

我是男神闪亮亮 2024-11-21 09:14:14

TLDR:虽然这不是OP所要求的,但如果您想将所有日志时间戳打印为带有毫秒和UTC偏移量的ISO8601,您可以覆盖默认格式化程序formatTime,如下所示:

import logging
from datetime import datetime

def formatTime(self, record, datefmt=None):
    return datetime.fromtimestamp(record.created).astimezone().isoformat(timespec='milliseconds')

logging.Formatter.formatTime = formatTime

产生这样的日志

2024-01-15T19:23:01.371+01:00 INFO     __main__        test 1

%(asctime)s 通过 logging.Formatter.formatTime(self, record),文档指出 formatTime 将使用 time.strftime()datetime.strftime()time.strftime 无法打印毫秒,而 datetime.strftime() 另一方面有 %f。因此您不能在 datefmt 上使用 %f

如果您只需将 ,(逗号)更改为 .(点),您就可以使用其他答案中结合 %(asctime) 的简单选项s%(msecs)03d 像这样(使用 logging.config.dictConfig() 和 YAML 文件)

# log_config.yml
version: 1
formatters:
  default:
    format: "%(asctime)s.%(msecs)03d %(levelname)-8s %(name)-15s %(message)s"
    datefmt: "%Y-%m-%d %H:%M:%S"

但是 如果您需要的话,将无法工作例如,在毫秒后添加 UTC 偏移量/时区

为了获得更合理的时间戳格式,您可以实现自己的格式化程序,该格式化程序是 logging.Formatter 的子类,并且只需覆盖 formatTime 即可。例如,以下格式化程序将使用 datetime.isoformat()

# myformatter.py
import logging
from datetime import datetime

class CustomFormatter(logging.Formatter):
    def formatTime(self, record, datefmt=None):
        return datetime.fromtimestamp(record.created).astimezone().isoformat(timespec='milliseconds')

在上面的代码中,

# log_config.yml
version: 1
formatters:
  default:
    class: myformatter.CustomFormatter
    format: "%(asctime)s %(levelname)-8s %(name)-15s %(message)s"
handlers:
  console:
    class: logging.StreamHandler
    stream: ext://sys.stdout
    formatter: default
loggers:
  root:
    level: INFO
    handlers: [console]

上述配置结果为:

2024-01-15T18:40:42.006307+01:00 INFO     __main__        test 1

其中 %(asctime)s 值来自 CustomFormatter.formatTime()

最终您可以替换 logging.Formatter.formatTime > 使用您自己的实现而不是创建新的格式化程序:

import logging
import logging.config
import yaml
from datetime import datetime

def formatTime(self, record, datefmt=None):
    return datetime.fromtimestamp(record.created).astimezone().isoformat(timespec='milliseconds')

logging.Formatter.formatTime = formatTime # no need for a custom formatter if we monkeypatch the default formatter

with open("log_config.yml", "rt") as f:
    logging.config.dictConfig(yaml.safe_load(f.read()))
    
logger = logging.getLogger(__name__) # __name__

myvar = 1
logger.info("test %s", myvar)


TLDR: Although it's not what the OP asked for, if you want to print all log timestamps as ISO8601 with milliseconds and UTC offset you can overwrite the default formatter formatTime like this:

import logging
from datetime import datetime

def formatTime(self, record, datefmt=None):
    return datetime.fromtimestamp(record.created).astimezone().isoformat(timespec='milliseconds')

logging.Formatter.formatTime = formatTime

produces logs like this

2024-01-15T19:23:01.371+01:00 INFO     __main__        test 1

The %(asctime)s is formatted through logging.Formatter.formatTime(self, record), the documentation states that formatTime will use time.strftime() which is not the same as datetime.strftime(). time.strftime has no way of printing milliseconds , datetime.strftime() on the other hand has %f. So you can't use %f on the datefmt.

If you only need to change the , (comma) to a . (dot) you have the simple options from other answers combining %(asctime)s, and %(msecs)03d like so (using logging.config.dictConfig() and a YAML file)

# log_config.yml
version: 1
formatters:
  default:
    format: "%(asctime)s.%(msecs)03d %(levelname)-8s %(name)-15s %(message)s"
    datefmt: "%Y-%m-%d %H:%M:%S"

But that will not work if you need to add the UTC offset /timezone after the milliseconds, for example.

In order to have a more sensible timestamp formatting you can implement your own formatter that subclasses logging.Formatter and just override formatTime. For example, the following formatter will produce logs with timestamps in ISO8601 including milliseconds and UTC offset by using datetime.isoformat():

# myformatter.py
import logging
from datetime import datetime

class CustomFormatter(logging.Formatter):
    def formatTime(self, record, datefmt=None):
        return datetime.fromtimestamp(record.created).astimezone().isoformat(timespec='milliseconds')

In the code above

# log_config.yml
version: 1
formatters:
  default:
    class: myformatter.CustomFormatter
    format: "%(asctime)s %(levelname)-8s %(name)-15s %(message)s"
handlers:
  console:
    class: logging.StreamHandler
    stream: ext://sys.stdout
    formatter: default
loggers:
  root:
    level: INFO
    handlers: [console]

The above configuration results in :

2024-01-15T18:40:42.006307+01:00 INFO     __main__        test 1

where %(asctime)s value comes from CustomFormatter.formatTime()

Ultimately you can replace the logging.Formatter.formatTime with your own implementation instead of creating a new formatter:

import logging
import logging.config
import yaml
from datetime import datetime

def formatTime(self, record, datefmt=None):
    return datetime.fromtimestamp(record.created).astimezone().isoformat(timespec='milliseconds')

logging.Formatter.formatTime = formatTime # no need for a custom formatter if we monkeypatch the default formatter

with open("log_config.yml", "rt") as f:
    logging.config.dictConfig(yaml.safe_load(f.read()))
    
logger = logging.getLogger(__name__) # __name__

myvar = 1
logger.info("test %s", myvar)


枯叶蝶 2024-11-21 09:14:14

不需要 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

这样可以根据需要编写日期格式,甚至允许对于区域差异,使用 %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

A simple expansion that doesn't require the datetime module and isn't handicapped like some other solutions is to use simple string replacement like so:

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

This way a date format can be written however you want, even allowing for region differences, by using %F for milliseconds. For example:

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
云之铃。 2024-11-21 09:14:14

实例化Formatter后,我通常设置formatter.converter = gmtime。因此,为了让 @unutbu 的答案在这种情况下起作用,您需要:

class MyFormatter(logging.Formatter):
    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

After instantiating a Formatter I usually set formatter.converter = gmtime. So in order for @unutbu's answer to work in this case you'll need:

class MyFormatter(logging.Formatter):
    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
再浓的妆也掩不了殇 2024-11-21 09:14:14

我所需要做的就是在创建格式化程序之前设置 default_msec_format 属性。

logging.Formatter.default_msec_format = '%s.%03d'                        # Just add this line
formatter = logging.Formatter('%(asctime)s %(levelname)s - %(message)s')

All I needed to do was set default_msec_format attribute before creating the formatter.

logging.Formatter.default_msec_format = '%s.%03d'                        # Just add this line
formatter = logging.Formatter('%(asctime)s %(levelname)s - %(message)s')
思念满溢 2024-11-21 09:14:14

如果您使用 箭头 或者您不介意使用箭头。您可以用 python 的时间格式替换 arrow 的时间格式。

import logging

from arrow.arrow import Arrow


class ArrowTimeFormatter(logging.Formatter):

    def formatTime(self, record, datefmt=None):
        arrow_time = Arrow.fromtimestamp(record.created)

        if datefmt:
            arrow_time = arrow_time.format(datefmt)

        return str(arrow_time)


logger = logging.getLogger(__name__)

default_handler = logging.StreamHandler()
default_handler.setFormatter(ArrowTimeFormatter(
    fmt='%(asctime)s',
    datefmt='YYYY-MM-DD HH:mm:ss.SSS'
))

logger.setLevel(logging.DEBUG)
logger.addHandler(default_handler)

现在,您可以在 datefmtarrow 的时间格式 > 属性。

If you are using arrow or if you don't mind using arrow. You can substitute python's time formatting for arrow's one.

import logging

from arrow.arrow import Arrow


class ArrowTimeFormatter(logging.Formatter):

    def formatTime(self, record, datefmt=None):
        arrow_time = Arrow.fromtimestamp(record.created)

        if datefmt:
            arrow_time = arrow_time.format(datefmt)

        return str(arrow_time)


logger = logging.getLogger(__name__)

default_handler = logging.StreamHandler()
default_handler.setFormatter(ArrowTimeFormatter(
    fmt='%(asctime)s',
    datefmt='YYYY-MM-DD HH:mm:ss.SSS'
))

logger.setLevel(logging.DEBUG)
logger.addHandler(default_handler)

Now you can use all of arrow's time formatting in datefmt attribute.

夜吻♂芭芘 2024-11-21 09:14:14

如果您更喜欢使用 style='{'fmt="{asctime}.{msecs:0<3.0f}" 会将您的微秒用 0 填充到三位为了一致性。

If you prefer to use style='{', fmt="{asctime}.{msecs:0<3.0f}" will 0-pad your microseconds to three places for consistency.

灼疼热情 2024-11-21 09:14:14

在花费了我一些宝贵的时间之后,下面的技巧对我有用。我刚刚在 settings.py 中更新了我的格式化程序,并将 datefmt 添加为 %y/%b/%Y %H:%M:%S并将毫秒附加到 asctime,如下所示 {asctime}.{msecs:0<3.0f}

EG:

    'formatters': {
        'verbose': {
            'format': '[{asctime}.{msecs:0<3.0f}] {levelname} [{threadName:s}] {module} → {message}',
            'datefmt': "%y/%b/%Y %H:%M:%S",
            'style': '{',
        },
    }

After burning some of my precious time the below hack worked for me. I just updated my formatter in settings.py and added datefmt as %y/%b/%Y %H:%M:%S and appended the milliseconds to the asctime like this {asctime}.{msecs:0<3.0f}

E.G:

    'formatters': {
        'verbose': {
            'format': '[{asctime}.{msecs:0<3.0f}] {levelname} [{threadName:s}] {module} → {message}',
            'datefmt': "%y/%b/%Y %H:%M:%S",
            'style': '{',
        },
    }
明媚如初 2024-11-21 09:14:14

使用这个智能答案来获取时区和选择的答案,您可以使用您的构造毫秒和时区所需的格式:

import logging
import time

if __name__ == "__main__":
    tz = time.strftime('%z')
    logging.basicConfig(
        format=(
            "%(asctime)s.%(msecs)03d" + tz + " %(levelname)s "
            "%(pathname)s:%(lineno)d[%(threadName)s]: %(message)s"
        ),
        level=logging.DEBUG,
        datefmt="%Y-%m-%dT%H:%M:%S",
    )
    logging.info("log example")

就我个人而言,我喜欢以 UTC 格式保存所有日志,但也会在日志中明确显示此格式,因为没有时区的日期时间在多区域应用程序中毫无意义:

    logging.Formatter.converter = time.gmtime
    logging.basicConfig(
        format=(
            "%(asctime)s.%(msecs)03d+0000 %(levelname)s "
            "%(pathname)s:%(lineno)d[%(threadName)s]: %(message)s"
        ),
        level=logging.DEBUG,
        datefmt="%Y-%m-%dT%H:%M:%S",
    )

Using this smart answer for the timezone and the chosen answer, you can construct the millisecond and timezone with your desired format:

import logging
import time

if __name__ == "__main__":
    tz = time.strftime('%z')
    logging.basicConfig(
        format=(
            "%(asctime)s.%(msecs)03d" + tz + " %(levelname)s "
            "%(pathname)s:%(lineno)d[%(threadName)s]: %(message)s"
        ),
        level=logging.DEBUG,
        datefmt="%Y-%m-%dT%H:%M:%S",
    )
    logging.info("log example")

Personally, I like to keep all the logs in UTC but also have this explicitly in the log as a datetime without a timezone is meaningless in a multizone application:

    logging.Formatter.converter = time.gmtime
    logging.basicConfig(
        format=(
            "%(asctime)s.%(msecs)03d+0000 %(levelname)s "
            "%(pathname)s:%(lineno)d[%(threadName)s]: %(message)s"
        ),
        level=logging.DEBUG,
        datefmt="%Y-%m-%dT%H:%M:%S",
    )
深海里的那抹蓝 2024-11-21 09:14:14

我对此的看法如下:

class MicrosecondsFormatter(logging.Formatter):
    def formatTime(self, record, datefmt=None):
        return super(MicroSecondsFormatter, self).formatTime(record, self.datefmt.replace('%f', str(datetime.fromtimestamp(record.created).microsecond).ljust(6, '0')))

logging.Formatter = MicrosecondsFormatter

上面添加了对日期格式“%f”的支持,因此可以像往常一样继续使用日志记录模块,例如:

log_format = f'%(asctime)s %(levelname)-8s %(message)s'
log_date_format = '%d/%m/%Y %H:%M:%S.%f %z'

logging.basicConfig(stream=sys.stdout,
                    format=log_format,
                    datefmt=log_date_format,
                    level=logging.INFO)

My take on this is the following:

class MicrosecondsFormatter(logging.Formatter):
    def formatTime(self, record, datefmt=None):
        return super(MicroSecondsFormatter, self).formatTime(record, self.datefmt.replace('%f', str(datetime.fromtimestamp(record.created).microsecond).ljust(6, '0')))

logging.Formatter = MicrosecondsFormatter

The above adds support for "%f" in the date format, and so one can continue using the logging module as usual, for example:

log_format = f'%(asctime)s %(levelname)-8s %(message)s'
log_date_format = '%d/%m/%Y %H:%M:%S.%f %z'

logging.basicConfig(stream=sys.stdout,
                    format=log_format,
                    datefmt=log_date_format,
                    level=logging.INFO)
梦里寻她 2024-11-21 09:14:14

要允许用户指定包含 %fdatefmt,您可以创建自定义格式化程序并重写 formatTime() 方法。

由于重写使用 super() 来获取默认格式化时间,因此此代码不会更改 formatTime() 方法的默认行为。

请注意,代码会将 %f 截断为毫秒,而不是微秒。

下面的代码执行以下操作 -

  1. 调用 super() 来获取格式化时间。
  2. 使用 record.msecs 作为基础,创建要在格式化时间中使用的毫秒 (msecs)。
  3. 使用 %f 作为分隔符分割格式化时间。
  4. 加入 msecs 上的分割格式化时间。
  5. 返回格式化时间(原始时间,或根据需要更新为毫秒)。
import logging

class CustomFormatter(logging.Formatter):

    def formatTime(self, record: logging.LogRecord, datefmt: str|None=None) -> str:
        '''Allow `datafmt` to interpret `%f` as milliseconds.'''

        s = super().formatTime(record, datefmt)
        if '%f' in s:
            s_split = s.split('%f')
            msecs = '%03d' % record.msecs
            s = msecs.join(s_split)

        return s
        
fmt='%(asctime)s %(levelname)-8s => %(message)s'
datefmt='%Y-%m-%d %H:%M:%S.%f'

stdout_handler = logging.StreamHandler()
stdout_handler.setFormatter(CustomFormatter(fmt, datefmt))

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger.addHandler(stdout_handler)

logger.info('FooBar')

To allow users to specify a datefmt that contains %f, you could create a custom Formatter and override the formatTime() method.

Because the override is using super() to get the default formatted time, this code doesn't change the default behaviour of the formatTime() method.

Note that the code will truncate %f to milliseconds, as opposed to microseconds.

The code below does the following -

  1. Calls super() to get the formatted time.
  2. Creates the milliseconds (msecs) to use in the formatted time, using record.msecs as the base.
  3. Splits the formatted time using %f as the separator.
  4. Joins the split formatted time on msecs.
  5. Returns the formatted time (either original, or updated with milliseconds if appropriate).
import logging

class CustomFormatter(logging.Formatter):

    def formatTime(self, record: logging.LogRecord, datefmt: str|None=None) -> str:
        '''Allow `datafmt` to interpret `%f` as milliseconds.'''

        s = super().formatTime(record, datefmt)
        if '%f' in s:
            s_split = s.split('%f')
            msecs = '%03d' % record.msecs
            s = msecs.join(s_split)

        return s
        
fmt='%(asctime)s %(levelname)-8s => %(message)s'
datefmt='%Y-%m-%d %H:%M:%S.%f'

stdout_handler = logging.StreamHandler()
stdout_handler.setFormatter(CustomFormatter(fmt, datefmt))

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger.addHandler(stdout_handler)

logger.info('FooBar')
☆獨立☆ 2024-11-21 09:14:14

tl;dr 对于在这里寻找 ISO 格式日期的人来说:

不要使用类似 '%Y-%m-%d %H:%M:%S.%03d%z' 的东西,而是创建你自己的类,如 @unutbu 所示。这是 iso 日期格式:

import logging
from time import gmtime, strftime

class ISOFormatter(logging.Formatter):
    def formatTime(self, record, datefmt=None):
        t = strftime("%Y-%m-%dT%H:%M:%S", gmtime(record.created))
        z = strftime("%z",gmtime(record.created))
        s = "%s.%03d%s" % (t, record.msecs,z)        
        return s

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

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

formatter = ISOFormatter(fmt='%(asctime)s - %(module)s - %(levelname)s - %(message)s')
console.setFormatter(formatter)

logger.debug('Jackdaws love my big sphinx of quartz.')
#2020-10-23T17:25:48.310-0800 - <stdin> - DEBUG - Jackdaws love my big sphinx of quartz.

tl;dr for folks looking here for an ISO formatted date:

instead of using something like '%Y-%m-%d %H:%M:%S.%03d%z', create your own class as @unutbu indicated. Here's one for iso date format:

import logging
from time import gmtime, strftime

class ISOFormatter(logging.Formatter):
    def formatTime(self, record, datefmt=None):
        t = strftime("%Y-%m-%dT%H:%M:%S", gmtime(record.created))
        z = strftime("%z",gmtime(record.created))
        s = "%s.%03d%s" % (t, record.msecs,z)        
        return s

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

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

formatter = ISOFormatter(fmt='%(asctime)s - %(module)s - %(levelname)s - %(message)s')
console.setFormatter(formatter)

logger.debug('Jackdaws love my big sphinx of quartz.')
#2020-10-23T17:25:48.310-0800 - <stdin> - DEBUG - Jackdaws love my big sphinx of quartz.

独享拥抱 2024-11-21 09:14:14

到目前为止,以下内容与 python 3 完美配合。

         logging.basicConfig(level=logging.DEBUG,
                     format='%(asctime)s %(levelname)-8s %(message)s',
                     datefmt='%Y/%m/%d %H:%M:%S.%03d',
                     filename=self.log_filepath,
                     filemode='w')

给出以下输出

2020/01/11 18:51:19.011 信息

As of now the following works perfectly with python 3 .

         logging.basicConfig(level=logging.DEBUG,
                     format='%(asctime)s %(levelname)-8s %(message)s',
                     datefmt='%Y/%m/%d %H:%M:%S.%03d',
                     filename=self.log_filepath,
                     filemode='w')

gives the following output

2020/01/11 18:51:19.011 INFO

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文