logging_service.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import csv
  2. import os
  3. import json
  4. from typing import List, Dict, Any, Optional
  5. from datetime import datetime
  6. from logging import getLogger
  7. from enum import Enum
  8. from models.sales import OrderWeb, ItemWeb
  9. logger = getLogger(__name__)
  10. class LogLevel(Enum):
  11. """Log levels for structured logging"""
  12. DEBUG = "DEBUG"
  13. INFO = "INFO"
  14. WARNING = "WARNING"
  15. ERROR = "ERROR"
  16. CRITICAL = "CRITICAL"
  17. class LogCategory(Enum):
  18. """Categories for different types of logs"""
  19. ORDER = "ORDER"
  20. USER = "USER"
  21. PAYMENT = "PAYMENT"
  22. SECURITY = "SECURITY"
  23. API = "API"
  24. DATABASE = "DATABASE"
  25. EMAIL = "EMAIL"
  26. PRINT = "PRINT"
  27. CHAT = "CHAT"
  28. SYSTEM = "SYSTEM"
  29. class StructuredLogger:
  30. """Enhanced logging service with structured logging capabilities"""
  31. def __init__(self):
  32. self.logger = getLogger(__name__)
  33. self.logs_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'logs')
  34. self._ensure_logs_directory()
  35. def _ensure_logs_directory(self):
  36. """Ensure logs directory exists"""
  37. if not os.path.exists(self.logs_dir):
  38. os.makedirs(self.logs_dir)
  39. self.logger.info(f"Created logs directory: {self.logs_dir}")
  40. def _write_structured_log(self, category: LogCategory, level: LogLevel,
  41. message: str, data: Optional[Dict[str, Any]] = None,
  42. user_id: Optional[int] = None, user_email: Optional[str] = None):
  43. """Write structured log entry"""
  44. log_entry = {
  45. "timestamp": datetime.now().isoformat(),
  46. "category": category.value,
  47. "level": level.value,
  48. "message": message,
  49. "user_id": user_id,
  50. "user_email": user_email,
  51. "data": data or {}
  52. }
  53. # Write to category-specific log file
  54. log_file = os.path.join(self.logs_dir, f"{category.value.lower()}.log")
  55. try:
  56. with open(log_file, 'a', encoding='utf-8') as f:
  57. f.write(json.dumps(log_entry, ensure_ascii=False) + '\n')
  58. except Exception as e:
  59. self.logger.error(f"Failed to write structured log: {e}")
  60. def log_order_event(self, message: str, level: LogLevel = LogLevel.INFO,
  61. order_data: Optional[Dict] = None, user_id: Optional[int] = None,
  62. user_email: Optional[str] = None):
  63. """Log order-related events"""
  64. self._write_structured_log(LogCategory.ORDER, level, message, order_data, user_id, user_email)
  65. # Also log to main logger
  66. getattr(self.logger, level.value.lower())(
  67. f"[ORDER] {message} - User: {user_email or user_id or 'N/A'}"
  68. )
  69. def log_user_event(self, message: str, level: LogLevel = LogLevel.INFO,
  70. user_data: Optional[Dict] = None, user_id: Optional[int] = None,
  71. user_email: Optional[str] = None):
  72. """Log user-related events"""
  73. self._write_structured_log(LogCategory.USER, level, message, user_data, user_id, user_email)
  74. # Also log to main logger
  75. getattr(self.logger, level.value.lower())(
  76. f"[USER] {message} - User: {user_email or user_id or 'N/A'}"
  77. )
  78. def log_security_event(self, message: str, level: LogLevel = LogLevel.WARNING,
  79. security_data: Optional[Dict] = None, user_id: Optional[int] = None,
  80. user_email: Optional[str] = None):
  81. """Log security-related events"""
  82. self._write_structured_log(LogCategory.SECURITY, level, message, security_data, user_id, user_email)
  83. # Security events should always be logged to main logger
  84. getattr(self.logger, level.value.lower())(
  85. f"[SECURITY] {message} - User: {user_email or user_id or 'N/A'}"
  86. )
  87. def log_api_event(self, message: str, level: LogLevel = LogLevel.INFO,
  88. api_data: Optional[Dict] = None, user_id: Optional[int] = None,
  89. user_email: Optional[str] = None):
  90. """Log API-related events"""
  91. self._write_structured_log(LogCategory.API, level, message, api_data, user_id, user_email)
  92. getattr(self.logger, level.value.lower())(
  93. f"[API] {message} - User: {user_email or user_id or 'N/A'}"
  94. )
  95. def log_database_event(self, message: str, level: LogLevel = LogLevel.INFO,
  96. db_data: Optional[Dict] = None, user_id: Optional[int] = None,
  97. user_email: Optional[str] = None):
  98. """Log database-related events"""
  99. self._write_structured_log(LogCategory.DATABASE, level, message, db_data, user_id, user_email)
  100. getattr(self.logger, level.value.lower())(
  101. f"[DATABASE] {message} - User: {user_email or user_id or 'N/A'}"
  102. )
  103. def log_email_event(self, message: str, level: LogLevel = LogLevel.INFO,
  104. email_data: Optional[Dict] = None, user_id: Optional[int] = None,
  105. user_email: Optional[str] = None):
  106. """Log email-related events"""
  107. self._write_structured_log(LogCategory.EMAIL, level, message, email_data, user_id, user_email)
  108. getattr(self.logger, level.value.lower())(
  109. f"[EMAIL] {message} - User: {user_email or user_id or 'N/A'}"
  110. )
  111. def log_print_event(self, message: str, level: LogLevel = LogLevel.INFO,
  112. print_data: Optional[Dict] = None, user_id: Optional[int] = None,
  113. user_email: Optional[str] = None):
  114. """Log printer-related events"""
  115. self._write_structured_log(LogCategory.PRINT, level, message, print_data, user_id, user_email)
  116. getattr(self.logger, level.value.lower())(
  117. f"[PRINT] {message} - User: {user_email or user_id or 'N/A'}"
  118. )
  119. def log_chat_event(self, message: str, level: LogLevel = LogLevel.INFO,
  120. chat_data: Optional[Dict] = None, user_id: Optional[int] = None,
  121. user_email: Optional[str] = None):
  122. """Log chat/LLM-related events"""
  123. self._write_structured_log(LogCategory.CHAT, level, message, chat_data, user_id, user_email)
  124. getattr(self.logger, level.value.lower())(
  125. f"[CHAT] {message} - User: {user_email or user_id or 'N/A'}"
  126. )
  127. def log_system_event(self, message: str, level: LogLevel = LogLevel.INFO,
  128. system_data: Optional[Dict] = None):
  129. """Log system-related events"""
  130. self._write_structured_log(LogCategory.SYSTEM, level, message, system_data)
  131. getattr(self.logger, level.value.lower())(f"[SYSTEM] {message}")
  132. # Global instance
  133. structured_logger = StructuredLogger()
  134. # Legacy functions for backward compatibility
  135. def log_order(username, table, order_date, items: List[str]):
  136. """Log order information to CSV file (legacy function)"""
  137. try:
  138. structured_logger.log_order_event(
  139. f"Order placed for table {table}",
  140. LogLevel.INFO,
  141. {
  142. "username": username,
  143. "table": table,
  144. "order_date": order_date,
  145. "items": items,
  146. "item_count": len(items) if isinstance(items, list) else 1
  147. },
  148. user_email=username
  149. )
  150. # Still maintain CSV for backward compatibility
  151. csv_file = os.path.join(structured_logger.logs_dir, 'orders.csv')
  152. if not os.path.exists(csv_file):
  153. with open(csv_file, 'w', newline='', encoding='utf-8') as f:
  154. writer = csv.writer(f)
  155. writer.writerow(['userName', 'table', 'orderDate', 'items'])
  156. with open(csv_file, 'a', newline='', encoding='utf-8') as f:
  157. writer = csv.writer(f)
  158. writer.writerow([username, table, order_date, items])
  159. except Exception as e:
  160. logger.error(f"Failed to log order: {e}")
  161. def log_llm_response(user: str, response: str):
  162. """Log LLM response to file (legacy function)"""
  163. try:
  164. structured_logger.log_chat_event(
  165. f"LLM response generated for user {user}",
  166. LogLevel.INFO,
  167. {
  168. "response_length": len(response),
  169. "response_preview": response[:100] + "..." if len(response) > 100 else response
  170. },
  171. user_email=user
  172. )
  173. # Still maintain text file for backward compatibility
  174. llm_log_file = os.path.join(structured_logger.logs_dir, 'llm_responses.txt')
  175. file_mode = "a" if os.path.exists(llm_log_file) else "w"
  176. with open(llm_log_file, file_mode, encoding='utf-8') as f:
  177. f.write(f"{datetime.now().isoformat()} - {user}: {response}\n")
  178. except Exception as e:
  179. logger.error(f"Failed to log LLM response: {e}")