Coverage for /Users/davegaeddert/Development/dropseed/plain/plain/plain/logs/loggers.py: 35%

46 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-10-16 22:03 -0500

1import logging 

2 

3app_logger = logging.getLogger("app") 

4 

5 

6class KVLogger: 

7 def __init__(self, logger): 

8 self.logger = logger 

9 self.context = {} # A dict that will be output in every log message 

10 

11 def log(self, level, message, **kwargs): 

12 msg_kwargs = { 

13 **kwargs, 

14 **self.context, # Put these last so they're at the end of the line 

15 } 

16 self.logger.log(level, f"{message} {self._format_kwargs(msg_kwargs)}") 

17 

18 def _format_kwargs(self, kwargs): 

19 outputs = [] 

20 

21 for k, v in kwargs.items(): 

22 self._validate_key(k) 

23 formatted_value = self._format_value(v) 

24 outputs.append(f"{k}={formatted_value}") 

25 

26 return " ".join(outputs) 

27 

28 def _validate_key(self, key): 

29 if " " in key: 

30 raise ValueError("Keys cannot have spaces") 

31 

32 if "=" in key: 

33 raise ValueError("Keys cannot have equals signs") 

34 

35 if '"' in key or "'" in key: 

36 raise ValueError("Keys cannot have quotes") 

37 

38 def _format_value(self, value): 

39 if isinstance(value, str): 

40 s = value 

41 else: 

42 s = str(value) 

43 

44 if '"' in s: 

45 # Escape quotes and surround it 

46 s = s.replace('"', '\\"') 

47 s = f'"{s}"' 

48 elif s == "": 

49 # Quote empty strings instead of printing nothing 

50 s = '""' 

51 elif any(char in s for char in [" ", "/", "'", ":", "=", "."]): 

52 # Surround these with quotes for parsers 

53 s = f'"{s}"' 

54 

55 return s 

56 

57 def info(self, message, **kwargs): 

58 self.log(logging.INFO, message, **kwargs) 

59 

60 def debug(self, message, **kwargs): 

61 self.log(logging.DEBUG, message, **kwargs) 

62 

63 def warning(self, message, **kwargs): 

64 self.log(logging.WARNING, message, **kwargs) 

65 

66 def error(self, message, **kwargs): 

67 self.log(logging.ERROR, message, **kwargs) 

68 

69 def critical(self, message, **kwargs): 

70 self.log(logging.CRITICAL, message, **kwargs) 

71 

72 

73# Make this accessible from the app_logger 

74app_logger.kv = KVLogger(app_logger)