Pencatatan python: gunakan milidetik dalam format waktu

163

Secara default logging.Formatter('%(asctime)s')mencetak dengan format berikut:

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

di mana 638 adalah milidetik. Saya perlu mengubah koma ke titik:

2011-06-09 10:54:40.638

Untuk memformat waktu yang dapat saya gunakan:

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

namun dokumentasi tidak menentukan cara memformat milidetik. Saya telah menemukan pertanyaan SO ini yang berbicara tentang mikrodetik, tetapi a) Saya lebih suka milidetik dan b) yang berikut ini tidak berfungsi pada Python 2.6 (yang sedang saya kerjakan) karena %f:

logging.Formatter(fmt='%(asctime)s',datefmt='%Y-%m-%d,%H:%M:%S.%f')
Jonathan
sumber
1
Mungkin mengubah lokal bisa membantu?
pajton
1
@ pajton - di tautan berikut ini tertulis "Informasi lokal tidak digunakan oleh asctime ()" - docs.python.org/library/time.html#time.asctime
Jonathan
%ftidak bekerja pada python 2.7.9 atau 3.5.1 baik
Antony Hatchkins
4
Percakapan yang baik di sini. Saya datang ke sini karena loggingmengklaim format waktu standarnya mengikuti ISO 8601. Tidak. Ini menggunakan ruang, bukan "T" untuk memisahkan waktu dan koma untuk detik pecahan, bukan titik desimal. Bagaimana mereka bisa salah?
LS

Jawaban:

76

Harap dicatat solusi Craig McDaniel jelas lebih baik.


formatTimemetode logging.Formatter terlihat seperti ini:

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

Perhatikan koma di "%s,%03d". Ini tidak dapat diperbaiki dengan menentukan a datefmtkarena ctis a time.struct_timedan objek-objek ini tidak merekam milidetik.

Jika kita mengubah definisi ctuntuk menjadikannya datetimeobjek, bukan a struct_time, maka (setidaknya dengan versi Python modern) kita dapat memanggil ct.strftimedan kemudian kita dapat gunakan %funtuk memformat mikrodetik:

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.

Atau, untuk mendapatkan milidetik, ubah koma ke titik desimal, dan hilangkan datefmtargumen:

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
sumber
1
jadi% f benar-benar akan memberikan mikrodetik, bukan milidetik, kan?
Jonathan
@ Jonathan: oops, Anda benar, %fmemberikan mikrodetik. Saya kira cara termudah untuk mendapatkan milidetik adalah dengan mengubah koma ke titik desimal (lihat edit di atas).
unutbu
3
Saya benar-benar berpikir ini adalah jawaban terbaik karena itu membuat Anda segera kembali untuk dapat menggunakan opsi format STANDAR. Saya sebenarnya ingin mikrodetik, dan ini adalah satu-satunya pilihan yang bisa melakukannya!
trumpetlicks
Terima kasih. Jawaban ini memberikan solusi mudah untuk mendapatkan mikrodetik.
Yongwei Wu
337

Ini juga harus bekerja:

logging.Formatter(fmt='%(asctime)s.%(msecs)03d',datefmt='%Y-%m-%d,%H:%M:%S')
Craig McDaniel
sumber
12
Terima kasih: Ini adalah dokumen untuk ini: docs.python.org/2/library/logging.html#logrecord-attributes docs.python.org/3/library/logging.html#logrecord-attributes .. Apakah ada cara untuk masih termasuk zona waktu (% z)? ... Format ISO8601 kali dalam Python log (, ->.) Akan lebih bagus.
Wes Turner
19
Solusi ini cacat, karena jika Anda memiliki %zatau %Zdi Anda datefmtAnda ingin itu muncul SETELAH msecs, bukan sebelumnya.
wim
1
Dan juga jika Anda menggunakan jam 12 jam yang memiliki AMatauPM
DollarAkshay
1
@wim sebagai tindak lanjut dari komentar saya sebelumnya (tidak dapat mengedit lagi ...), inilah yang telah saya lakukan: 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
@ Markus Anda tidak dapat menyematkan zona waktu dalam default_msec_format(pada Python 3.7) karena hanya waktu dan milidetik yang dimasukkan. Dari loggingsumber:self.default_msec_format % (t, record.msecs)
M. Dudley
27

Menambahkan msec adalah opsi yang lebih baik, Terima kasih. Ini amandemen saya menggunakan ini dengan Python 3.5.3 di 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")
Tuan James
sumber
1
Sejauh ini pilihan paling sederhana dan terbersih. Tidak yakin mengapa Anda mendapatkan logger ketika Anda bisa memanggil logging.info (msg) dll, tetapi formatnya persis seperti yang saya cari. Siapa pun yang mencari semua atribut yang dapat digunakan dapat melihat di sini: docs.python.org/3.6/library/logging.html#logrecord-attributes
naphier
Hmmm poin yang menarik, terima kasih untuk komentar itu adalah makanan untuk dipikirkan pasti. Ya saya mungkin baru saja menambahkannya sebagai pelajaran dalam apa yang terjadi di sana juga, dan untuk memastikan itu ada di sana dan karena saya telah menanyakan banyak hal sehingga tidak perlu banyak panggilan ke orang tua (via '.') Untuk mengambilnya. Jika Anda memanggil .info atau .debug lagi, saya mungkin akan menyimpannya lagi secara langsung saat Anda menyarankan untuk menyimpan referensi mencari siklus. [let info = logging.info]
Master James
Terima kasih sudah mengatakan Jason. Terkadang ada cara yang lebih sederhana untuk melihat dunia, jangan takut untuk mencoba dan menemukan kebenaran itu di banyak jika tidak ada / setiap situasi.
Tuan James
15

Cara paling sederhana yang saya temukan adalah menimpa default_msec_format:

formatter = logging.Formatter('%(asctime)s')
formatter.default_msec_format = '%s.%03d'
Mickey B
sumber
1
Menarik, terima kasih. Tapi ini tidak berhasil untuk saya di Python 2.7. Ini hanya dapat bekerja di Python 3.x untuk beberapa nilai x.
nealmcb
1
@nealmcb ini tidak tersedia hingga Python 3.3 per dokumen
Mark
3

Setelah instantiating Formattersaya biasanya mengatur formatter.converter = gmtime. Jadi agar jawaban @ unutbu berfungsi dalam hal ini, Anda perlu:

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

Perluasan sederhana yang tidak memerlukan datetimemodul dan tidak cacat seperti beberapa solusi lain adalah dengan menggunakan penggantian string sederhana seperti:

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

Dengan cara ini format tanggal dapat ditulis sesuai keinginan Anda, bahkan memungkinkan untuk perbedaan wilayah, dengan menggunakan %Fselama milidetik. Sebagai contoh:

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

Jika Anda menggunakan panah atau jika Anda tidak keberatan menggunakan panah. Anda dapat mengganti format waktu python dengan yang panah.

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)

Sekarang Anda dapat menggunakan semua format waktu panah di datefmtatribut.

Slapy
sumber
-1

tl; dr untuk orang yang mencari tanggal diformat ISO di sini:

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

Jeff Bryner
sumber
-3

Sampai sekarang berikut ini berfungsi dengan sempurna dengan 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')

memberikan hasil sebagai berikut

2020/01/11 18: 51: 19.011 INFO

Pasindu Madushan
sumber
1
Ini tidak bekerja. % d sedang mencetak tanggal. Dalam contoh Anda tanggal dicetak dengan 0 padded di depannya.
Klik