Coverage for src/app.py: 0%

56 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-17 18:19 +0000

1import logging 

2import signal 

3import sys 

4from os import environ 

5from tempfile import TemporaryDirectory 

6 

7from couchers.i18n.i18n import get_translations 

8 

9# these two lines need to be at the top of the file before we span child processes 

10# this temp dir will be destroyed when prometheus_multiproc_dir is destroyed, aka at the end of the program 

11prometheus_multiproc_dir = TemporaryDirectory() 

12environ["PROMETHEUS_MULTIPROC_DIR"] = prometheus_multiproc_dir.name 

13# ruff: noqa: E402 

14 

15import sentry_sdk 

16from sentry_sdk.integrations import argv, atexit, dedupe, modules, stdlib, threading 

17from sentry_sdk.integrations import logging as sentry_logging 

18from sqlalchemy.sql import text 

19 

20from couchers.config import check_config, config 

21from couchers.db import apply_migrations, session_scope 

22from couchers.jobs.worker import start_jobs_scheduler, start_jobs_worker 

23from couchers.metrics import create_prometheus_server 

24from couchers.server import create_main_server, create_media_server 

25from couchers.tracing import setup_tracing 

26from dummy_data import add_dummy_data 

27 

28check_config(config) 

29 

30logging.basicConfig( 

31 format="[%(process)5d:%(thread)20d] %(asctime)s: %(name)s:%(lineno)d: %(message)s", level=logging.INFO 

32) 

33logger = logging.getLogger(__name__) 

34 

35if config["SENTRY_ENABLED"]: 

36 # Sends exception tracebacks to Sentry, a cloud service for collecting exceptions 

37 sentry_sdk.init( 

38 config["SENTRY_URL"], 

39 traces_sample_rate=0.0, 

40 environment=config["COOKIE_DOMAIN"], 

41 release=config["VERSION"], 

42 default_integrations=False, 

43 integrations=[ 

44 # we need to manually list out the integrations, there is no other way of disabling the global excepthook integration 

45 # we want to disable that because it seems to be picking up already handled gRPC errors (e.g. grpc.StatusCode.NOT_FOUND) 

46 argv.ArgvIntegration(), 

47 atexit.AtexitIntegration(), 

48 dedupe.DedupeIntegration(), 

49 sentry_logging.LoggingIntegration(), 

50 modules.ModulesIntegration(), 

51 stdlib.StdlibIntegration(), 

52 threading.ThreadingIntegration(), 

53 ], 

54 ) 

55 

56# used to export metrics 

57create_prometheus_server(8000) 

58 

59 

60def log_unhandled_exception(exc_type, exc_value, exc_traceback): 

61 """Make sure that any unhandled exceptions will write to the logs""" 

62 if issubclass(exc_type, KeyboardInterrupt): 

63 # call the default excepthook saved at __excepthook__ 

64 sys.__excepthook__(exc_type, exc_value, exc_traceback) 

65 return 

66 logger.critical("Unhandled exception", exc_info=(exc_type, exc_value, exc_traceback)) 

67 

68 

69sys.excepthook = log_unhandled_exception 

70 

71logger.info("Checking DB connection") 

72 

73with session_scope() as session: 

74 res = session.execute(text("SELECT 42;")) 

75 if list(res) != [(42,)]: 

76 raise Exception("Failed to connect to DB") 

77 

78logger.info("Running DB migrations") 

79 

80apply_migrations() 

81 

82get_translations() 

83 

84if config["ADD_DUMMY_DATA"]: 

85 add_dummy_data() 

86 

87logger.info("Starting") 

88 

89if config["ROLE"] in ["scheduler", "all"]: 

90 scheduler = start_jobs_scheduler() 

91 

92if config["ROLE"] in ["worker", "all"]: 

93 for _ in range(config["BACKGROUND_WORKER_COUNT"]): 

94 start_jobs_worker() 

95 

96setup_tracing() 

97 

98if config["ROLE"] in ["api", "all"]: 

99 server = create_main_server(port=1751) 

100 server.start() 

101 media_server = create_media_server(port=1753) 

102 media_server.start() 

103 logger.info("Serving on 1751 (secure) and 1753 (media)") 

104 

105logger.info("App waiting for signal...") 

106 

107signal.pause()