Home

Awesome

django-vitals

A django app that provides health check endpoints for vital services.

Build Status Coverage Status

It is often useful to get the status of services from the perspective of the application that needs them. django-vitals provides a simple mechanism for writing a running health checks. These checks can then be exposed via an endpoint:


{
    "ok": [
        "DatabaseCheck",
        "CacheCheck",
        "StorageCheck"
    ],
    "failed": {}
}

Which in the above case, would return a status code of 200

If something is down:

{
    "ok": [
        "DatabaseCheck",
        "CacheCheck",
        "StorageCheck"
    ],
    "failed": {
        "OtherMicroServiceEndpointCheck": [
            "Endpoint http://suchabadsite11112222.com unreachable: Failed to establish a new connection: [Errno -2] Name or service not known"
        ]
    }
}

a http code 500 is returned instead.

Subsets of checks can be run by passing the check parameter to the endpoint: ?checks=DatabaseCheck,CacheCheck

This can be particularity useful when used along with load balancers, container orchestration and other infrastructure tools that can take automatic actions when problems arise.

Requirements

Tested with all combinations of:

Python 2.7 probably works, but I have no interest in testing it. Support will be on a best effort basis.

Installation

Install the package from pypi:

pip install django-vitals

Add vitals to INSTALLED_APPS:

INSTALLED_APPS = (
    ...
    'vitals'
    ...
)

Add vitals.urls to your urlconf:

urlpatterns = [
    ...
    url(r'^healthz/', include('vitals.urls', namespace='vitals')),
    ...
]

Visit the url in your browser.

That's it if all you want is the default checks (DatabaseCheck, CacheCheck, StorageCheck) HTTPCheck is included but not enabled by default.

Configuration

By default 3 health checks are run: DatabaseCheck, CacheCheck and StorageCheck. Add VITALS_ENABLED_CHECKS to your settings.py to customize:

VITALS_ENABLED_CHECKS = [
    {
        'NAME': 'DatabaseCheck',
        'CLASS': 'vitals.checks.DatabaseCheck'
    },
    {
        'NAME': 'CacheCheck',
        'CLASS': 'vitals.checks.CacheCheck'
    },
    {
        'NAME': 'StorageCheck',
        'CLASS': 'vitals.checks.StorageCheck'
    },
    {
        'NAME': 'HTTPCheck',
        'CLASS': 'vitals.checks.HTTPCheck',
        'OPTIONS': {
            'url': 'https://google.com'
        }
    }
]

Included Checks

DatabaseCheck

Options: None

Iterates over every database in settings.DATABASES and attempts to access the list of tables for each.

CacheCheck

Options: None

Iterates over every cache in settings.CACHES and attempts to set a value, get it and remove it.

StorageCheck

Options: None

Using the default storage backend, attempts to write a small file and delete it.

HTTPCheck

Options: url

Attempts to GET the url provided by the url option. Will fail if the GET results in anything other than a 200 response code.

Writing Custom Checks

You are not limited to the included health checks. To write a custom check, simply subclass BaseHealthCheck and implement the check() method:

from vitals.checks import BaseHealthCheck

class MyHealthCheck(BaseHealthCheck):
    def check(self):
        assert 2 == 2

Any exceptions thrown by your check will be added as errors. You can also manually add errors using self.add_error(error):

from vitals.checks import BaseHealthCheck

class MyHealthCheck(BaseHealthCheck):
    def check(self):
        try:
            assert 1 == 2
        except AssertionError as exc:
            self.add_error('Strange error! {}'.format(exc))

Arguments can be passed to health checks by setting the OPTIONS key in settings:

VITALS_ENABLED_CHECKS = [
     {
        'NAME': 'MyFailingHealthCheck',
        'CLASS': 'foo.MyHealthCheck',
        'OPTIONS': {
            'number': 3
        }
    }
]

They will be passed a kwargs to your class constructor:

from vitals.checks import BaseHealthCheck

class MyHealthCheck(BaseHealthCheck):
    def __init__(self, *args, **kwargs):
        self.number = kwargs.pop('number')
        super(MyHealthCheck, self).__init__(*args, **kwargs)

    def check(self):
        assert self.number == 2

Add your custom checks to VITALS_ENABLED_CHECKS in settings.py providing the path to them in the CLASS key.