Python-Protokollierung: Verwenden Sie Millisekunden im Zeitformat

163

Standardmäßig wird logging.Formatter('%(asctime)s')im folgenden Format gedruckt:

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

wobei 638 die Millisekunde ist. Ich muss das Komma in einen Punkt ändern:

2011-06-09 10:54:40.638

So formatieren Sie die Zeit, die ich verwenden kann:

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

In der Dokumentation wird jedoch nicht angegeben, wie Millisekunden formatiert werden sollen. Ich habe diese SO-Frage gefunden, die sich mit Mikrosekunden befasst, aber a) ich würde Millisekunden bevorzugen und b) das Folgende funktioniert unter Python 2.6 (an dem ich arbeite) nicht, weil %f:

logging.Formatter(fmt='%(asctime)s',datefmt='%Y-%m-%d,%H:%M:%S.%f')
Jonathan
quelle
1
Vielleicht könnte das Ändern des Gebietsschemas helfen?
Pyjton
1
@ pajton - im folgenden Link steht "Gebietsschemainformationen werden von asctime () nicht verwendet" - docs.python.org/library/time.html#time.asctime
Jonathan
%ffunktioniert auch nicht mit Python 2.7.9 oder 3.5.1
Antony Hatchkins
4
Gutes Gespräch hier. Ich bin hierher gekommen, weil loggingbehauptet wird, dass das Standardzeitformat ISO 8601 entspricht. Es verwendet Leerzeichen, nicht "T", um Zeit und Komma für Sekundenbruchteile zu trennen, nicht Dezimalpunkt. Wie konnten sie so falsch sein?
LS

Antworten:

76

Bitte beachten Sie, dass die Lösung von Craig McDaniel deutlich besser ist.


Die formatTimeMethode von logging.Formatter sieht folgendermaßen aus:

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

Beachten Sie das Komma in "%s,%03d". Dies kann nicht durch Angabe von a behoben werden, datefmtda a ctist time.struct_timeund diese Objekte keine Millisekunden aufzeichnen.

Wenn wir die Definition von ändern ct, um es zu einem datetimeObjekt anstelle von a zu machen, können wir struct_time(zumindest bei modernen Versionen von Python) aufrufen ct.strftimeund dann %fMikrosekunden formatieren:

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.

Um Millisekunden zu erhalten, ändern Sie das Komma in einen Dezimalpunkt und lassen Sie das datefmtArgument weg :

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
quelle
1
% f würde also tatsächlich Mikrosekunden ergeben, nicht Millisekunden, oder?
Jonathan
@ Jonathan: Ups, du hast recht, %fgibt Mikrosekunden. Ich nehme an, der einfachste Weg, Millisekunden zu erhalten, besteht darin, das Komma in einen Dezimalpunkt zu ändern (siehe oben).
Unutbu
3
Ich denke tatsächlich, dass dies die beste Antwort ist, da Sie sofort wieder in der Lage sind, STANDARD-Formatierungsoptionen zu verwenden. Ich wollte eigentlich Mikrosekunden, und dies war die einzige Option, die das konnte!
Trompetenlicks
Vielen Dank. Diese Antwort bietet eine einfache Lösung, um Mikrosekunden zu erhalten.
Yongwei Wu
336

Das sollte auch funktionieren:

logging.Formatter(fmt='%(asctime)s.%(msecs)03d',datefmt='%Y-%m-%d,%H:%M:%S')
Craig McDaniel
quelle
12
Danke: Hier sind die Dokumente für diese: docs.python.org/2/library/logging.html#logrecord-attributes docs.python.org/3/library/logging.html#logrecord-attributes .. Gibt es einen Weg zu noch die Zeitzone (% z) einschließen? ... ISO8601-Formatierungszeiten in Python-Protokollen (, ->.) Wären großartig.
Wes Turner
19
Diese Lösung ist behindert, denn wenn Sie %zoder %Zin Ihrem haben datefmt, möchten Sie, dass das NACH den ms erscheint, nicht vorher.
wim
1
Und auch, wenn Sie eine 12-Stunden-Uhr verwenden, die AModerPM
DollarAkshay
1
@wim als Folge meines vorherigen Kommentars (konnte nicht mehr bearbeitet werden ...), hier ist, was ich getan habe: 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 Sie können die Zeitzone nicht in default_msec_format(ab Python 3.7) einbetten, da nur die Zeit und Millisekunden ersetzt werden. Aus der loggingQuelle:self.default_msec_format % (t, record.msecs)
M. Dudley
27

