gunicorn_config.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. # gunicorn_config.py
  2. import os
  3. import logging
  4. from lightrag.kg.shared_storage import finalize_share_data
  5. from lightrag.utils import setup_logger, get_env_value
  6. from lightrag.constants import (
  7. DEFAULT_LOG_MAX_BYTES,
  8. DEFAULT_LOG_BACKUP_COUNT,
  9. DEFAULT_LOG_FILENAME,
  10. )
  11. # Get log directory path from environment variable
  12. log_dir = os.getenv("LOG_DIR", os.getcwd())
  13. log_file_path = os.path.abspath(os.path.join(log_dir, DEFAULT_LOG_FILENAME))
  14. # Ensure log directory exists
  15. os.makedirs(os.path.dirname(log_file_path), exist_ok=True)
  16. # Get log file max size and backup count from environment variables
  17. log_max_bytes = get_env_value("LOG_MAX_BYTES", DEFAULT_LOG_MAX_BYTES, int)
  18. log_backup_count = get_env_value("LOG_BACKUP_COUNT", DEFAULT_LOG_BACKUP_COUNT, int)
  19. # These variables will be set by run_with_gunicorn.py
  20. workers = None
  21. bind = None
  22. loglevel = None
  23. certfile = None
  24. keyfile = None
  25. # Enable preload_app option
  26. preload_app = True
  27. # Use Uvicorn worker
  28. worker_class = "uvicorn.workers.UvicornWorker"
  29. # Other Gunicorn configurations
  30. # Logging configuration
  31. errorlog = os.getenv("ERROR_LOG", log_file_path) # Default write to lightrag.log
  32. accesslog = os.getenv("ACCESS_LOG", log_file_path) # Default write to lightrag.log
  33. logconfig_dict = {
  34. "version": 1,
  35. "disable_existing_loggers": False,
  36. "formatters": {
  37. "standard": {"format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s"},
  38. },
  39. "handlers": {
  40. "console": {
  41. "class": "logging.StreamHandler",
  42. "formatter": "standard",
  43. "stream": "ext://sys.stdout",
  44. },
  45. "file": {
  46. "class": "logging.handlers.RotatingFileHandler",
  47. "formatter": "standard",
  48. "filename": log_file_path,
  49. "maxBytes": log_max_bytes,
  50. "backupCount": log_backup_count,
  51. "encoding": "utf8",
  52. },
  53. },
  54. "filters": {
  55. "path_filter": {
  56. "()": "lightrag.utils.LightragPathFilter",
  57. },
  58. },
  59. "loggers": {
  60. "lightrag": {
  61. "handlers": ["console", "file"],
  62. "level": loglevel.upper() if loglevel else "INFO",
  63. "propagate": False,
  64. },
  65. "gunicorn": {
  66. "handlers": ["console", "file"],
  67. "level": loglevel.upper() if loglevel else "INFO",
  68. "propagate": False,
  69. },
  70. "gunicorn.error": {
  71. "handlers": ["console", "file"],
  72. "level": loglevel.upper() if loglevel else "INFO",
  73. "propagate": False,
  74. },
  75. "gunicorn.access": {
  76. "handlers": ["console", "file"],
  77. "level": loglevel.upper() if loglevel else "INFO",
  78. "propagate": False,
  79. "filters": ["path_filter"],
  80. },
  81. },
  82. }
  83. def on_starting(server):
  84. """
  85. Executed when Gunicorn starts, before forking the first worker processes
  86. You can use this function to do more initialization tasks for all processes
  87. """
  88. print("=" * 80)
  89. print(f"GUNICORN MASTER PROCESS: on_starting jobs for {workers} worker(s)")
  90. print(f"Process ID: {os.getpid()}")
  91. print("=" * 80)
  92. # Memory usage monitoring
  93. try:
  94. import psutil
  95. process = psutil.Process(os.getpid())
  96. memory_info = process.memory_info()
  97. msg = (
  98. f"Memory usage after initialization: {memory_info.rss / 1024 / 1024:.2f} MB"
  99. )
  100. print(msg)
  101. except ImportError:
  102. print("psutil not installed, skipping memory usage reporting")
  103. # Log the location of the LightRAG log file
  104. print(f"LightRAG log file: {log_file_path}\n")
  105. print("Gunicorn initialization complete, forking workers...\n")
  106. def on_exit(server):
  107. """
  108. Executed when Gunicorn is shutting down.
  109. This is a good place to release shared resources.
  110. """
  111. print("=" * 80)
  112. print("GUNICORN MASTER PROCESS: Shutting down")
  113. print(f"Process ID: {os.getpid()}")
  114. print("Finalizing shared storage...")
  115. finalize_share_data()
  116. print("Gunicorn shutdown complete")
  117. print("=" * 80)
  118. def post_fork(server, worker):
  119. """
  120. Executed after a worker has been forked.
  121. This is a good place to set up worker-specific configurations.
  122. """
  123. # Set up main loggers
  124. log_level = loglevel.upper() if loglevel else "INFO"
  125. setup_logger("uvicorn", log_level, add_filter=False, log_file_path=log_file_path)
  126. setup_logger(
  127. "uvicorn.access", log_level, add_filter=True, log_file_path=log_file_path
  128. )
  129. setup_logger("lightrag", log_level, add_filter=True, log_file_path=log_file_path)
  130. # Set up lightrag submodule loggers
  131. for name in logging.root.manager.loggerDict:
  132. if name.startswith("lightrag."):
  133. setup_logger(name, log_level, add_filter=True, log_file_path=log_file_path)
  134. # Disable uvicorn.error logger
  135. uvicorn_error_logger = logging.getLogger("uvicorn.error")
  136. uvicorn_error_logger.handlers = []
  137. uvicorn_error_logger.setLevel(logging.CRITICAL)
  138. uvicorn_error_logger.propagate = False