Background
I am trying to write logs in Django that not only stores the messages but also necessary information of the requests like IP address and user in the logs.
I noticed that I could use the extra
attribute of logger to include request
like logger.info("my message", extra={'ip': request.ip, 'user': request.user}
. But this will be extremely cumbersome to include this extra block for every log in my app. It will also be unmanageable if I wish to add an extra attribute to display in the future.
Based on my understanding LogAdapter is only useful when the extra context is static.
I tried to follow How can I add the currently logged in username to the access log of django.server? to use a custom middleware to store request object in thread local storage and read the object from my custom filter.
import logging
import threading
local = threading.local()
class RequestFilter(logging.Filter):
def filter(self, record):
request = getattr(local, 'request', None)
if request:
record.ip = request.META.get('REMOTE_ADDR')
record.user = request.user.username
else:
record.ip = '-'
record.user = '-'
return True
class LogRequestMiddleware:
"""
A middleware that stores request object into the thread local storage,
so that logging filter can access it and populate necessary information to the logs
"""
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
setattr(local, 'request', request)
return self.get_response(request)
Question
thread.local
method worked but I wonder if this approach is safe in production as I am using nginx+gunicorn+django in production. Is there a potential risk that the thread local storage may be overwritten by other treads?I have the following LOGGING configuration, which writes the log to a file. Will there be a race condition that multiple threads tries to write to the file the same time?
I believe my question should be a common one in django development, but I can hardly find any resource on this question. I wonder if there is a better approach of doing this. Thanks ahead!
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
"simple": {
"format": "{levelname} {asctime} {ip} {funcName} {user} {message}",
"style": "{",
},
},
'filters': {
'ip_filter': {
'()': 'EngMemo.logging.RequestFilter',
}
},
'handlers': {
'file': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': LOGS_DIR + 'api.log',
'maxBytes': 1024 * 1024, # 1 MB
'backupCount': 5, # Number of backup log files to keep
'formatter': 'simple',
'filters': ['ip_filter'],
},
},
'loggers': {
'api_log': {
'handlers': ['file'],
'level': 'INFO',
'propagate': True,
},
},
}