logging.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import logging
  2. from logging.handlers import RotatingFileHandler
  3. import os
  4. from .config import RobotConfig
  5. THROTTLING_FILTER_ENABLED = False
  6. # Usage:
  7. # In main.py, after loading config:
  8. # from robot_control.src.utils.logging import setup_logging
  9. # setup_logging(config)
  10. # In each module:
  11. # import logging
  12. # logger = logging.getLogger(__name__)
  13. # logger.info("message")
  14. def setup_logging(config: RobotConfig):
  15. """
  16. Set up logging configuration globally using the provided config.
  17. Should be called once at program startup (e.g., in main.py).
  18. """
  19. log_config = config.logging
  20. log_level = getattr(logging, log_config.level)
  21. os.makedirs(os.path.dirname(log_config.file_path), exist_ok=True)
  22. # Remove all handlers associated with the root logger object.
  23. for handler in logging.root.handlers[:]:
  24. logging.root.removeHandler(handler)
  25. handlers = []
  26. # File handler
  27. file_handler = RotatingFileHandler(
  28. log_config.file_path,
  29. maxBytes=log_config.max_file_size_mb * 1024 * 1024,
  30. backupCount=log_config.backup_count
  31. )
  32. file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
  33. if THROTTLING_FILTER_ENABLED:
  34. file_handler.addFilter(ThrottlingFilter())
  35. file_handler.setFormatter(file_formatter)
  36. handlers.append(file_handler)
  37. # Console handler
  38. if log_config.console_output:
  39. console_handler = logging.StreamHandler()
  40. if THROTTLING_FILTER_ENABLED:
  41. console_handler.addFilter(ThrottlingFilter())
  42. console_formatter = logging.Formatter('%(levelname)s: %(name)s: %(message)s')
  43. console_handler.setFormatter(console_formatter)
  44. handlers.append(console_handler)
  45. logging.basicConfig(level=log_level, handlers=handlers)
  46. import time
  47. class ThrottlingFilter(logging.Filter):
  48. def __init__(self, name='', initial_throttle_interval=1.0, max_throttle_interval=120.0,
  49. throttle_multiplier=2.0, reset_after=120.0):
  50. """
  51. :param name: Filter name (can be left empty)
  52. :param initial_throttle_interval: Initial time interval (in seconds) before allowing duplicate messages
  53. :param max_throttle_interval: Maximum throttle interval (in seconds)
  54. :param throttle_multiplier: Factor by which to increase the interval for repeated messages
  55. :param reset_after: Time in seconds after which to reset throttle interval if message wasn't seen
  56. """
  57. super().__init__(name)
  58. self.initial_throttle_interval = initial_throttle_interval
  59. self.max_throttle_interval = max_throttle_interval
  60. self.throttle_multiplier = throttle_multiplier
  61. self.reset_after = reset_after
  62. self.message_states = {}
  63. def filter(self, record):
  64. current_time = time.time()
  65. message_key = record.getMessage()
  66. if message_key not in self.message_states:
  67. self.message_states[message_key] = {
  68. 'last_time': current_time,
  69. 'current_interval': self.initial_throttle_interval
  70. }
  71. return True
  72. state = self.message_states[message_key]
  73. time_since_last = current_time - state['last_time']
  74. # Reset throttle interval if message hasn't been seen for reset_after seconds
  75. if time_since_last >= self.reset_after:
  76. self.message_states[message_key] = {
  77. 'last_time': current_time,
  78. 'current_interval': self.initial_throttle_interval
  79. }
  80. return True
  81. if time_since_last >= state['current_interval']:
  82. # Message allowed - increase throttle interval for next occurrence
  83. next_interval = min(
  84. state['current_interval'] * self.throttle_multiplier,
  85. self.max_throttle_interval
  86. )
  87. self.message_states[message_key] = {
  88. 'last_time': current_time,
  89. 'current_interval': next_interval
  90. }
  91. return True
  92. return False