Supercharge Your Django Logging: Custom Filters for the Win

Saurav SharmaSaurav Sharma
2 min read

The Problem

Default Django logs are okay, but they lack juicy details like IP addresses, browsers, and user info. Wouldn't it be cool to have all that at your fingertips?

Enter Custom Logging Filters

Django lets us create custom filters to add extra info to our logs. Here's how:

  1. First, set up your logging config in settings.py. It'll look something like this:
LOGGING = {
    # ... other config ...
    "filters": {
        "custom_request_filter": {
            "()": "path.to.your.CustomRequestFilter",
        },
    },
    # ... more config ...
}
  1. Now, let's create our custom filter:
import logging
from django.core.exceptions import ObjectDoesNotExist


class CustomRequestFilter(logging.Filter):
    def filter(self, record):
        if hasattr(record, "request"):
            request = record.request  # type: ignore
            try:
                record.ip_address = request.META.get("REMOTE_ADDR", "-")
                record.device_type = request.META.get("HTTP_SEC_CH_UA_PLATFORM")
                # ex string - "Brave";v="129", "Not=A?Brand";v="8", "Chromium";v="129"
                record.browser = request.META.get("HTTP_SEC_CH_UA").split(";")[0]
            except AttributeError:
                record.browser = "-"
                record.device_type = "-"
                record.ip_address = "-"
            try:
                record.user = request.user.username if request.user.is_authenticated else "unauthenticated"
            except (ObjectDoesNotExist, AttributeError):
                record.user = "unauthenticated"
            record.http_status = getattr(record, "status_code", "-")
        else:
            record.ip_address = "-"
            record.user = "-"
            record.device_type = "-"
            record.browser = "-"
            record.http_status = "-"
        return True
  1. Apply the filter to your handlers:
"handlers": {
    "file": {
        # ... other config ...
        "filters": ["custom_request_filter"],
    },
},
  1. Update your formatter to use the new attributes:
"formatters": {
    "verbose": {
        "format": "{asctime} {ip_address} {user} {message}",
        "style": "{",
    },
},

Bonus: Logging in WebSocket Consumers

Don't forget about your WebSocket consumers! They need love too. Here's a quick tip:

class FocusSessionConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.request = self._generate_request_metadata()
        # ... other connect logic ...
        logger.info(f"{self.user.username} connected", extra={"request": self.request})

    def _generate_request_metadata(self):
        request = HttpRequest()
        # Populate request.META with relevant info
        return request

Here are some code snippets from how i use it in django channels context

consumer.py

class FocusSessionConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.user = self.scope["user"]
        self.username = self.scope["url_route"]["kwargs"]["username"]
        self.request = self._generate_request_metadata()
        await self.channel_layer.group_add(self.session_group_name, self.channel_name)  # type: ignore
        await self.accept()
        # othe code related to project
        logger.info(f"{self.user.username} connected to session '{self.session_id}'", extra={"request": self.request})
        await self.update_session_followers_list_to_all_clients()

    def _generate_request_metadata(self):
        request = HttpRequest()
        user_agent = str(self.scope["headers"][4][1].strip())
        try:
            ip_add = self.scope["client"][0]
        except Exception:
            ip_add = "-"
        try:
            user_os = user_agent.rsplit("(")[1].split(";")[0]
        except Exception:
            user_os = "-"
        request.META = {
            "REMOTE_ADDR": ip_add,
            "HTTP_SEC_CH_UA_PLATFORM": user_os,
        }
        request.user = self.user
        return request

Wrapping Up

With these custom filters, your logs will be bursting with useful info. Happy debugging, folks!

Remember, logging is like a good curry - it should be rich, flavorful, and help you find the source of the problem. Now go forth and log like a pro! 🚀📝

p.s - i am still learning so there could be some mistakes in this blog. please test your code after implementing.

1
Subscribe to my newsletter

Read articles from Saurav Sharma directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Saurav Sharma
Saurav Sharma

I am a Self Taught Backend developer With 3 Years of Experience. Currently, I am working at a tech Startup based in The Bahamas. Here are my skills so far - 💪Expert at - 🔹Python 🔹Django 🔹Django REST framework 🔹Celery ( for distributed tasks ) 🔹ORM ( Know how to write fast queries & design models ) 🔹Django 3rd party packages along with postgresQL and mysql as Databases. 🔹Cache using Redis & Memcache 🔹Numpy + OpenCV for Image Processing 🔹ElasticSearch + HayStack 🔹Linux ( Debian ) 😎 Working Knowledge - Html, CSS, JavaScript, Ajax, Jquery, Git ( GitHub & BitBucket ), Basic React & React Native, Linux ( Arch ), MongoDB, VPS 🤠 Currently Learning - 🔹More Deep Dive into Django 🔹Docker 🔹Making APIs more Robust 🔹NeoVim 🔹System Design ☺️ Will Be Learn in upcoming months - 🔹GraphQL 🔹 Rust language Other than above, there is not a single technology ever exists that i can't master if needed.