Журнал Python: использовать миллисекунды в формате времени

163

По умолчанию 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)

Однако документации не указано, как форматировать миллисекунды. Я нашел этот вопрос, который говорит о микросекундах, но а) я бы предпочел миллисекунды и б) следующее не работает на Python 2.6 (над которым я работаю) из-за %f:

logging.Formatter(fmt='%(asctime)s',datefmt='%Y-%m-%d,%H:%M:%S.%f')
Джонатан
источник
1
Может быть, смена локали может помочь?
Пейтон
1
@ pajton - в следующей ссылке написано «Информация о локали не используется asctime ()» - docs.python.org/library/time.html#time.asctime
Джонатан
%fне работает на Python 2.7.9 или 3.5.1 либо
Энтони Хэтчкинс
4
Хороший разговор здесь. Я пришел сюда, потому что loggingутверждает, что его формат времени по умолчанию соответствует ISO 8601. Это не так. Он использует пробел, а не «Т» для разделения времени и запятой на доли секунды, а не десятичную точку. Как они могли быть такими неправыми?
LS

Ответы:

76

Обратите внимание, что решение Крейга МакДаниэля явно лучше.


Метод 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потому что ctявляется, time.struct_timeи эти объекты не записывают миллисекунды.

Если мы изменим определение, ctчтобы сделать его datetimeобъектом вместо a 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.
unutbu
источник
1
так что% f на самом деле даст микросекунды, а не миллисекунды, верно?
Джонатан
@Jonathan: ой, вы правы, %fдает микросекунды. Я полагаю, что самый простой способ получить миллисекунды - это заменить запятую на десятичную точку (см. Выше).
unutbu
3
Я действительно считаю, что это лучший ответ, потому что он возвращает вас к возможности использовать параметры форматирования STANDARD. Я действительно хотел микросекунды, и это был единственный вариант, который мог это сделать!
трубачи
Спасибо. Этот ответ дает простое решение для получения микросекунд.
Yongwei Wu
338

Это должно работать тоже:

logging.Formatter(fmt='%(asctime)s.%(msecs)03d',datefmt='%Y-%m-%d,%H:%M:%S')
Крейг МакДэниел
источник
12
Спасибо: вот документы для них: docs.python.org/2/library/logging.html#logrecord-attributes docs.python.org/3/library/logging.html#logrecord-attributes .. Есть ли способ по-прежнему включать часовой пояс (% z)? ... Время форматирования ISO8601 в журналах Python (, ->.) Было бы здорово.
Уэс Тернер
19
Это решение является недостатком, потому что, если у вас есть %zили %Zв вашем, datefmtвы хотите, чтобы это появилось ПОСЛЕ мсек, а не раньше.
Вим
1
А также, если вы используете 12-часовые часы, которые имеют AMилиPM
DollarAkshay
1
@wim как продолжение моего предыдущего комментария (больше не мог редактировать ...), вот что я сделал: from time import gmtime- # Use UTC rather than local date/time- 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)
Марк
1
@Mark Вы не можете встроить часовой пояс default_msec_format( начиная с Python 3.7), потому что в нем подставляются только время и миллисекунды. Из loggingисточника:self.default_msec_format % (t, record.msecs)
М. Дадли
28

Добавление msecs было лучшим вариантом, спасибо. Вот моя поправка, использующая это с Python 3.5.3 в 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")
Мастер Джеймс
источник
1
Безусловно самый простой и чистый вариант. Не уверен, почему вы получаете регистратор, когда вы можете просто вызвать logging.info (msg) и т. Д., Но формат - это именно то, что я искал. Любой другой, кто ищет все используемые атрибуты, может посмотреть здесь: docs.python.org/3.6/library/logging.html#logrecord-attributes
naphier
Хммм интересный момент, спасибо за комментарий, это пища для размышлений наверняка. Я, наверное, просто добавил это как урок в том, что там происходит, и чтобы убедиться, что это там, и потому что я попросил несколько вещей, чтобы он не нуждался в нескольких вызовах parent (через '.'), Чтобы получить его. Если бы вы снова вызвали .info или .debug, я бы, возможно, сохранял их снова, поскольку вы предлагаете сохранить цикл поиска справочной информации. [let info = logging.info]
Мастер Джеймс
Спасибо, что сказал Джейсон. Иногда есть более простой способ увидеть мир, не бойтесь попытаться открыть эту истину во многих, если не в каждой ситуации.
Мастер Джеймс
15

Самый простой способ, который я нашел, это переопределить default_msec_format:

formatter = logging.Formatter('%(asctime)s')
formatter.default_msec_format = '%s.%03d'
Микки Б
источник
1
Интересно, спасибо. Но это не сработало для меня в Python 2.7. Это может работать только в Python 3.x для некоторого значения x.
nealmcb
1
@nealmcb это не доступно до Python 3.3 в документации
Mark
3

После создания экземпляра 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
Джонатан
источник
2

Простое расширение, которое не требует 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
torrentails
источник
1

Если вы используете стрелку или если вы не против использовать стрелку. Вы можете заменить форматирование времени Python стрелкой.

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)

Теперь вы можете использовать все стрелки времени форматирования в datefmtатрибуте.

Slapy
источник
-1

tl; dr для людей, ищущих здесь дату в формате ISO:

datefmt: '% Y-% m-% d% H:% M:% S.% 03d% z'

Джефф Брайнер
источник
-3

На данный момент следующее отлично работает с 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 INFO

Пасинду Мадушан
источник
1
Это не работает. % d печатает дату. В вашем примере дата печатается с добавлением 0 перед ней.
Клик