If you’ve spent some time with Django
chances are you’ve run across its
signals system.
It is most commonly used to provide hooks in to the model setup and CRUD
processes, but there are many other uses of the facility throughout the
code including auth, db, and the request life-cycle. What it doesn’t
offer out of the box is a signal that’s called during startup after your
models have been loaded.
There are many potential uses of a startup signal, but for the purposes
of this post we’ll use this signal for something that’s invaluable when
debugging production applications. We’ll use it to log the application’s
current version and environment (on startup.)
We will delve much deeper in to Django settings in a future post, but
for now let’s assume we have both settings.VERSION and settings.ENV
configured with the appropriate values for the Django apps to make use
of.
Creating custom signals is a one-liner and we’ll create a signals.py
file in which it’ll live, the contents of which will look something like
this.
#
#
#
from __future__ import absolute_import
from django.dispatch import Signal
# create our startup signal and specify the two arguments we'll be passing
# to it
startup_signal = Signal(providing_args=('version', 'env'))
That’s all there is to defining the signal, now the trick in this case
is picking a good place to do the send. One of the best options is at
the end of your last app’s models.py file. It will be one of the last
things imported and by that point the bulk of Django’s setup will be
complete (including settings and logging, which is important for our
use-case.)
We’ll add the following to our models.py file.
#
#
#
from __future__ import absolute_import
from django.conf import settings
from startup.foo.signals import startup_signal
...
# we've started up, models have been loaded, send the signal
startup_signal.send(sender=startup_signal,
version=settings.VERSION, env=settings.ENV)
We’re just about done, the only thing remaining is to connect our signal
handler(s.) In this trivial case this code can live just above the
signal send, but the beauty of using the signal sub-system is that it
can go where ever you need it so long as it is imported before the final
models.py file. You can also have as many callbacks connected as you
need. The callback we’ll use to log our version and environment
information is as follows.
#
#
#
from __future__ import absolute_import
from startup.foo.signals import startup_signal
import logging
logger = logging.getLogger(__name__)
...
def on_startup(sender, version, env, **kwargs):
logger.info('startup: running version=%s, with env=%s',
version, env)
# example
startup_signal.connect(on_startup, dispatch_uid='models-startup')
And that’s it, a quick example of defining a custom signal in Django
that is sent out during startup and with an added bonus of getting the
version and environment in to the logs. As always, feel free to comment
or email with any questions or suggestions.