Loguru adalah perpustakaan yang bertujuan untuk membawa penebangan yang menyenangkan di Python.
Apakah Anda pernah merasa malas tentang mengonfigurasi logger dan bekas print()
sebagai gantinya? ... Saya lakukan, namun logging sangat mendasar untuk setiap aplikasi dan memudahkan proses debugging. Menggunakan Loguru Anda tidak punya alasan untuk tidak menggunakan logging dari awal, ini sesederhana from loguru import logger
.
Juga, perpustakaan ini dimaksudkan untuk membuat logging Python kurang menyakitkan dengan menambahkan banyak fungsi yang berguna yang memecahkan peringatan dari logger standar. Menggunakan log dalam aplikasi Anda harus automatisme, Loguru mencoba membuatnya menyenangkan dan kuat.
pip install loguru
Konsep utama Loguru adalah bahwa ada satu dan hanya satu logger
.
Untuk kenyamanan, itu sudah dikonfigurasi sebelumnya dan output ke stderr
untuk memulai (tapi itu sepenuhnya dapat dikonfigurasi).
from loguru import logger
logger . debug ( "That's it, beautiful and simple logging!" )
logger
hanyalah antarmuka yang mengirimkan pesan log ke penangan yang dikonfigurasi. Sederhana, kan?
Bagaimana cara menambahkan pawang? Bagaimana cara mengatur pemformatan log? Bagaimana cara memfilter pesan? Bagaimana cara mengatur level?
Satu Jawaban: Fungsi add()
.
logger . add ( sys . stderr , format = "{time} {level} {message}" , filter = "my_module" , level = "INFO" )
Fungsi ini harus digunakan untuk mendaftarkan wastafel yang bertanggung jawab untuk mengelola pesan log yang dikontekstualisasikan dengan dikt. Wastafel dapat mengambil banyak bentuk: fungsi sederhana, jalur string, objek seperti file, fungsi coroutine atau pawang bawaan.
Perhatikan bahwa Anda juga dapat remove()
penangan yang ditambahkan sebelumnya dengan menggunakan pengidentifikasi yang dikembalikan saat menambahkannya. Ini sangat berguna jika Anda ingin menggantikan pawang stderr
default: Cukup hubungi logger.remove()
untuk membuat awal yang baru.
Jika Anda ingin mengirim pesan yang dicatat ke file, Anda hanya perlu menggunakan jalur string sebagai wastafel. Ini dapat diatur secara otomatis juga untuk kenyamanan:
logger . add ( "file_{time}.log" )
Ini juga mudah dikonfigurasi jika Anda membutuhkan logger berputar, jika Anda ingin menghapus log yang lebih lama, atau jika Anda ingin mengompres file Anda saat penutupan.
logger . add ( "file_1.log" , rotation = "500 MB" ) # Automatically rotate too big file
logger . add ( "file_2.log" , rotation = "12:00" ) # New file is created each day at noon
logger . add ( "file_3.log" , rotation = "1 week" ) # Once the file is too old, it's rotated
logger . add ( "file_X.log" , retention = "10 days" ) # Cleanup after some time
logger . add ( "file_Y.log" , compression = "zip" ) # Save some loved space
Loguru mendukung pemformatan {}
%
yang jauh lebih elegan dan kuat, fungsi logging sebenarnya setara dengan str.format()
.
logger . info ( "If you're using Python {}, prefer {feature} of course!" , 3.6 , feature = "f-strings" )
Pernahkah Anda melihat program Anda mogok secara tak terduga tanpa melihat apa pun di file log? Apakah Anda pernah memperhatikan bahwa pengecualian yang terjadi di utas tidak dicatat? Ini dapat diselesaikan dengan menggunakan Dekorator / Manajer Konteks catch()
yang memastikan bahwa kesalahan apa pun disebarkan dengan benar kepada logger
.
@ logger . catch
def my_function ( x , y , z ):
# An error? It's caught anyway!
return 1 / ( x + y + z )
Loguru secara otomatis menambahkan warna ke log Anda jika terminal Anda kompatibel. Anda dapat mendefinisikan gaya favorit Anda dengan menggunakan tag markup dalam format wastafel.
logger . add ( sys . stdout , colorize = True , format = "<green>{time}</green> <level>{message}</level>" )
Semua wastafel yang ditambahkan ke logger
aman-utas secara default. Mereka tidak aman multiproses, tetapi Anda dapat enqueue
pesan untuk memastikan integritas log. Argumen yang sama ini juga dapat digunakan jika Anda ingin penebangan async.
logger . add ( "somefile.log" , enqueue = True )
Fungsi coroutine yang digunakan sebagai wastafel juga didukung dan harus ditunggu dengan complete()
.
Pengecualian logging yang terjadi dalam kode Anda penting untuk melacak bug, tetapi tidak berguna jika Anda tidak tahu mengapa itu gagal. Loguru membantu Anda mengidentifikasi masalah dengan memungkinkan seluruh jejak tumpukan ditampilkan, termasuk nilai variabel (terima kasih better_exceptions
untuk ini!).
Kode:
# Caution, "diagnose=True" is the default and may leak sensitive data in prod
logger . add ( "out.log" , backtrace = True , diagnose = True )
def func ( a , b ):
return a / b
def nested ( c ):
try :
func ( 5 , c )
except ZeroDivisionError :
logger . exception ( "What?!" )
nested ( 0 )
Akan menghasilkan:
2018-07-17 01: 38: 43.975 | Kesalahan | __main __: bersarang: 10 - apa?!
Traceback (panggilan terbaru terakhir):
File "test.py", baris 12, di <module>
bersarang (0)
└ <Fungsi bersarang di 0x7f5c755322f0>
> File "test.py", baris 8, di bersarang
func (5, c)
│ └ 0
└ <Function func di 0x7f5c79fc2e18>
File "test.py", baris 4, di func
mengembalikan a / b
│ └ 0
└ 5
ZerodivisionError: Divisi dengan nol
Perhatikan bahwa fitur ini tidak akan berfungsi pada Python Rept default karena data bingkai yang tidak tersedia.
Lihat juga: Pertimbangan Keamanan Saat Menggunakan Loguru.
Ingin log Anda diserialisasi agar lebih mudah dikurangkan atau melewatinya? Menggunakan argumen serialize
, setiap pesan log akan dikonversi ke string JSON sebelum dikirim ke wastafel yang dikonfigurasi.
logger . add ( custom_sink_function , serialize = True )
Menggunakan bind()
Anda dapat mengontekstualisasikan pesan logger Anda dengan memodifikasi atribut catatan tambahan.
logger . add ( "file.log" , format = "{extra[ip]} {extra[user]} {message}" )
context_logger = logger . bind ( ip = "192.168.0.1" , user = "someone" )
context_logger . info ( "Contextualize your logger easily" )
context_logger . bind ( user = "someone_else" ). info ( "Inline binding of extra attribute" )
context_logger . info ( "Use kwargs to add context during formatting: {user}" , user = "anybody" )
Dimungkinkan untuk memodifikasi keadaan konteks-lokal sementara dengan contextualize()
:
with logger . contextualize ( task = task_id ):
do_something ()
logger . info ( "End of task" )
Anda juga dapat memiliki lebih banyak kontrol berbutir halus atas log Anda dengan menggabungkan bind()
dan filter
:
logger . add ( "special.log" , filter = lambda record : "special" in record [ "extra" ])
logger . debug ( "This message is not logged to the file" )
logger . bind ( special = True ). info ( "This message, though, is logged to the file!" )
Akhirnya, metode patch()
memungkinkan nilai dinamis dilampirkan pada dikt rekaman setiap pesan baru:
logger . add ( sys . stderr , format = "{extra[utc]} {message}" )
logger = logger . patch ( lambda record : record [ "extra" ]. update ( utc = datetime . utcnow ()))
Kadang -kadang Anda ingin mencatat informasi verbose tanpa penalti kinerja dalam produksi, Anda dapat menggunakan metode opt()
untuk mencapai ini.
logger . opt ( lazy = True ). debug ( "If sink level <= DEBUG: {x}" , x = lambda : expensive_function ( 2 ** 64 ))
# By the way, "opt()" serves many usages
logger . opt ( exception = True ). info ( "Error stacktrace added to the log message (tuple accepted too)" )
logger . opt ( colors = True ). info ( "Per message <blue>colors</blue>" )
logger . opt ( record = True ). info ( "Display values from the record (eg. {record[thread]})" )
logger . opt ( raw = True ). info ( "Bypass sink formatting n " )
logger . opt ( depth = 1 ). info ( "Use parent stack context (useful within wrapped functions)" )
logger . opt ( capture = False ). info ( "Keyword arguments not added to {dest} dict" , dest = "extra" )
Loguru hadir dengan semua level logging standar yang ditambahkan oleh trace()
dan success()
. Apakah Anda membutuhkan lebih banyak? Kemudian, cukup buat dengan menggunakan fungsi level()
.
new_level = logger . level ( "SNAKY" , no = 38 , color = "<yellow>" , icon = "?" )
logger . log ( "SNAKY" , "Here we go!" )
Pencatatan standar dibengkak dengan argumen seperti datefmt
atau msecs
, %(asctime)s
dan %(created)s
, datetime naif tanpa informasi zona waktu, bukan pemformatan intuitif, dll. Loguru memperbaikinya:
logger . add ( "file.log" , format = "{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}" )
Menggunakan logger di skrip Anda mudah, dan Anda dapat configure()
di awal. Untuk menggunakan Loguru dari dalam perpustakaan, ingatlah untuk tidak pernah memanggil add()
tetapi gunakan disable()
sebagai gantinya sehingga fungsi logging menjadi tidak ada op. Jika pengembang ingin melihat log perpustakaan Anda, mereka dapat enable()
lagi.
# For scripts
config = {
"handlers" : [
{ "sink" : sys . stdout , "format" : "{time} - {message}" },
{ "sink" : "file.log" , "serialize" : True },
],
"extra" : { "user" : "someone" }
}
logger . configure ( ** config )
# For libraries, should be your library's `__name__`
logger . disable ( "my_library" )
logger . info ( "No matter added sinks, this message is not displayed" )
# In your application, enable the logger in the library
logger . enable ( "my_library" )
logger . info ( "This message however is propagated to the sinks" )
Untuk kenyamanan tambahan, Anda juga dapat menggunakan pustaka loguru-config
untuk mengatur logger
langsung dari file konfigurasi.
Ingin menggunakan Handler
logging bawaan sebagai wastafel loguru?
handler = logging . handlers . SysLogHandler ( address = ( 'localhost' , 514 ))
logger . add ( handler )
Perlu menyebarkan pesan Loguru ke logging standar?
class PropagateHandler ( logging . Handler ):
def emit ( self , record : logging . LogRecord ) -> None :
logging . getLogger ( record . name ). handle ( record )
logger . add ( PropagateHandler (), format = "{message}" )
Ingin mencegat pesan logging standar ke arah wastafel loguru Anda?
class InterceptHandler ( logging . Handler ):
def emit ( self , record : logging . LogRecord ) -> None :
# Get corresponding Loguru level if it exists.
level : str | int
try :
level = logger . level ( record . levelname ). name
except ValueError :
level = record . levelno
# Find caller from where originated the logged message.
frame , depth = inspect . currentframe (), 0
while frame and ( depth == 0 or frame . f_code . co_filename == logging . __file__ ):
frame = frame . f_back
depth += 1
logger . opt ( depth = depth , exception = record . exc_info ). log ( level , record . getMessage ())
logging . basicConfig ( handlers = [ InterceptHandler ()], level = 0 , force = True )
Tidak suka pemformatan logger default? Lebih suka warna DEBUG
lain? Tidak masalah:
# Linux / OSX
export LOGURU_FORMAT = "{time} | <lvl>{message}</lvl>"
# Windows
setx LOGURU_DEBUG_COLOR "<green>"
Seringkali berguna untuk mengekstrak informasi spesifik dari log yang dihasilkan, inilah sebabnya Loguru menyediakan metode parse()
yang membantu menangani log dan regex.
pattern = r"(?P<time>.*) - (?P<level>[0-9]+) - (?P<message>.*)" # Regex with named groups
caster_dict = dict ( time = dateutil . parser . parse , level = int ) # Transform matching groups
for groups in logger . parse ( "file.log" , pattern , cast = caster_dict ):
print ( "Parsed:" , groups )
# {"level": 30, "message": "Log example", "time": datetime(2018, 12, 09, 11, 23, 55)}
Loguru dapat dengan mudah dikombinasikan dengan perpustakaan Great notifiers
(harus diinstal secara terpisah) untuk menerima email ketika program Anda gagal secara tak terduga atau mengirim banyak jenis pemberitahuan lainnya.
import notifiers
params = {
"username" : "[email protected]" ,
"password" : "abc123" ,
"to" : "[email protected]"
}
# Send a single notification
notifier = notifiers . get_notifier ( "gmail" )
notifier . notify ( message = "The application is running!" , ** params )
# Be alerted on each error message
from notifiers . logging import NotificationHandler
handler = NotificationHandler ( "gmail" , defaults = params )
logger . add ( handler , level = "ERROR" )
Meskipun dampak logging pada kinerja dalam banyak kasus dapat diabaikan, logger tanpa biaya akan memungkinkan untuk menggunakannya di mana saja tanpa banyak perhatian. Dalam rilis yang akan datang, fungsi kritis Loguru akan diimplementasikan dalam C untuk kecepatan maksimum.