Coverage for session_mgmt_mcp/utils/logging.py: 41.18%

41 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-01 05:22 -0700

1#!/usr/bin/env python3 

2"""Structured logging utilities for session management.""" 

3 

4import json 

5import logging 

6import sys 

7from datetime import datetime 

8from pathlib import Path 

9 

10 

11class SessionLogger: 

12 """Structured logging for session management with context.""" 

13 

14 def __init__(self, log_dir: Path) -> None: 

15 self.log_dir = log_dir 

16 self.log_dir.mkdir(parents=True, exist_ok=True) 

17 self.log_file = ( 

18 log_dir / f"session_management_{datetime.now().strftime('%Y%m%d')}.log" 

19 ) 

20 

21 # Configure logger 

22 self.logger = logging.getLogger("session_management") 

23 self.logger.setLevel(logging.INFO) 

24 

25 # Avoid duplicate handlers 

26 if not self.logger.handlers: 26 ↛ 28line 26 didn't jump to line 28 because the condition on line 26 was never true

27 # File handler with structured format 

28 file_handler = logging.FileHandler(self.log_file) 

29 file_handler.setLevel(logging.INFO) 

30 

31 # Console handler for errors 

32 console_handler = logging.StreamHandler(sys.stderr) 

33 console_handler.setLevel(logging.ERROR) 

34 

35 # Structured formatter 

36 formatter = logging.Formatter( 

37 "%(asctime)s | %(levelname)s | %(funcName)s:%(lineno)d | %(message)s", 

38 ) 

39 file_handler.setFormatter(formatter) 

40 console_handler.setFormatter(formatter) 

41 

42 self.logger.addHandler(file_handler) 

43 self.logger.addHandler(console_handler) 

44 

45 def info(self, message: str, **context) -> None: 

46 """Log info with optional context.""" 

47 if context: 

48 message = f"{message} | Context: {json.dumps(context)}" 

49 self.logger.info(message) 

50 

51 def warning(self, message: str, **context) -> None: 

52 """Log warning with optional context.""" 

53 if context: 

54 message = f"{message} | Context: {json.dumps(context)}" 

55 self.logger.warning(message) 

56 

57 def error(self, message: str, **context) -> None: 

58 """Log error with optional context.""" 

59 if context: 

60 message = f"{message} | Context: {json.dumps(context)}" 

61 self.logger.error(message) 

62 

63 def debug(self, message: str, **context) -> None: 

64 """Log debug with optional context.""" 

65 if context: 

66 message = f"{message} | Context: {json.dumps(context)}" 

67 self.logger.debug(message) 

68 

69 

70def get_session_logger() -> SessionLogger: 

71 """Get the global session logger instance.""" 

72 claude_dir = Path.home() / ".claude" 

73 return SessionLogger(claude_dir / "logs")