使用 Loguru 日志库记录在导入文件中发出的请求
Use Loguru logging library to log requests made in an imported file
所以我在 python 文件 (main.py) 中编写了程序,在 api 包装文件 (bluerev.py) 中使用了 类。我想在 main.py 中使用 loguru 记录器来收集程序中的所有异常 + 在 api 包装器中发出的所有请求。因此 bluerev.py api 包装器中设置的日志记录如下所示:
import logging
#Logging setup
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
class BluerevApiRequestHandler:
def __init__(self):
self.date_str_format = "%Y-%m-%dT%H:%M:%S.%f"
@staticmethod
def setup_logger_for_requests():
"""
Sets up the requests library to log api requests
"""
logger.setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
main.py 日志代码如下所示:
from blurev import *
from loguru import logger
#more imports and code
@logger.catch
def main():
# associated file and credential locations
gmail_creds_file = "gmail_creds.json"
revu_creds_file = r"revu_credentials.json"
token_file = r"revu_refresh_token.json"
files_location = os.path.join(os.getcwd(), "rev_dist_files")
review_files_location = os.path.join(os.getcwd(), "files_to_distribute")
# Set up Logging
logging_file = r"plan_review_distributor.log"
#logging_format_str = "%(levelname)s - %(asctime)-15s - %(filename)s - line %(lineno)d --> %(message)s"
logger.add(os.path.join(files_location, logging_file), level="WARNING")
logger.add(os.path.join(files_location, logging_file), filter="blurev", level="DEBUG")
#call the requests logging function here in main()
request_handler = BluerevApiRequestHandler()
request_handler.setup_logger_for_requests()
#more code
所以我想知道应该如何更改它,以便将 blurev.py 文件中由 main.py 中的函数和代码调用的请求记录到 logging_file 中。现在他们不是。
问题在于 loguru
使用与经典 logging
库完全不同的日志记录机制。经典的 logging
库构建了记录器的层次结构,日志记录向上传播到根(请参阅 Python 文档中的 Advanced logging tutorial 以供参考)。但是 loguru
根本不使用这个层次结构,它以完全脱节的方式运行。
因此,如果您希望使用经典 logging
库发出的日志最终由 loguru
处理,则必须拦截它们。
这是我的 Minimal Reproducible Example 解决方案:
# main.py
import logging as classic_logging
import os
from blurev import BluerevApiRequestHandler
from loguru import logger as loguru_logger
@loguru_logger.catch
def main():
logging_file = r"plan_review_distributor.log"
loguru_logger.add(os.path.join(".", logging_file), filter=lambda rec: rec["name"] != "blurev", level="WARNING")
loguru_logger.add(os.path.join(".", logging_file), filter="blurev", level="DEBUG")
root_logger = classic_logging.getLogger("")
root_logger.handlers = [InterceptHandler()] # replace ANY pre-existing handler
root_logger.setLevel("DEBUG")
request_handler = BluerevApiRequestHandler()
request_handler.do_something_using_requests()
########################################################################################################################
# recipe from https://loguru.readthedocs.io/en/stable/overview.html#entirely-compatible-with-standard-logging
class InterceptHandler(classic_logging.Handler):
def emit(self, record):
# Get corresponding Loguru level if it exists
try:
level = loguru_logger.level(record.levelname).name
except ValueError:
level = record.levelno
# Find caller from where originated the logged message
frame, depth = classic_logging.currentframe(), 2
while frame.f_code.co_filename == classic_logging.__file__:
frame = frame.f_back
depth += 1
loguru_logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())
########################################################################################################################
if __name__ == "__main__":
main()
# bluerev.py
import logging as classic_logging
import requests
bluerev_logger = classic_logging.getLogger(__name__)
class BluerevApiRequestHandler:
def do_something_using_requests(self):
print(requests.get("http://example.com").text.splitlines()[3]) # expecting: " <title>Example Domain</title>"
classic_logging.getLogger("requests.packages.urllib3").warning("warning from requests") # simulating
bluerev_logger.debug("debug from bluerev")
bluerev_logger.critical("critical from bluerev")
想法是在经典的root logger中添加一个处理程序,它将日志记录传输到loguru logger。我们不需要 setup_logger_for_requests
,我们只是让日志记录从 "requests.packages.urllib3"
层次结构向上自然传播。
我向第一个 loguru
sink 添加了一个过滤器,这样 "blurev"
日志记录就不会被写入文件两次(因为被两个接收器捕获)。
这是我得到的输出:
# stdout
2022-01-07 11:58:02.480 | DEBUG | urllib3.connectionpool:_new_conn:227 - Starting new HTTP connection (1): example.com:80
2022-01-07 11:58:02.653 | DEBUG | urllib3.connectionpool:_make_request:452 - http://example.com:80 "GET / HTTP/1.1" 200 648
2022-01-07 11:58:02.654 | WARNING | blurev:do_something_using_requests:14 - warning from requests
2022-01-07 11:58:02.654 | DEBUG | blurev:do_something_using_requests:15 - debug from bluerev
2022-01-07 11:58:02.654 | CRITICAL | blurev:do_something_using_requests:16 - critical from bluerev
<title>Example Domain</title>
# plan_review_distributor.log
2022-01-07 11:58:02.654 | WARNING | blurev:do_something_using_requests:14 - warning from requests
2022-01-07 11:58:02.654 | DEBUG | blurev:do_something_using_requests:15 - debug from bluerev
2022-01-07 11:58:02.654 | CRITICAL | blurev:do_something_using_requests:16 - critical from bluerev
参见 Loguru's documentation section "Entirely compatible with standard logging"。
另外,你有时写“bluerev”,有时写“bluerev”,小心点!
所以我在 python 文件 (main.py) 中编写了程序,在 api 包装文件 (bluerev.py) 中使用了 类。我想在 main.py 中使用 loguru 记录器来收集程序中的所有异常 + 在 api 包装器中发出的所有请求。因此 bluerev.py api 包装器中设置的日志记录如下所示:
import logging
#Logging setup
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
class BluerevApiRequestHandler:
def __init__(self):
self.date_str_format = "%Y-%m-%dT%H:%M:%S.%f"
@staticmethod
def setup_logger_for_requests():
"""
Sets up the requests library to log api requests
"""
logger.setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
main.py 日志代码如下所示:
from blurev import *
from loguru import logger
#more imports and code
@logger.catch
def main():
# associated file and credential locations
gmail_creds_file = "gmail_creds.json"
revu_creds_file = r"revu_credentials.json"
token_file = r"revu_refresh_token.json"
files_location = os.path.join(os.getcwd(), "rev_dist_files")
review_files_location = os.path.join(os.getcwd(), "files_to_distribute")
# Set up Logging
logging_file = r"plan_review_distributor.log"
#logging_format_str = "%(levelname)s - %(asctime)-15s - %(filename)s - line %(lineno)d --> %(message)s"
logger.add(os.path.join(files_location, logging_file), level="WARNING")
logger.add(os.path.join(files_location, logging_file), filter="blurev", level="DEBUG")
#call the requests logging function here in main()
request_handler = BluerevApiRequestHandler()
request_handler.setup_logger_for_requests()
#more code
所以我想知道应该如何更改它,以便将 blurev.py 文件中由 main.py 中的函数和代码调用的请求记录到 logging_file 中。现在他们不是。
问题在于 loguru
使用与经典 logging
库完全不同的日志记录机制。经典的 logging
库构建了记录器的层次结构,日志记录向上传播到根(请参阅 Python 文档中的 Advanced logging tutorial 以供参考)。但是 loguru
根本不使用这个层次结构,它以完全脱节的方式运行。
因此,如果您希望使用经典 logging
库发出的日志最终由 loguru
处理,则必须拦截它们。
这是我的 Minimal Reproducible Example 解决方案:
# main.py
import logging as classic_logging
import os
from blurev import BluerevApiRequestHandler
from loguru import logger as loguru_logger
@loguru_logger.catch
def main():
logging_file = r"plan_review_distributor.log"
loguru_logger.add(os.path.join(".", logging_file), filter=lambda rec: rec["name"] != "blurev", level="WARNING")
loguru_logger.add(os.path.join(".", logging_file), filter="blurev", level="DEBUG")
root_logger = classic_logging.getLogger("")
root_logger.handlers = [InterceptHandler()] # replace ANY pre-existing handler
root_logger.setLevel("DEBUG")
request_handler = BluerevApiRequestHandler()
request_handler.do_something_using_requests()
########################################################################################################################
# recipe from https://loguru.readthedocs.io/en/stable/overview.html#entirely-compatible-with-standard-logging
class InterceptHandler(classic_logging.Handler):
def emit(self, record):
# Get corresponding Loguru level if it exists
try:
level = loguru_logger.level(record.levelname).name
except ValueError:
level = record.levelno
# Find caller from where originated the logged message
frame, depth = classic_logging.currentframe(), 2
while frame.f_code.co_filename == classic_logging.__file__:
frame = frame.f_back
depth += 1
loguru_logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())
########################################################################################################################
if __name__ == "__main__":
main()
# bluerev.py
import logging as classic_logging
import requests
bluerev_logger = classic_logging.getLogger(__name__)
class BluerevApiRequestHandler:
def do_something_using_requests(self):
print(requests.get("http://example.com").text.splitlines()[3]) # expecting: " <title>Example Domain</title>"
classic_logging.getLogger("requests.packages.urllib3").warning("warning from requests") # simulating
bluerev_logger.debug("debug from bluerev")
bluerev_logger.critical("critical from bluerev")
想法是在经典的root logger中添加一个处理程序,它将日志记录传输到loguru logger。我们不需要 setup_logger_for_requests
,我们只是让日志记录从 "requests.packages.urllib3"
层次结构向上自然传播。
我向第一个 loguru
sink 添加了一个过滤器,这样 "blurev"
日志记录就不会被写入文件两次(因为被两个接收器捕获)。
这是我得到的输出:
# stdout
2022-01-07 11:58:02.480 | DEBUG | urllib3.connectionpool:_new_conn:227 - Starting new HTTP connection (1): example.com:80
2022-01-07 11:58:02.653 | DEBUG | urllib3.connectionpool:_make_request:452 - http://example.com:80 "GET / HTTP/1.1" 200 648
2022-01-07 11:58:02.654 | WARNING | blurev:do_something_using_requests:14 - warning from requests
2022-01-07 11:58:02.654 | DEBUG | blurev:do_something_using_requests:15 - debug from bluerev
2022-01-07 11:58:02.654 | CRITICAL | blurev:do_something_using_requests:16 - critical from bluerev
<title>Example Domain</title>
# plan_review_distributor.log
2022-01-07 11:58:02.654 | WARNING | blurev:do_something_using_requests:14 - warning from requests
2022-01-07 11:58:02.654 | DEBUG | blurev:do_something_using_requests:15 - debug from bluerev
2022-01-07 11:58:02.654 | CRITICAL | blurev:do_something_using_requests:16 - critical from bluerev
参见 Loguru's documentation section "Entirely compatible with standard logging"。
另外,你有时写“bluerev”,有时写“bluerev”,小心点!