| """ |
| Logging utility with structured logging support |
| """ |
| import logging |
| import sys |
| from typing import Optional |
| from datetime import datetime |
|
|
|
|
| def get_logger( |
| name: str, |
| level: int = logging.INFO, |
| log_file: Optional[str] = None |
| ) -> logging.Logger: |
| """ |
| Get or create a logger with consistent formatting |
| |
| Args: |
| name: Logger name (usually __name__) |
| level: Logging level |
| log_file: Optional file path for file logging |
| |
| Returns: |
| Configured logger instance |
| """ |
| logger = logging.getLogger(name) |
| |
| |
| if logger.handlers: |
| return logger |
| |
| logger.setLevel(level) |
| |
| |
| formatter = logging.Formatter( |
| fmt='%(asctime)s | %(levelname)-8s | %(name)s | %(message)s', |
| datefmt='%Y-%m-%d %H:%M:%S' |
| ) |
| |
| |
| console_handler = logging.StreamHandler(sys.stdout) |
| console_handler.setLevel(level) |
| console_handler.setFormatter(formatter) |
| logger.addHandler(console_handler) |
| |
| |
| if log_file: |
| file_handler = logging.FileHandler(log_file) |
| file_handler.setLevel(level) |
| file_handler.setFormatter(formatter) |
| logger.addHandler(file_handler) |
| |
| return logger |
|
|
|
|
| class JobContextAdapter(logging.LoggerAdapter): |
| """ |
| Logger adapter that adds job context to all log messages |
| """ |
| |
| def process(self, msg, kwargs): |
| extra = kwargs.get('extra', {}) |
| extra.update(self.extra) |
| kwargs['extra'] = extra |
| return msg, kwargs |
|
|
|
|
| def get_job_logger(job_id: str, level: int = logging.INFO) -> logging.Logger: |
| """ |
| Get a logger with job context |
| |
| Args: |
| job_id: Unique job identifier |
| level: Logging level |
| |
| Returns: |
| Logger with job context |
| """ |
| logger = get_logger(f"job.{job_id}", level) |
| return JobContextAdapter(logger, {'job_id': job_id}) |
|
|