Rejestrowanie w Pythonie: użyj milisekund w formacie czasu


163

Domyślnie logging.Formatter('%(asctime)s')drukuje w następującym formacie:

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

gdzie 638 to milisekunda. Muszę zmienić przecinek na kropkę:

2011-06-09 10:54:40.638

Aby sformatować czas, którego mogę użyć:

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

jednak dokumentacja nie określa, jak formatować milisekundy. Znalazłem to pytanie SO, które mówi o mikrosekundach, ale a) wolałbym milisekundy ib) poniższe nie działa w Pythonie 2.6 (nad którym pracuję) z powodu %f:

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

1
Może zmiana ustawień regionalnych mogłaby pomóc?
pajton,

1
@ pajton - w poniższym linku jest napisane „Informacje o ustawieniach regionalnych nie są używane przez asctime ()” - docs.python.org/library/time.html#time.asctime
Jonathan

%fnie działa również na Pythonie 2.7.9 lub 3.5.1
Antony Hatchkins

4
Dobra rozmowa. Przyszedłem tutaj, ponieważ loggingtwierdzi, że jego domyślny format czasu jest zgodny z ISO 8601. Tak nie jest. Używa spacji, a nie „T” do oddzielania czasu i przecinka dla ułamków sekund, a nie kropki dziesiętnej. Jak mogli się tak mylić?
LS

Odpowiedzi:


76

Proszę zauważyć, że rozwiązanie Craiga McDaniela jest wyraźnie lepsze.


logowanie. formatTimemetoda formatowania wygląda następująco:

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

Zwróć uwagę na przecinek w "%s,%03d". Nie można tego naprawić, określając a, datefmtponieważ ctjest a, time.struct_timea te obiekty nie rejestrują milisekund.

Jeśli zmienimy definicję, ctaby uczynić go datetimeobiektem zamiast a struct_time, to (przynajmniej w nowoczesnych wersjach Pythona) możemy wywołać, ct.strftimea następnie użyć %fdo sformatowania mikrosekund:

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.

Lub, aby uzyskać milisekundy, zmień przecinek na kropkę dziesiętną i pomiń datefmtargument:

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.

1
więc% f faktycznie dałoby mikrosekundy, a nie milisekundy, prawda?
Jonathan

@Jonathan: Ups, masz rację, %fpodaje mikrosekundy. Przypuszczam, że najłatwiejszym sposobem uzyskania milisekund jest zmiana przecinka na przecinek dziesiętny (patrz edycja powyżej).
unutbu

3
Właściwie uważam, że jest to najlepsza odpowiedź ze względu na fakt, że wraca ona do możliwości korzystania ze STANDARDOWYCH opcji formatowania. Właściwie chciałem mikrosekund, a to była jedyna opcja, która mogła to zrobić!
trumpetlicks

Dzięki. Ta odpowiedź daje łatwe rozwiązanie, aby uzyskać mikrosekundy.
Yongwei Wu

337

To też powinno działać:

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

12
Dzięki: oto ich dokumenty: docs.python.org/2/library/logging.html#logrecord-attributes docs.python.org/3/library/logging.html#logrecord-attributes .. Czy istnieje sposób, aby nadal uwzględniać strefę czasową (% z)? ... Czasy formatu ISO8601 w dziennikach Pythona (, ->.) Byłyby świetne.
Wes Turner

19
To rozwiązanie jest ułomne, ponieważ jeśli masz %zlub %Zw swoim datefmtchcesz, aby pojawiło się PO msecs, a nie wcześniej.
wim

1
A także, jeśli używasz zegara 12-godzinnego, który ma AMlubPM
DollarAkshay,

1
@wim jako kontynuacja mojego poprzedniego komentarza (nie mogłem już edytować ...), oto co zrobiłem: 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)
Mark

1
@Mark Nie możesz osadzić strefy czasowej w default_msec_format(od Pythona 3.7), ponieważ podstawiane są tylko czas i milisekundy. Ze loggingźródła:self.default_msec_format % (t, record.msecs)
M. Dudley

27

Dodanie msecs było lepszą opcją, dzięki. Oto moja poprawka wykorzystująca to w Pythonie 3.5.3 w Blenderze

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
Zdecydowanie najprostsza i najczystsza opcja. Nie jestem pewien, dlaczego otrzymujesz rejestrator, skoro możesz po prostu zadzwonić do logging.info (msg) itp., Ale format jest dokładnie tym, czego szukałem. Każdy, kto szuka wszystkich użytecznych atrybutów, może zajrzeć tutaj: docs.python.org/3.6/library/logging.html#logrecord-attributes
naphier

Hmmm ciekawa uwaga, dzięki za komentarz to na pewno do przemyślenia. Tak, prawdopodobnie właśnie dodałem to jako lekcję o tym, co się tam dzieje, i aby upewnić się, że tam jest, i ponieważ pytałem o wiele rzeczy, więc nie potrzeba wielu wywołań do rodzica (przez „.”), Aby go pobrać. Jeśli ponownie zadzwonisz .info lub .debug, może zapisałbym je ponownie bezpośrednio, tak jak sugerujesz, aby zapisać cykl wyszukiwania referencji. [let info = logging.info]
Master James

Dzięki za powiedzenie Jason. Czasami istnieje prostszy sposób, aby zobaczyć świat, nie bój się próbować odkryć tej prawdy w wielu, jeśli nie w żadnej / każdej sytuacji.
Master James

15

Najprostszym sposobem, jaki znalazłem, było zastąpienie default_msec_format:

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

1
Ciekawe, dzięki. Ale to nie zadziałało dla mnie w Pythonie 2.7. Może działać tylko w Pythonie 3.x dla pewnej wartości x.
nealmcb

1
@nealmcb to nie jest dostępne do czasu Pythona 3.3 według dokumentacji
Mark

3

Po utworzeniu instancji Formatterzwykle ustawiam formatter.converter = gmtime. Aby więc odpowiedź @ unutbu zadziałała w tym przypadku, potrzebujesz:

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

Prostym rozszerzeniem, które nie wymaga datetimemodułu i nie jest upośledzone, jak niektóre inne rozwiązania, jest użycie prostej zamiany ciągu, jak na przykład:

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

W ten sposób format daty można zapisać w dowolny sposób, nawet uwzględniając różnice między regionami, używając %Fmilisekund. Na przykład:

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

1

Jeśli używasz strzałki lub nie masz nic przeciwko używaniu strzałki. Formatowanie czasu w Pythonie można zastąpić formatowaniem czasu strzałki.

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)

Teraz możesz użyć całego formatowania czasu strzałki w datefmtatrybucie.


-1

tl; dr dla osób szukających tutaj daty w formacie ISO:

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


-3

W tej chwili poniższe działa doskonale z Pythonem 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')

daje następujący wynik

2020/01/11 18: 51: 19.011 INFO


1
To nie działa. % d drukuje datę. W twoim przykładzie data jest drukowana z 0 wypełnionym przed nią.
Klik
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.