Wellfire Interactive // Expertise for established Django SaaS applications

I heard you liked logging from your app so I put logging in your app! (This Old Pony #23)

Logging is important for providing insight _into _your application _from outside _of your application.

But sometimes this isn’t enough and you need to have this information from within the app itself.

Wise words to live by

When and how has this object changed?

Keeping in mind that we’re not, strictly speaking, talking about logging but logging-like activities, the most straightforward kind of track record here is an individual item action history.

This is useful for objects that have some kind of state change, like subscriptions in a SaaS app or content with approval stages. Each time the state of the model creates a new instance of a model-specific log.

This code is the equivalent of a napkin sketch, but demonstrates the point.

​class Application(models.Model):   status = models.CharField( ... choices ) ...   def \_\_init\_\_(self, \*args, \*\*kwargs):   super().\_\_init\_\_(\*args, \*\*kwargs)   self.\_cached\_status = self.status   def save(self):   if self.\_cached\_status != self.status:   self.logs.create(             previous\_status=self.\_cached\_status, new\_status=self.status, )   super().save()   class ApplicationLog(models.Model):   application = models.ForeignKey(   'Application',   related\_name='logs',   )   previous\_status = models.CharField( ... choices )   new\_status = models.CharField( ... choices )

This history can then be accessed in the Django admin or displayed on non-admin pages.

It was left out of the example, but you’ll want to add automatic timestamps to every entry. And naturally there are existing apps which you can use[1].

Who did what and when did they do it? An object history tracker is a simple form of audit trail. To extend this, you need to add the user  to the log.

In-app audit logging is different from history logging by a difference of shade rather than category. There are apps out there that will take care of basic model change auditing for you[2].

If you’re tracking a limited set of your application and/or need very specific data about the changes, you may find it better to write your own little audit code.

What’s going on around the world?

Now, the game changes if you want to see more a more human friendly history and across models.

An activity stream is commonly used to display updates as primary content for users, like a social media feed, but it doesn’t have to be used that way. It can also be used to display a friendly log of actions on a site for anyone else, including staff users.

With an activity stream there’s usually a cleaner break between the core models and the recording models, unlike the previous suggested strategies which typically rely on a foreign key or at least generic foreign key to tie a record to change.

Who and what is it for?

What you need to do and to what extent is going to depend on the specific problem you’re trying to solve, and who you’re solving it for.

If you’re solving a problem for end users, the most likely solutions will be an object history or an activity stream. And which of course depends on what problem for them you’re trying to solve. If it’s awareness of what’s going on and what they or colleagues have done, then an activity stream is a good option. If it’s a mini audit trail for an important model then an object history is your best bet.

For staff users you usually need to provide either some kind of general context of app activity or audit history to back up support requests. For the former, again, an activity stream is your best bet, and for the latter a slightly more robust audit trail is the best bet.

An activity stream will solve both, but not necessarily in a way that allows nice tracking backing to objects in question, which is why it’s not a panacea for these issues.

Where should we keep it?

The simplest answer is the database. That’s necessary in the case of a solution that relies on a true foreign key, of course, but there are other solutions[3].

This isn’t the kind of data that makes sense to store in files, regardless of what they’re stored.

Another option would be Redis. You’d want to perform periodic cleanup - as you would with a database solution - but it lends itself very nicely to just this kind of problem.

I heard you liked email sign offs so I signed off in your email sign off,

[0] I’m pretty sure Xzibit never said that, but you never know.
[1] The examples using a status field make a good use case for a state machine like django-fsm, and in that case django-fsm-log may fit the bill
[2] See the Model Auditing and History grid  on Django Packages
[3] There’s nothing I’ve seen that does this out of the box, but there’s little reason I can think of why you coudn’t use something like ElasticSearch for this purpose.

Learn from more articles like this how to make the most out of your existing Django site.