logger.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. """
  2. Internal logging utility. Adapted from
  3. https://github.com/theroyallab/tabbyAPI/blob/4cc0b59bdc94e6342b6d1d7acadbadc63c740ed9/common/logger.py
  4. """
  5. import logging
  6. from loguru import logger
  7. from rich.console import Console
  8. from rich.markup import escape
  9. from rich.progress import (
  10. Progress,
  11. TextColumn,
  12. BarColumn,
  13. TimeRemainingColumn,
  14. TaskProgressColumn,
  15. MofNCompleteColumn,
  16. )
  17. RICH_CONSOLE = Console()
  18. def unwrap(wrapped, default=None):
  19. """Unwrap function for Optionals."""
  20. if wrapped is None:
  21. return default
  22. return wrapped
  23. def get_loading_progress_bar():
  24. """Gets a pre-made progress bar for loading tasks."""
  25. return Progress(
  26. TextColumn("[progress.description]{task.description}"),
  27. BarColumn(),
  28. TaskProgressColumn(),
  29. MofNCompleteColumn(),
  30. TimeRemainingColumn(),
  31. console=RICH_CONSOLE,
  32. )
  33. def _log_formatter(record: dict):
  34. """Log message formatter."""
  35. color_map = {
  36. "TRACE": "dim blue",
  37. "DEBUG": "cyan",
  38. "INFO": "green",
  39. "SUCCESS": "bold green",
  40. "WARNING": "yellow",
  41. "ERROR": "red",
  42. "CRITICAL": "bold white on red",
  43. }
  44. level = record.get("level")
  45. level_color = color_map.get(level.name, "cyan")
  46. colored_level = f"[{level_color}]{level.name}[/{level_color}]:"
  47. separator = " " * (9 - len(level.name))
  48. message = unwrap(record.get("message"), "")
  49. # Replace once loguru allows for turning off str.format
  50. message = message.replace("{{", "{{").replace("}}", "}}")
  51. # Manually escape < and > characters
  52. message = message.replace("<", "\\<").replace(">", "\\>")
  53. message = escape(message)
  54. lines = message.splitlines()
  55. fmt = ""
  56. if len(lines) > 1:
  57. fmt = "\n".join(
  58. [f"{colored_level}{separator}{line}" for line in lines])
  59. else:
  60. fmt = f"{colored_level}{separator}{message}"
  61. return fmt
  62. # Uvicorn log handler
  63. # Uvicorn log portions inspired from https://github.com/encode/uvicorn/discussions/2027#discussioncomment-6432362
  64. class UvicornLoggingHandler(logging.Handler):
  65. def emit(self, record: logging.LogRecord) -> None:
  66. logger.opt(exception=record.exc_info).log(record.levelname,
  67. self.format(record).rstrip())
  68. # Uvicorn config for logging. Passed into run when creating all loggers in
  69. #server
  70. UVICORN_LOG_CONFIG = {
  71. "version": 1,
  72. "disable_existing_loggers": False,
  73. "handlers": {
  74. "uvicorn": {
  75. "class":
  76. f"{UvicornLoggingHandler.__module__}.{UvicornLoggingHandler.__qualname__}", # noqa
  77. },
  78. },
  79. "root": {
  80. "handlers": ["uvicorn"],
  81. "propagate": False,
  82. "level": "INFO"
  83. },
  84. }
  85. def setup_logger():
  86. """Bootstrap the logger."""
  87. logger.remove()
  88. logger.add(
  89. RICH_CONSOLE.print,
  90. level="INFO",
  91. format=_log_formatter,
  92. colorize=True,
  93. )