Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Healthcheck-app #174

Merged
merged 2 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
DEBUGGING_APP_PATH=(str, "this-is-just-a-temporary-debugging-app-path"),
LOGFIRE_ENVIRONMENT=(str, "dev"),
BLUESKY_APP_PASSWORD=(str, ""),
HEALTHCHECK_PATH=(str, ""),
)

environ.Env.read_env(Path(BASE_DIR / ".env"))
Expand Down Expand Up @@ -76,6 +77,7 @@
"django.contrib.staticfiles",
"django.contrib.sitemaps",
"djpress.apps.DjpressConfig",
"healthcheck_app",
"timezone_converter",
"markdown_editor",
"shell",
Expand Down Expand Up @@ -325,4 +327,10 @@
# Logfire
LOGFIRE_ENVIRONMENT = env("LOGFIRE_ENVIRONMENT")
logfire.configure(environment=LOGFIRE_ENVIRONMENT)
logfire.instrument_django()
logfire.instrument_django(
capture_headers=True,
excluded_urls="/healthcheck",
)

# Healthcheck app
HEALTHCHECK_PATH = env("HEALTHCHECK_PATH")
1 change: 1 addition & 0 deletions config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

urlpatterns += [
path(f"{settings.ADMIN_URL}/", admin.site.urls),
path("healthcheck/", view=include("healthcheck_app.urls")),
path("contact/", view=include("contact_form.urls")),
path("utils/timezones/", view=include("timezone_converter.urls")),
path("utils/markdown-editor/", view=include("markdown_editor.urls")),
Expand Down
1 change: 1 addition & 0 deletions healthcheck_app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""healthcheck_app package."""
10 changes: 10 additions & 0 deletions healthcheck_app/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""App configuration for healthcheck_app."""

from django.apps import AppConfig


class HealthcheckAppConfig(AppConfig):
"""App configuration for the healthcheck app."""

default_auto_field = "django.db.models.BigAutoField"
name = "healthcheck_app"
23 changes: 23 additions & 0 deletions healthcheck_app/checks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Health checks for the healthcheck_app."""

import logging

from django.db import connections
from django.db.utils import OperationalError

logger = logging.getLogger(__name__)


def check_database() -> tuple[str, str | None]:
"""Perform the database connectivity check.

Returns a tuple of the status ('healthy' or 'unhealthy') and an optional error
message.
"""
try:
connections["default"].cursor() # You could use a specific database alias if needed
except OperationalError as e:
logger.debug(f"Health check found an error with the database: {e!s}")
return "unhealthy", "Error connecting to the database."
else:
return "healthy", None
Empty file.
14 changes: 14 additions & 0 deletions healthcheck_app/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""URLs for the healthcheck_app."""

from django.conf import settings
from django.urls import path

from .views import health_check

app_name = "healthcheck_app"

url_path = f"{settings.HEALTHCHECK_PATH}/" if settings.HEALTHCHECK_PATH else ""

urlpatterns = [
path(url_path, health_check, name="healthcheck"),
]
47 changes: 47 additions & 0 deletions healthcheck_app/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""Views for the healthcheck_app."""

import logging

from django.http import HttpRequest, JsonResponse
from django.utils import timezone

from healthcheck_app.checks import check_database

logger = logging.getLogger(__name__)

# Checks to perform, with their corresponding check functions
checks = {
"database": check_database,
}


def health_check(_: HttpRequest) -> JsonResponse:
"""Main function that returns the healthcheck.

Perform a health check on the application by checking various components.
Returns a JsonResponse with the health information of the components.
"""
# Initial health data structure
health_data = {
"status": "healthy", # Starts assuming everything is healthy
"timestamp": timezone.now().isoformat(),
"details": {},
}

# Iterate over the checks and perform them
for check_name, check_function in checks.items():
status, error = check_function()

# Update the component's status in the health data
health_data["details"][check_name] = {"status": status}

# If there's an error, add it to the component's data
if error:
health_data["details"][check_name]["error"] = error

# If any component is unhealthy, set overall status to 'unhealthy'
if status == "unhealthy":
health_data["status"] = "unhealthy"

# Return the JsonResponse with the health data and status code
return JsonResponse(health_data)
Loading