Coverage for src / sentry_tool / monitoring.py: 95.65%

23 statements  

« prev     ^ index     » next       coverage.py v7.13.2, created at 2026-02-15 10:53 -0500

1"""Monitoring setup: Sentry error tracking and structlog logging. 

2 

3Logging goes to stderr to keep stdout clean for data output (piping). 

4Sentry is initialized after logging for self-monitoring. 

5DSN is read from SENTRY_DSN environment variable (optional for public distribution). 

6""" 

7 

8import logging 

9import os 

10import sys 

11from typing import Any 

12 

13import sentry_sdk 

14import structlog 

15 

16from sentry_tool.__about__ import __version__ 

17 

18_LOG_LEVELS: dict[str, int] = { 

19 "debug": logging.DEBUG, 

20 "info": logging.INFO, 

21 "warning": logging.WARNING, 

22 "error": logging.ERROR, 

23 "critical": logging.CRITICAL, 

24} 

25 

26 

27def setup_logging(verbose: bool = False) -> None: 

28 log_level = "debug" if verbose else "info" 

29 

30 structlog.configure( 

31 processors=[ 

32 structlog.contextvars.merge_contextvars, 

33 structlog.processors.add_log_level, 

34 structlog.processors.TimeStamper(fmt="iso"), 

35 structlog.dev.ConsoleRenderer(colors=sys.stderr.isatty()), 

36 ], 

37 wrapper_class=structlog.make_filtering_bound_logger(_LOG_LEVELS[log_level]), 

38 context_class=dict, 

39 logger_factory=structlog.PrintLoggerFactory(file=sys.stderr), 

40 cache_logger_on_first_use=True, 

41 ) 

42 

43 

44def get_logger(name: str | None = None) -> Any: 

45 logger = structlog.get_logger() 

46 if name: 

47 logger = logger.bind(logger=name) 

48 return logger 

49 

50 

51def setup_sentry(environment: str = "local") -> None: 

52 """Configure error tracking for this CLI tool instance. 

53 

54 Reads DSN from SENTRY_DSN environment variable. Skips initialization if unset, 

55 allowing the tool to run without error tracking in public distributions. 

56 """ 

57 dsn = os.environ.get("SENTRY_DSN") 

58 if not dsn: 

59 log = structlog.get_logger() 

60 log.info("Sentry DSN not configured, skipping error tracking setup") 

61 return 

62 

63 sentry_sdk.init( 

64 dsn=dsn, 

65 traces_sample_rate=0.03, 

66 environment=environment, 

67 release=__version__, 

68 attach_stacktrace=True, 

69 send_default_pii=False, 

70 )