Skip to content

Commit

Permalink
Merge pull request #174 from stuartmaxwell:healthcheck-app
Browse files Browse the repository at this point in the history
Healthcheck-app
  • Loading branch information
stuartmaxwell authored Dec 5, 2024
2 parents 4fd72f3 + d2ae01c commit d249197
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 1 deletion.
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)

0 comments on commit d249197

Please sign in to comment.