log.py 3.1 KB

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