Pahami program Anda tanpa mengorbankan kinerja

Penjurnalan adalah bagian yang sangat penting dari pengembangan perangkat lunak. Ini membantu pengembang lebih memahami pelaksanaan program dan menilai cacat dan kegagalan tak terduga. Pesan log dapat menyimpan informasi seperti status program saat ini atau di mana program itu dijalankan. Jika terjadi kesalahan, pengembang dapat dengan cepat menemukan baris kode yang menyebabkan masalah dan bertindak sesuai.
Python logging . , .
logging
, , logging.
, , . logger = logging.getLogger(name). β name . name . , , .
, . , β (. .: formatter) (. .: handler).
. β LogRecord ( β ). , , , , , . :
: :
# : WARNING:root: !
:
"%(asctime)s - [%(levelname)s] - %(name)s - (%(filename)s).%(funcName)s(%(lineno)d) - %(message)s"
# 2020-07-26 23:37:15,374 - [INFO] - __main__ - (main.py).main(18) - !
. . , logging . β FileHandler, , StreamHandler, , sys.stderr sys.stdout. . , sys.stderr. , .
, FileHandler WARNING (. .: ) StreamHandler INFO (. .: ). , sys.stdout, .
:
main.py, package1.py, app_logger.py. app_logger.py get_logger, . : StreamHandler INFO FileHandler WARNING. INFO DEBUG ( β WARNING), , WARNING, . main.py, package1.py, get_logger, .
Xiaoxu Gao
# app_logger.py
import logging
_log_format = f"%(asctime)s - [%(levelname)s] - %(name)s - (%(filename)s).%(funcName)s(%(lineno)d) - %(message)s"
def get_file_handler():
file_handler = logging.FileHandler("x.log")
file_handler.setLevel(logging.WARNING)
file_handler.setFormatter(logging.Formatter(_log_format))
return file_handler
def get_stream_handler():
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
stream_handler.setFormatter(logging.Formatter(_log_format))
return stream_handler
def get_logger(name):
logger = logging.getLogger(name)
logger.setLevel(logging.INFO)
logger.addHandler(get_file_handler())
logger.addHandler(get_stream_handler())
return logger
# package1.py
import app_logger
logger = app_logger.get_logger(__name__)
def process(msg):
logger.info(" ")
print(msg)
logger.info(" ")
# main.py
import package1
import app_logger
logger = app_logger.get_logger(__name__)
def main():
logger.info(" ")
package1.process(msg="")
logger.warning(" , ")
logger.info(" ")
if __name__ == "__main__":
main()
# 2020-07-25 21:06:06,657 - [INFO] - __main__ - (main.py).main(8) -
# 2020-07-25 21:06:06,658 - [INFO] - package1 - (package1.py).process(7) -
#
# 2020-07-25 21:06:06,658 - [INFO] - package1 - (package1.py).process(9) -
# 2020-07-25 21:06:06,658 - [WARNING] - __main__ - (main.py).main(10) - ,
# 2020-07-25 21:06:06,658 - [INFO] - __main__ - (main.py).main(11) -
# cat x.log
# 2020-07-25 21:06:06,658 - [WARNING] - __main__ - (main.py).main(10) - ,
basic-logging.py
INFO (sys.stdout), , WARNING . , , .
1. LogRecord, LoggerAdapter
, LogRecord . . , logging LogRecord .
β LoggerAdapter. , ( ). , Logger, logger.info.
- , , LoggerAdapter (. .: , -, ). . . app, , .
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - [%(levelname)s] - %(app)s - %(name)s - (%(filename)s).%(funcName)s(%(lineno)d) - %(message)s",
)
logger = logging.getLogger(__name__)
logger = logging.LoggerAdapter(logger, {"app": " "})
logger.info(" ")
logger.info(" ")
# 2020-07-25 21:36:20,709 - [INFO] - - __main__ - (main.py).main(8) -
# 2020-07-25 21:36:20,709 - [INFO] - - __main__ - (main.py).main(11) -
logging_adapter_fixed_value.py
, , , - . LoggerAdapter . process() β , . id, . .
import logging
class CustomAdapter(logging.LoggerAdapter):
def process(self, msg, kwargs):
my_context = kwargs.pop('id', self.extra['id'])
return '[%s] %s' % (my_context, msg), kwargs
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - [%(levelname)s] - %(name)s - (%(filename)s).%(funcName)s(%(lineno)d) - %(message)s",
)
logger = logging.getLogger(__name__)
logger = CustomAdapter(logger, {"id": None})
logger.info('ID ', id='1234')
logger.info('ID ', id='5678')
logger.info(' ID')
# 2020-07-25 22:12:06,715 - [INFO] - __main__ - (main.py).<module>(38) - [1234] ID # 2020-07-25 22:12:06,715 - [INFO] - __main__ - (main.py).<module>(38) - [1234] ID
# 2020-07-25 22:21:31,568 - [INFO] - __main__ - (main.py).<module>(39) - [5678] ID # 2020-07-25 22:21:31,568 - [INFO] - __main__ - (main.py).<module>(39) - [5678] ID
# 2020-07-25 22:12:06,715 - [INFO] - __main__ - (main.py).<module>(39) - [None] ID# 2020-07-25 22:12:06,715 - [INFO] - __main__ - (main.py).<module>(39) - [None] ID
logging_adapter_dynamic_value.py
2. LogRecord, Filter
β Filter. , . , . , , filter().
color (. .: ) filter(), . .
import logging
class CustomFilter(logging.Filter):
COLOR = {
"DEBUG": "GREEN",
"INFO": "GREEN",
"WARNING": "YELLOW",
"ERROR": "RED",
"CRITICAL": "RED",
}
def filter(self, record):
record.color = CustomFilter.COLOR[record.levelname]
return True
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - [%(levelname)s] - [%(color)s] - %(name)s - (%(filename)s).%(funcName)s(%(lineno)d) - %(message)s",
)
logger = logging.getLogger(__name__)
logger.addFilter(CustomFilter())
logger.debug(" , β ")
logger.info(" , β ")
logger.warning(" , β ")
logger.error(" , β ")
logger.critical(" , β ")
# 2020-07-25 22:45:17,178 - [DEBUG] - [GREEN] - __main__ - (main.py).<module>(52) - , β
# 2020-07-25 22:45:17,179 - [INFO] - [GREEN] - __main__ - (main.py).<module>(53) - , β
# 2020-07-25 22:45:17,179 - [WARNING] - [YELLOW] - __main__ - (main.py).<module>(54) - , β
# 2020-07-25 22:45:17,179 - [ERROR] - [RED] - __main__ - (main.py).<module>(55) - , β
# 2020-07-25 22:45:17,179 - [CRITICAL] - [RED] - __main__ - (main.py).<module>(56) - , β
logging_filter_dynamic_attributes.py
3. logging
logging , . , MainThread WorkThread . threadName .
import logging
import threading
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - [%(levelname)s] - [%(threadName)s] - %(name)s - (%(filename)s).%(funcName)s(%(lineno)d) - %(message)s",
)
logger = logging.getLogger(__name__)
def worker():
for i in range(5):
logger.info(f" {i} ")
thread = threading.Thread(target=worker)
thread.start()
for i in range(5):
logger.info(f" {i} ")
thread.join()
# 2020-07-26 15:33:21,078 - [INFO] - [Thread-1] - __main__ - (main.py).worker(62) - 0-
# 2020-07-26 15:33:21,078 - [INFO] - [Thread-1] - __main__ - (main.py).worker(62) - 1-
# 2020-07-26 15:33:21,078 - [INFO] - [Thread-1] - __main__ - (main.py).worker(62) - 2-
# 2020-07-26 15:33:21,078 - [INFO] - [Thread-1] - __main__ - (main.py).worker(62) - 3-
# 2020-07-26 15:33:21,078 - [INFO] - [Thread-1] - __main__ - (main.py).worker(62) - 4-
# 2020-07-26 15:33:21,078 - [INFO] - [MainThread] - __main__ - (main.py).<module>(69) - 0-
# 2020-07-26 15:33:21,078 - [INFO] - [MainThread] - __main__ - (main.py).<module>(69) - 1-
# 2020-07-26 15:33:21,078 - [INFO] - [MainThread] - __main__ - (main.py).<module>(69) - 2-
# 2020-07-26 15:33:21,078 - [INFO] - [MainThread] - __main__ - (main.py).<module>(69) - 3-
# 2020-07-26 15:33:21,078 - [INFO] - [MainThread] - __main__ - (main.py).<module>(69) - 4-
logging_multi_threading.py
logging threading.RLock() . RLock Lock:
Lock , . , RLock , .
Lock , RLock β , .
, Handler, handle(), . Handler.handle(). , . emit() - .
def handle(self, record):
"""
.
, .
/
-. , ,
.
"""
rv = self.filter(record)
if rv:
self.acquire()
try:
self.emit(record)
finally:
self.release()
return rv
logging_handle.py
4. logging β QueueHandler
, logging , . , , . logging, .
QueueHandler + Β«-Β»
β QueueHandler. , multiprocessing.Queue . 2 Β«-Β», Β«-Β», .
, , , log_processor logger.log(record.levelno, record.msg) logger.info() logger.warning(). (. .: main) , log_processor . β , logging .
import os
from logging.handlers import QueueHandler
formatter = "%(asctime)s - [%(levelname)s] - %(name)s - (%(filename)s).%(funcName)s(%(lineno)d) - %(message)s"
logging.basicConfig(level=logging.INFO)
def log_processor(queue):
logger = logging.getLogger(__name__)
file_handler = logging.FileHandler("a.log")
file_handler.setFormatter(logging.Formatter(formatter))
logger.addHandler(file_handler)
while True:
try:
record = queue.get()
if record is None:
break
logger.log(record.levelno, record.msg)
except Exception as e:
pass
def log_producer(queue):
pid = os.getpid()
logger = logging.getLogger(__name__)
queue_handler = QueueHandler(queue)
logger.addHandler(QueueHandler(queue))
logger.info(f" INFO - {pid}")
logger.warning(f" WARNING - {pid}")
def main():
queue = multiprocessing.Queue(-1)
listener = multiprocessing.Process(target=log_processor, args=(queue,))
listener.start()
workers = []
for i in range(2):
worker = multiprocessing.Process(target=log_producer, args=(queue,))
workers.append(worker)
worker.start()
for w in workers:
w.join()
queue.put_nowait(None)
listener.join()
if __name__ == '__main__':
main()
# >> cat a.log
# 2020-07-26 18:38:10,525 - [INFO] - __mp_main__ - (main.py).log_processer(118) - INFO - 32242
# 2020-07-26 18:38:10,525 - [WARNING] - __mp_main__ - (main.py).log_processer(118) - WARNING - 32242
# 2020-07-26 18:38:10,530 - [INFO] - __mp_main__ - (main.py).log_processer(118) - INFO - 32243
# 2020-07-26 18:38:10,530 - [WARNING] - __mp_main__ - (main.py).log_processer(118) - WARNING - 32243
logging_queue_handler.py
QueueHandler + QueueListener
logging.handlers QueueListener. . QueueListener , , listener. .
def log_producer(queue):
pid = os.getpid()
logger = logging.getLogger(__name__)
logger.addHandler(QueueHandler(queue))
logger.info(f" INFO - {pid}")
logger.warning(f" WARNING - {pid}")
def main():
queue = multiprocessing.Queue(-1)
file_handler = logging.FileHandler("b.log")
file_handler.setFormatter(logging.Formatter(formatter))
queue_listener = QueueListener(queue, file_handler)
queue_listener.start()
workers = []
for i in range(2):
worker = multiprocessing.Process(target=log_producer, args=(queue,))
workers.append(worker)
worker.start()
for w in workers:
w.join()
queue_listener.stop()
if __name__ == '__main__':
main()
# >> cat b.log
# 2020-07-26 20:15:58,656 - [INFO] - __mp_main__ - (main.py).log_producer(130) - INFO - 34199
# 2020-07-26 20:15:58,656 - [WARNING] - __mp_main__ - (main.py).log_producer(131) - WARNING - 34199
# 2020-07-26 20:15:58,662 - [INFO] - __mp_main__ - (main.py).log_producer(130) - INFO - 34200
# 2020-07-26 20:15:58,662 - [WARNING] - __mp_main__ - (main.py).log_producer(131) - WARNING - 34200
logging_queue_listener.py
SocketHandler
, , β SocketHandler , -, . .
, , : β , . .
5. - β NullHandler
, logging.
β NullHandler. NullHandler . , .
NullHandler.
class NullHandler(Handler):
"""
. ,
" XXX ".
, .
,
; ,
NullHandler
.
"""
def handle(self, record):
"""."""
def emit(self, record):
"""."""
def createLock(self):
self.lock = None
logging_nullhandler.py
?
logging, Vinay Sajip:
, logging, , / , .
β , .
init.py, NullHandler. . pip install, .
# package/
# β
# βββ __init__.py
# βββ module1.py
# __init__.py
import logging
logging.getLogger(__name__).addHandler(logging.NullHandler())
# module1.py
logger = logging.getLogger(__name__)
logger.info(" ")
logging_nullhandler_example.py
, .
#
logging.getLogger("package").addHandler(logging.StreamHandler())
NullHandler, , logging.getLogger("package").propagate = False. propagate False, .
6. β RotatingFileHandler, TimedRotatingFileHandler
RotatingFileHandler , . : maxBytes backupCount. maxBytes , . backupCount β . «» Β«.1Β», Β«.2Β» . - , .
import logging
from logging.handlers import RotatingFileHandler
def create_rotating_log(path):
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = RotatingFileHandler(path, maxBytes=20, backupCount=5)
logger.addHandler(handler)
for i in range(100):
logger.info(" - %s" % i)
if __name__ == "__main__":
log_file = "test.log"
create_rotating_log(log_file)
logging_file_rotation.py
β TimeRotatingFileHandler, , . : , , , , (0=) ( ).
. .
import logging
import time
from logging.handlers import TimedRotatingFileHandler
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = TimedRotatingFileHandler(
"timed_test.log", when="s", interval=1, backupCount=5
)
logger.addHandler(handler)
for i in range(6):
logger.info(i)
time.sleep(1)
time_file_rotation.py
7.
logger.error() logger.exception() . ? ? , .
, emit(). , , , , . , handleError() stderr, . , Handler, handleError().
def emit(self, record):
"""
.
, .
.
,
traceback.print_exception .
'encoding', ,
.
"""
try:
msg = self.format(record)
stream = self.stream
# issue 35046: merged two stream.writes into one.
stream.write(msg + self.terminator)
self.flush()
except RecursionError: # See issue 36272
raise
except Exception:
self.handleError(record)
logging_emit.py
. , .
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - [%(levelname)s] - %(name)s - (%(filename)s).%(funcName)s(%(lineno)d) - %(message)s",
)
logger = logging.getLogger(__name__)
logger.info(" ")
logger.info(" %s ", "msg", "other")
logger.info(" ")
# 2020-07-26 23:37:15,373 - [INFO] - __main__ - (main.py).main(16) -
# --- Logging error ---
# Traceback (most recent call last):
# File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/logging/__init__.py", line 1197, in emit
# msg = self.format(record)
# File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/logging/__init__.py", line 1032, in format
# return fmt.format(record)
# File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/logging/__init__.py", line 756, in format
# record.message = record.getMessage()
# File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/logging/__init__.py", line 443, in getMessage
# msg = msg % self.args
# TypeError: not all arguments converted during string formatting (. .: )
# Call stack:
# File "/Users/ie15qx/repo/compare_xml_files/source/main.py", line 22, in <module>
# main()
# File "/Users/ie15qx/repo/compare_xml_files/source/main.py", line 17, in main
# logger.info(" %s ", "msg", "other")
# Message: ' %s '
# Arguments: ('msg', 'other')
# 2020-07-26 23:37:15,374 - [INFO] - __main__ - (main.py).main(18) -
logging_error_handle.py
, emit(), , . , id logger.info LoggerAdapter. .
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - [%(levelname)s] - %(app)s - %(name)s - (%(filename)s).%(funcName)s(%(lineno)d) - %(message)s",
)
logger = logging.getLogger(__name__)
logger = logging.LoggerAdapter(logger, {"app": " "})
logger.info(" ", id="123")
logger.info(" ")
# source/main.py
# Traceback (most recent call last):
# File "/Users/ie15qx/repo/compare_xml_files/source/main.py", line 10, in <module>
# logger.info(" ", id="123")
# File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/logging/__init__.py", line 1960, in info
# self.log(INFO, msg, *args, **kwargs)
# File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/logging/__init__.py", line 2001, in log
# self.logger.log(level, msg, *args, **kwargs)
# File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/logging/__init__.py", line 1637, in log
# self._log(level, msg, args, **kwargs)
# TypeError: _log() got an unexpected keyword argument 'id' (. .: _log() 'id')
logging_exception_raise.py
8.
, , β . .
β , , , . , (. .: ) .
use dictConfig
β logging.config.dictConfig, . JSON- . , , - .
import logging
import logging.config
LOGGING_CONFIG = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'standard': {
'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
},
},
'handlers': {
'default': {
'level': 'INFO',
'formatter': 'standard',
'class': 'logging.StreamHandler',
'stream': 'ext://sys.stdout',
},
},
'loggers': {
'': { # root logger
'handlers': ['default'],
'level': 'DEBUG',
'propagate': True,
}
},
}
logging.config.dictConfig(LOGGING_CONFIG)
if __name__ == "__main__":
log = logging.getLogger(__name__)
log.info("hello world")
# 2020-07-28 21:44:09,966 [INFO] __main__: hello world
logging_configure_json.py
fileConfig
, , β logging.config.fileConfig .ini.
# logging_config.ini
# [loggers]
# keys=root
# [handlers]
# keys=stream_handler
# [formatters]
# keys=formatter
# [logger_root]
# level=DEBUG
# handlers=stream_handler
# [handler_stream_handler]
# class=StreamHandler
# level=DEBUG
# formatter=formatter
# args=(sys.stderr,)
# [formatter_formatter]
# format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s
import logging
from logging.config import fileConfig
fileConfig('logging_config.ini')
logger = logging.getLogger()
logger.debug('hello world')
logging_configure_file.py
. , . , c = logging.config.listen(PORT) c.start(), .
, , logging, . -, , , !