Das Hinzufügen von ms war die bessere Option. Danke. Hier ist meine Änderung, die dies mit Python 3.5.3 in Blender verwendet

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")
Meister James
quelle
1
Mit Abstand die einfachste und sauberste Option. Ich bin mir nicht sicher, warum Sie den Logger erhalten, wenn Sie nur logging.info (msg) usw. aufrufen können, aber das Format ist genau das, wonach ich gesucht habe. Jeder andere, der nach allen verwendbaren Attributen sucht, kann hier nachsehen
naphier
Hmmm interessanter Punkt, danke für den Kommentar, es ist sicher ein Denkanstoß. Ja, ich habe es wahrscheinlich nur als Lektion hinzugefügt, was dort auch vor sich geht, und um sicherzugehen, dass es dort ist und weil ich mehrere Dinge gefragt habe, damit es nicht mehrere Anrufe an die Eltern (über '.') Benötigt, um es abzurufen. Wenn Sie erneut .info oder .debug aufrufen, würde ich diese möglicherweise direkt erneut speichern, da Sie vorschlagen, einen Referenz-Suchzyklus zu speichern. [let info = logging.info]
Meister James
Danke, dass du Jason gesagt hast. Manchmal gibt es eine einfachere Möglichkeit, die Welt zu sehen. Haben Sie keine Angst, diese Wahrheit in vielen, wenn nicht in jeder Situation zu entdecken.
Meister James
15

Der einfachste Weg, den ich gefunden habe, war das Überschreiben von default_msec_format:

formatter = logging.Formatter('%(asctime)s')
formatter.default_msec_format = '%s.%03d'
Mickey B.
quelle
1
Interessant, danke. In Python 2.7 hat dies bei mir jedoch nicht funktioniert. Es funktioniert möglicherweise nur in Python 3.x für einen Wert von x.
Nealmcb
1
@nealmcb Dies ist nicht verfügbar, bis Python 3.3 gemäß den Dokumenten
Mark
3

Nach dem Instanziieren eines Formattersetze ich normalerweise formatter.converter = gmtime. Damit die Antwort von @ unutbu in diesem Fall funktioniert, benötigen Sie:

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
quelle
2

Eine einfache Erweiterung, die das datetimeModul nicht benötigt und nicht wie einige andere Lösungen behindert ist, besteht darin, einen einfachen String-Ersatz wie folgt zu verwenden:

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

Auf diese Weise kann ein Datumsformat geschrieben werden, wie Sie möchten, auch unter Berücksichtigung von Regionsunterschieden, indem es %Ffür Millisekunden verwendet wird. Beispielsweise:

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
quelle
1

Wenn Sie Pfeil verwenden oder wenn es Ihnen nichts ausmacht, Pfeil zu verwenden. Sie können die Zeitformatierung von Python durch die von Pfeil ersetzen.

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)

Jetzt können Sie die gesamte Zeitformatierung des Pfeils im datefmtAttribut verwenden.

Slapy
quelle
-1

tl; dr für Leute, die hier nach einem ISO-formatierten Datum suchen:

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

Jeff Bryner
quelle
-3

Ab sofort funktioniert das Folgende perfekt mit 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')

gibt die folgende Ausgabe

2020/01/11 18: 51: 19.011 INFO

Pasindu Madushan
quelle
1
Das funktioniert nicht. % d druckt das Datum. In Ihrem Beispiel wird das Datum mit einer vorangestellten 0 gedruckt.
Klik