Journalisation Python: utilisez des millisecondes au format horaire

163

Par défaut, logging.Formatter('%(asctime)s')imprime avec le format suivant:

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

où 638 est la milliseconde. J'ai besoin de changer la virgule en point:

2011-06-09 10:54:40.638

Pour formater l'heure que je peux utiliser:

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

cependant la documentation ne spécifie pas comment formater les millisecondes. J'ai trouvé cette question SO qui parle de microsecondes, mais a) je préférerais les millisecondes et b) ce qui suit ne fonctionne pas sur Python 2.6 (sur lequel je travaille) en raison de %f:

logging.Formatter(fmt='%(asctime)s',datefmt='%Y-%m-%d,%H:%M:%S.%f')
Jonathan
la source
1
Peut-être que changer les paramètres régionaux pourrait vous aider?
pajton
1
@ pajton - dans le lien suivant, il est dit "Les informations locales ne sont pas utilisées par asctime ()" - docs.python.org/library/time.html#time.asctime
Jonathan
%fne fonctionne pas non plus sur python 2.7.9 ou 3.5.1
Antony Hatchkins
4
Bonne conversation ici. Je suis venu ici parce loggingque le format de l'heure par défaut suit ISO 8601. Ce n'est pas le cas. Il utilise l'espace, pas "T" pour séparer le temps et la virgule pour les fractions de seconde, pas la virgule décimale. Comment ont-ils pu se tromper?
LS

Réponses:

76

Veuillez noter que la solution de Craig McDaniel est clairement meilleure.


La formatTimeméthode de Formatter ressemble à ceci:

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

Remarquez la virgule "%s,%03d". Cela ne peut pas être résolu en spécifiant un datefmtcar ctest un time.struct_timeet ces objets n'enregistrent pas les millisecondes.

Si nous changeons la définition de ctpour en faire un datetimeobjet au lieu de a struct_time, alors (au moins avec les versions modernes de Python) nous pouvons appeler ct.strftimeet ensuite nous pouvons utiliser %fpour formater les microsecondes:

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.

Ou, pour obtenir des millisecondes, remplacez la virgule par une virgule décimale et omettez l' 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
la source
1
donc% f donnerait en fait des microsecondes, pas des millisecondes, non?
Jonathan
@Jonathan: oups, vous avez raison, %fdonne des microsecondes. Je suppose que le moyen le plus simple d'obtenir des millisecondes est de changer la virgule en un point décimal (voir la modification ci-dessus).
unutbu
3
Je pense en fait que c'est la meilleure réponse car cela vous permet de revenir à la possibilité d'utiliser les options de formatage STANDARD. Je voulais en fait des microsecondes, et c'était la seule option qui pouvait le faire!
trumpetlicks
Merci. Cette réponse donne une solution simple pour obtenir des microsecondes.
Yongwei Wu
338

Cela devrait également fonctionner:

logging.Formatter(fmt='%(asctime)s.%(msecs)03d',datefmt='%Y-%m-%d,%H:%M:%S')
Craig McDaniel
la source
12
Merci: Voici la documentation pour ceux-ci: docs.python.org/2/library/logging.html#logrecord-attributes docs.python.org/3/library/logging.html#logrecord-attributes .. Existe-t-il un moyen de inclure toujours le fuseau horaire (% z)? ... Les temps au format ISO8601 dans les journaux Python (, ->.) Seraient super.
Wes Turner
19
Cette solution est handicapée, car si vous avez %zou %Zdans votre datefmtvous voulez que cela apparaisse APRÈS les msecs, pas avant.
wim
1
Et aussi si vous utilisez une horloge de 12 heures qui a AMouPM
DollarAkshay
1
@wim comme suite à mon commentaire précédent (impossible de modifier plus ...), voici ce que j'ai fait: 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 Vous ne pouvez pas intégrer le fuseau horaire dans default_msec_format(à partir de Python 3.7) car seuls l'heure et les millisecondes sont remplacées. De la loggingsource:self.default_msec_format % (t, record.msecs)
M. Dudley
28

L'ajout de msecs était la meilleure option, merci. Voici mon amendement en utilisant ceci avec Python 3.5.3 dans 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")
Maître James
la source
1
De loin l'option la plus simple et la plus propre. Je ne sais pas pourquoi vous obtenez le logger alors que vous pouvez simplement appeler logging.info (msg), etc., mais le format est exactement ce que je recherchais. Quiconque recherche tous les attributs utilisables peut regarder ici: docs.python.org/3.6/library/logging.html#logrecord-attributes
naphier
Hmmm point intéressant, merci pour le commentaire, c'est matière à réflexion à coup sûr. Oui, je l'ai probablement juste ajouté comme une leçon sur ce qui se passe là-bas aussi, et pour m'assurer qu'il est là et parce que j'ai demandé plusieurs choses, donc il n'a pas besoin de plusieurs appels au parent (via '.') Pour le récupérer. Si vous appeliez à nouveau .info ou .debug, je les enregistrerais peut-être à nouveau directement comme vous suggérez d'enregistrer un cycle de recherche de références. [let info = logging.info]
Master James
Merci d'avoir dit Jason. Parfois, il existe un moyen plus simple de voir le monde, n'ayez pas peur d'essayer de découvrir cette vérité dans de nombreuses, sinon aucune / toutes les situations.
Master James
15

Le moyen le plus simple que j'ai trouvé était de remplacer default_msec_format:

formatter = logging.Formatter('%(asctime)s')
formatter.default_msec_format = '%s.%03d'
Mickey B
la source
1
Intéressant, merci. Mais cela n'a pas fonctionné pour moi dans Python 2.7. Cela ne peut fonctionner en Python 3.x que pour une valeur de x.
nealmcb
1
@nealmcb ce n'est pas disponible avant Python 3.3 selon la documentation
Mark
3

Après avoir instancié un, Formatterj'ai généralement défini formatter.converter = gmtime. Donc, pour que la réponse de @ unutbu fonctionne dans ce cas, vous aurez besoin de:

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
la source
2

Une extension simple qui ne nécessite pas le datetimemodule et qui n'est pas handicapée comme certaines autres solutions consiste à utiliser un simple remplacement de chaîne comme ceci:

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

De cette façon, un format de date peut être écrit comme vous le souhaitez, même en tenant compte des différences de région, en utilisant %Fpendant des millisecondes. Par exemple:

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
la source
1

Si vous utilisez la flèche ou si cela ne vous dérange pas d'utiliser la flèche. Vous pouvez remplacer le formatage de l'heure de python par celui de la flèche.

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)

Vous pouvez maintenant utiliser toute la mise en forme de l'heure de la flèche dans l' datefmtattribut.

Slapy
la source
-1

tl; dr pour les personnes qui recherchent ici une date au format ISO:

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

Jeff Bryner
la source
-3

À partir de maintenant, ce qui suit fonctionne parfaitement avec 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')

donne la sortie suivante

2020/01/11 18: 51: 19.011 INFO

Pasindu Madushan
la source
1
Cela ne fonctionne pas. % d imprime la date. Dans votre exemple, la date est imprimée avec un 0 rembourré devant.
Klik