logger.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. """
  2. Internal logging utility.
  3. """
  4. import logging
  5. import os
  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. LOG_LEVEL = os.getenv("APHRODITE_LOG_LEVEL", "INFO").upper()
  19. def unwrap(wrapped, default=None):
  20. """Unwrap function for Optionals."""
  21. if wrapped is None:
  22. return default
  23. return wrapped
  24. def get_loading_progress_bar():
  25. """Gets a pre-made progress bar for loading tasks."""
  26. return Progress(
  27. TextColumn("[progress.description]{task.description}"),
  28. BarColumn(),
  29. TaskProgressColumn(),
  30. MofNCompleteColumn(),
  31. TimeRemainingColumn(),
  32. console=RICH_CONSOLE,
  33. )
  34. def _log_formatter(record: dict):
  35. """Log message formatter."""
  36. color_map = {
  37. "TRACE": "dim blue",
  38. "DEBUG": "cyan",
  39. "INFO": "green",
  40. "SUCCESS": "bold green",
  41. "WARNING": "yellow",
  42. "ERROR": "red",
  43. "CRITICAL": "bold white on red",
  44. }
  45. level = record.get("level")
  46. level_color = color_map.get(level.name, "cyan")
  47. colored_level = f"[{level_color}]{level.name}[/{level_color}]:"
  48. separator = " " * (9 - len(level.name))
  49. message = unwrap(record.get("message"), "")
  50. # Replace once loguru allows for turning off str.format
  51. message = message.replace("{", "{{").replace("}", "}}").replace("<", "\<")
  52. # Escape markup tags from Rich
  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": LOG_LEVEL
  83. },
  84. }
  85. def setup_logger():
  86. """Bootstrap the logger."""
  87. logger.remove()
  88. logger.add(
  89. RICH_CONSOLE.print,
  90. level=LOG_LEVEL,
  91. format=_log_formatter,
  92. colorize=True,
  93. )