Coverage for src/app.py: 0%
58 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-12-20 11:53 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-12-20 11:53 +0000
1import logging
2import signal
3import sys
4from os import environ
5from tempfile import TemporaryDirectory
7from couchers.i18n.i18n import get_translations
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
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
20from couchers.config import check_config, config
21from couchers.db import apply_migrations, session_scope
22from couchers.experimentation import setup_experimentation
23from couchers.jobs.worker import start_jobs_scheduler, start_jobs_worker
24from couchers.metrics import create_prometheus_server
25from couchers.server import create_main_server, create_media_server
26from couchers.tracing import setup_tracing
27from dummy_data import add_dummy_data
29check_config(config)
31logging.basicConfig(
32 format="[%(process)5d:%(thread)20d] %(asctime)s: %(name)s:%(lineno)d: %(message)s", level=logging.INFO
33)
34logger = logging.getLogger(__name__)
36if config["SENTRY_ENABLED"]:
37 # Sends exception tracebacks to Sentry, a cloud service for collecting exceptions
38 sentry_sdk.init(
39 config["SENTRY_URL"],
40 traces_sample_rate=0.0,
41 environment=config["COOKIE_DOMAIN"],
42 release=config["VERSION"],
43 default_integrations=False,
44 integrations=[
45 # we need to manually list out the integrations, there is no other way of disabling the global excepthook integration
46 # we want to disable that because it seems to be picking up already handled gRPC errors (e.g. grpc.StatusCode.NOT_FOUND)
47 argv.ArgvIntegration(),
48 atexit.AtexitIntegration(),
49 dedupe.DedupeIntegration(),
50 sentry_logging.LoggingIntegration(),
51 modules.ModulesIntegration(),
52 stdlib.StdlibIntegration(),
53 threading.ThreadingIntegration(),
54 ],
55 )
57# used to export metrics
58create_prometheus_server(8000)
61def log_unhandled_exception(exc_type, exc_value, exc_traceback):
62 """Make sure that any unhandled exceptions will write to the logs"""
63 if issubclass(exc_type, KeyboardInterrupt):
64 # call the default excepthook saved at __excepthook__
65 sys.__excepthook__(exc_type, exc_value, exc_traceback)
66 return
67 logger.critical("Unhandled exception", exc_info=(exc_type, exc_value, exc_traceback))
70sys.excepthook = log_unhandled_exception
72logger.info("Checking DB connection")
74with session_scope() as session:
75 res = session.execute(text("SELECT 42;"))
76 if list(res) != [(42,)]:
77 raise Exception("Failed to connect to DB")
79logger.info("Running DB migrations")
81apply_migrations()
83get_translations()
85if config["ADD_DUMMY_DATA"]:
86 add_dummy_data()
88logger.info("Starting")
90if config["ROLE"] in ["scheduler", "all"]:
91 scheduler = start_jobs_scheduler()
93if config["ROLE"] in ["worker", "all"]:
94 for _ in range(config["BACKGROUND_WORKER_COUNT"]):
95 start_jobs_worker()
97setup_tracing()
99# Initialize experimentation framework for feature flags in the main process.
100# IMPORTANT: This MUST be called AFTER worker processes are spawned (above).
101# The underlying SDK uses internal threading that doesn't survive fork().
102# Worker processes initialize their own instance in _run_forever().
103setup_experimentation()
105if config["ROLE"] in ["api", "all"]:
106 server = create_main_server(port=1751)
107 server.start()
108 media_server = create_media_server(port=1753)
109 media_server.start()
110 logger.info("Serving on 1751 (secure) and 1753 (media)")
112logger.info("App waiting for signal...")
114signal.pause()