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')
Jonathan
źródło
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.
unutbu
źródło
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')
Craig McDaniel
źródło
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")
Mistrz James
źródło
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'
Mickey B.
źródło
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
Jonathan
źródło
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
torrentails
źródło
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.

Slapy
źródło
-1

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

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

Jeff Bryner
źródło
-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

Pasindu Madushan
źródło
1
To nie działa. % d drukuje datę. W twoim przykładzie data jest drukowana z 0 wypełnionym przed nią.
Klik