|
1 | 1 | #!/usr/bin/env python3
|
2 | 2 | # -*- coding: utf-8 -*-
|
3 |
| -from __future__ import annotations |
4 |
| - |
| 3 | +import inspect |
| 4 | +import logging |
5 | 5 | import os
|
6 | 6 |
|
7 |
| -from typing import TYPE_CHECKING |
| 7 | +from sys import stderr, stdout |
8 | 8 |
|
9 | 9 | from loguru import logger
|
10 | 10 |
|
11 | 11 | from backend.core import path_conf
|
12 | 12 | from backend.core.conf import settings
|
13 | 13 |
|
14 |
| -if TYPE_CHECKING: |
15 |
| - import loguru |
16 |
| - |
17 |
| - |
18 |
| -class Logger: |
19 |
| - def __init__(self): |
20 |
| - self.log_path = path_conf.LOG_DIR |
21 |
| - |
22 |
| - def log(self) -> loguru.Logger: |
23 |
| - if not os.path.exists(self.log_path): |
24 |
| - os.mkdir(self.log_path) |
25 |
| - |
26 |
| - # 日志文件 |
27 |
| - log_stdout_file = os.path.join(self.log_path, settings.LOG_STDOUT_FILENAME) |
28 |
| - log_stderr_file = os.path.join(self.log_path, settings.LOG_STDERR_FILENAME) |
29 |
| - |
30 |
| - # loguru 日志: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.add |
31 |
| - log_config = dict(rotation='10 MB', retention='15 days', compression='tar.gz', enqueue=True) |
32 |
| - # stdout |
33 |
| - logger.add( |
34 |
| - log_stdout_file, |
35 |
| - level='INFO', |
36 |
| - filter=lambda record: record['level'].name == 'INFO' or record['level'].no <= 25, |
37 |
| - **log_config, |
38 |
| - backtrace=False, |
39 |
| - diagnose=False, |
40 |
| - ) |
41 |
| - # stderr |
42 |
| - logger.add( |
43 |
| - log_stderr_file, |
44 |
| - level='ERROR', |
45 |
| - filter=lambda record: record['level'].name == 'ERROR' or record['level'].no >= 30, |
46 |
| - **log_config, |
47 |
| - backtrace=True, |
48 |
| - diagnose=True, |
49 |
| - ) |
50 |
| - |
51 |
| - return logger |
52 |
| - |
53 |
| - |
54 |
| -log = Logger().log() |
| 14 | + |
| 15 | +class InterceptHandler(logging.Handler): |
| 16 | + """ |
| 17 | + Default handler from examples in loguru documentation. |
| 18 | + See https://loguru.readthedocs.io/en/stable/overview.html#entirely-compatible-with-standard-logging |
| 19 | + """ |
| 20 | + |
| 21 | + def emit(self, record: logging.LogRecord): |
| 22 | + # Get corresponding Loguru level if it exists |
| 23 | + try: |
| 24 | + level = logger.level(record.levelname).name |
| 25 | + except ValueError: |
| 26 | + level = record.levelno |
| 27 | + |
| 28 | + # Find caller from where originated the logged message. |
| 29 | + frame, depth = inspect.currentframe(), 0 |
| 30 | + while frame and (depth == 0 or frame.f_code.co_filename == logging.__file__): |
| 31 | + frame = frame.f_back |
| 32 | + depth += 1 |
| 33 | + |
| 34 | + logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage()) |
| 35 | + |
| 36 | + |
| 37 | +def setup_logging(): |
| 38 | + """ |
| 39 | + From https://pawamoy.github.io/posts/unify-logging-for-a-gunicorn-uvicorn-app/ |
| 40 | + https://github.com/pawamoy/pawamoy.github.io/issues/17 |
| 41 | + """ |
| 42 | + # Intercept everything at the root logger |
| 43 | + logging.root.handlers = [InterceptHandler()] |
| 44 | + logging.root.setLevel(settings.LOG_LEVEL) |
| 45 | + |
| 46 | + # Remove all log handlers and propagate to root logger |
| 47 | + for name in logging.root.manager.loggerDict.keys(): |
| 48 | + logging.getLogger(name).handlers = [] |
| 49 | + if 'uvicorn.access' in name or 'watchfiles.main' in name: |
| 50 | + logging.getLogger(name).propagate = False |
| 51 | + else: |
| 52 | + logging.getLogger(name).propagate = True |
| 53 | + |
| 54 | + logging.debug(f'{logging.getLogger(name)}, {logging.getLogger(name).propagate}') |
| 55 | + |
| 56 | + # Remove every other logger's handlers |
| 57 | + logger.remove() |
| 58 | + |
| 59 | + # Configure logger before starts logging |
| 60 | + logger.configure(handlers=[{'sink': stdout, 'level': settings.LOG_LEVEL, 'format': settings.LOG_FORMAT}]) |
| 61 | + logger.configure(handlers=[{'sink': stderr, 'level': settings.LOG_LEVEL, 'format': settings.LOG_FORMAT}]) |
| 62 | + |
| 63 | + |
| 64 | +def set_customize_logfile(): |
| 65 | + log_path = path_conf.LOG_DIR |
| 66 | + if not os.path.exists(log_path): |
| 67 | + os.mkdir(log_path) |
| 68 | + |
| 69 | + # log files |
| 70 | + log_stdout_file = os.path.join(log_path, settings.LOG_STDOUT_FILENAME) |
| 71 | + log_stderr_file = os.path.join(log_path, settings.LOG_STDERR_FILENAME) |
| 72 | + |
| 73 | + # loguru logger: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.add |
| 74 | + log_config = { |
| 75 | + 'rotation': '10 MB', |
| 76 | + 'retention': '15 days', |
| 77 | + 'compression': 'tar.gz', |
| 78 | + 'enqueue': True, |
| 79 | + 'format': settings.LOG_FORMAT, |
| 80 | + } |
| 81 | + |
| 82 | + # stdout |
| 83 | + logger.add( |
| 84 | + log_stdout_file, |
| 85 | + level='INFO', |
| 86 | + filter=lambda record: record['level'].name == 'INFO' or record['level'].no <= 25, |
| 87 | + **log_config, |
| 88 | + backtrace=False, |
| 89 | + diagnose=False, |
| 90 | + ) |
| 91 | + |
| 92 | + # stderr |
| 93 | + logger.add( |
| 94 | + log_stderr_file, |
| 95 | + level='ERROR', |
| 96 | + filter=lambda record: record['level'].name == 'ERROR' or record['level'].no >= 30, |
| 97 | + **log_config, |
| 98 | + backtrace=True, |
| 99 | + diagnose=True, |
| 100 | + ) |
| 101 | + |
| 102 | + |
| 103 | +log = logger |
0 commit comments