The problem is that loguru
uses a completely different mechanism for logging than the classic logging
library. The classic logging
library constructs a hierarchy of loggers, and log records are propagate up to the root (see the Advanced logging tutorial from the Python docs for reference). But loguru
does not use at all this hierarchy, it operates in a totally disjointed way from it.
So if you want the logs emitted using the classic logging
library to be ultimately handled by loguru
, you have to intercept them.
Here is a Minimal Reproducible Example of my solution :
# 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")
The idea is to add an handler to the classic root logger, which will transmit the log records to the loguru logger. We don't need to setup_logger_for_requests
, we just let the log record propagate naturally from "requests.packages.urllib3"
up the hierarchy.
I added a filter to the first loguru
sink so that a "blurev"
log record does not get written twice to the file (because caught by the two sinks).
This is the output I get :
# 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
See Loguru's documentation section "Entirely compatible with standard logging".
Also, you sometimes wrote "bluerev", some other times "bluerev", better watch out !