Skip to content

Commit e5d9e22

Browse files
author
happy-devs
committed
configure application with bugsnag
1 parent cc3104b commit e5d9e22

6 files changed

Lines changed: 537 additions & 111 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,4 @@ create_test_user.sql
145145
/.prs
146146
Taskfile.yml
147147
.env.local
148+
/.logs

main.py

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,32 @@
33
the frontend platform with the backend database.
44
"""
55

6+
import os
7+
import logging
8+
import datetime
69
from dotenv import load_dotenv
7-
from fastapi import Depends, FastAPI
10+
from fastapi import Depends, FastAPI, Request
811
from fastapi.middleware.cors import CORSMiddleware
12+
from fastapi.responses import JSONResponse
913

1014
from src import routers
1115
from src.authentication import authenticate_user
1216
from src.config.logging_config import setup_logging
17+
from src.bugsnag_config import configure_bugsnag, setup_bugsnag_logging, get_bugsnag_middleware, BUGSNAG_ENABLED
1318

19+
# Load environment variables and set up logging
1420
load_dotenv()
1521
setup_logging()
1622

23+
# Get application version
24+
app_version = os.environ.get("RELEASE_VERSION", "dev")
25+
app_env = os.environ.get("ENVIRONMENT", "development")
26+
logging.info(f"Starting application - version: {app_version}, environment: {app_env}")
27+
28+
# Configure Bugsnag for error tracking
29+
configure_bugsnag()
30+
setup_bugsnag_logging()
31+
1732
app = FastAPI(
1833
debug=False,
1934
title="Future Trends and Signals API",
@@ -60,6 +75,64 @@
6075
allow_headers=["*"],
6176
)
6277

78+
# Add Bugsnag exception handling middleware
79+
app = get_bugsnag_middleware(app)
80+
81+
# Add global exception handler to report errors to Bugsnag
82+
@app.exception_handler(Exception)
83+
async def global_exception_handler(request: Request, exc: Exception):
84+
logging.error(f"Unhandled exception: {str(exc)}", exc_info=True)
85+
86+
if BUGSNAG_ENABLED:
87+
import bugsnag
88+
bugsnag.notify(
89+
exc,
90+
metadata={
91+
"request": {
92+
"url": str(request.url),
93+
"method": request.method,
94+
"headers": dict(request.headers),
95+
"client": request.client.host if request.client else None,
96+
}
97+
}
98+
)
99+
100+
return JSONResponse(
101+
status_code=500,
102+
content={"detail": "Internal server error"},
103+
)
104+
63105

64106
for router in routers.ALL:
65107
app.include_router(router=router, dependencies=[Depends(authenticate_user)])
108+
109+
# Add diagnostic endpoint for health checks and Bugsnag verification
110+
@app.get("/_health", include_in_schema=False)
111+
async def health_check():
112+
"""Health check endpoint that also shows the current environment and version."""
113+
return {
114+
"status": "ok",
115+
"environment": app_env,
116+
"version": app_version,
117+
"bugsnag_enabled": BUGSNAG_ENABLED
118+
}
119+
120+
# Test endpoint to trigger a test error report to Bugsnag if enabled
121+
@app.get("/_test-error", include_in_schema=False)
122+
async def test_error():
123+
"""Trigger a test error to verify Bugsnag is working."""
124+
if BUGSNAG_ENABLED:
125+
import bugsnag
126+
bugsnag.notify(
127+
Exception("Test error triggered via /_test-error endpoint"),
128+
metadata={
129+
"test_info": {
130+
"environment": app_env,
131+
"version": app_version,
132+
"timestamp": str(datetime.datetime.now())
133+
}
134+
}
135+
)
136+
return {"status": "error_reported", "message": "Test error sent to Bugsnag"}
137+
else:
138+
return {"status": "disabled", "message": "Bugsnag is not enabled"}

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ pillow ~= 11.0.0
1414
beautifulsoup4 ~= 4.12.3
1515
lxml ~= 5.3.0
1616
openai == 1.52.2
17+
bugsnag>=4.0.0

src/bugsnag_config.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"""
2+
Bugsnag configuration module for error tracking.
3+
"""
4+
5+
import os
6+
import logging
7+
import bugsnag
8+
from bugsnag.handlers import BugsnagHandler
9+
from bugsnag.asgi import BugsnagMiddleware
10+
11+
# Get API key from environment variable with fallback to the provided key
12+
BUGSNAG_API_KEY = os.environ.get("BUGSNAG_API_KEY")
13+
ENVIRONMENT = os.environ.get("ENVIRONMENT", "development")
14+
RELEASE_VERSION = os.environ.get("RELEASE_VERSION", "dev")
15+
16+
if not BUGSNAG_API_KEY:
17+
logging.warning("BUGSNAG_API_KEY is not set - error reporting will be disabled")
18+
BUGSNAG_ENABLED = False
19+
else:
20+
BUGSNAG_ENABLED = True
21+
22+
def configure_bugsnag():
23+
"""Configure Bugsnag for error tracking."""
24+
if not BUGSNAG_ENABLED:
25+
logging.warning("Bugsnag is disabled - skipping configuration")
26+
return
27+
28+
bugsnag.configure(
29+
api_key=BUGSNAG_API_KEY,
30+
project_root=os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
31+
release_stage=ENVIRONMENT,
32+
app_version=RELEASE_VERSION,
33+
notify_release_stages=["production", "staging", "development"],
34+
app_type="fastapi",
35+
)
36+
37+
logging.info(f"Bugsnag configured - environment: {ENVIRONMENT}, version: {RELEASE_VERSION}")
38+
39+
def setup_bugsnag_logging(level=logging.ERROR):
40+
"""
41+
Set up Bugsnag to capture logs at specified level.
42+
43+
Parameters
44+
----------
45+
level : int
46+
Minimum log level to send to Bugsnag
47+
"""
48+
if not BUGSNAG_ENABLED:
49+
return
50+
51+
logger = logging.getLogger()
52+
handler = BugsnagHandler()
53+
handler.setLevel(level)
54+
logger.addHandler(handler)
55+
56+
logging.info(f"Bugsnag logging handler added at level {level}")
57+
58+
def get_bugsnag_middleware(app):
59+
"""
60+
Wrap an ASGI app with Bugsnag middleware.
61+
62+
Parameters
63+
----------
64+
app : ASGI application
65+
The FastAPI application instance
66+
67+
Returns
68+
-------
69+
ASGI application
70+
The application wrapped with Bugsnag middleware
71+
"""
72+
if not BUGSNAG_ENABLED:
73+
logging.warning("Bugsnag middleware not added - Bugsnag is disabled")
74+
return app
75+
76+
return BugsnagMiddleware(app)

0 commit comments

Comments
 (0)