How to insert Django data on Nginx logs? - django

Im thinking on how to retrieve Django user data on the user authetication class and pass it to Nginx session variables, then on the nginx logging settings use that data to create a Nginx access log entry that contains the Django user that create such a request.
I have found these ideas:
Get current request by Django's or Python threading
https://gist.github.com/vparitskiy/71bb97b4fd2c3fbd6d6db81546622346
https://nedbatchelder.com/blog/201008/global_django_requests.html
Set a session variable:
How can I set and get session variable in django?
And then log the cookie variable via a Nginx configuration like:
https://serverfault.com/questions/223584/how-to-add-recently-set-cookies-to-nginxs-access-log
https://serverfault.com/questions/872375/what-is-the-difference-between-http-cookie-and-cookie-name-in-nginx
Any better idea?. I'm reinventing the wheel?

Finally I have done this. Place a middleware en Django that insert in the cookies the logging data that I want nginx to log.
Then I used the $upstream_cookies_NAME to rescue the COOKIES['NAME'] if any.

have you read django's documentation on logging?
I haven't worked with nginx, yet, but with apache djangos default logger also outputs to the apache log, meaning that you can do this:
from logging import getLogger
logger = getLogger('django')
def my_view(request):
logger.info(f'my view: {request.user}')
which will output the user to the server log.

Related

How to link Django and React URLs to perform actions?

In Django, I have my login URL set to 'api/auth/login'. When given a username and password, it will log that user in. Running 'python manage.py runserver', it will put that URL at 'http://127.0.0.1:8000/api/auth/login'
However, my React project, when running 'yarn start' is at 'http://localhost:3000/' and giving it the extension 'api/auth/login', the url it attempts is 'http://localhost:3000/api/auth/login'.
This does not work, but if I replace the URL extension with 'http://127.0.0.1:8000/api/auth/login', the login works as expected.
How can I have the URLs work with my React app? Or is this not something that necessarily needs to be fixed? I do plan to host this project somewhere and am not yet sure how the URLs will work out..
One option is to set proxy in the package.json.
Second option is to set axois baseURL:
// add in your code before main component definition
// for example in App.js
import axios from "axios";
axios.defaults.baseURL = "http://127.0.0.1:8000";
I'm preferring the second approach. For doing production build it can be easily overwritten with an environment variable.
import axios from "axios";
axios.defaults.baseURL = REACT_APP_SERVER_URL
Remember to set the CORS headers in the Django server (I'm working on tutorial with an example how to do this).
You are hosting react application on another port that's why when you put a request without mentioning host, it gets appended to your current host i.e. 127.0.0.1:3000. I suggest you write "proxy": '127. 0.0.1:8000' in your package.json (refer https://create-react-app.dev/docs/proxying-api-requests-in-development/) and restart your react server or use full url of django server i.e. 127.0.0.1:8000/

Get HTTP Request like transfered over the wire (Django)

Is it possible to get the http request as bytestring like it gets transferred over the wire if you have a django request object?
Of course the plain text (not encrypted if https gets used).
I would like to store the bytestring to analyze it later.
At best I would like to access the real bytestring. Creating a bytestring from request.META, request.GET and friends will likely not be the same like the original.
Update: it seems that it is impossible to get to the original bytes. Then the question is: how to construct a bytestring which roughly looks like the original?
As others pointed out it is not possible because Django doesn't interact with raw requests.
You could just try reconstructing the request like this.
def reconstruct_request(request):
headers = ''
for header, value in request.META.items():
if not header.startswith('HTTP'):
continue
header = '-'.join([h.capitalize() for h in header[5:].lower().split('_')])
headers += '{}: {}\n'.format(header, value)
return (
'{method} HTTP/1.1\n'
'Content-Length: {content_length}\n'
'Content-Type: {content_type}\n'
'{headers}\n\n'
'{body}'
).format(
method=request.method,
content_length=request.META['CONTENT_LENGTH'],
content_type=request.META['CONTENT_TYPE'],
headers=headers,
body=request.body,
)
NOTE this is not a complete example only proof of concept
The basic answer is no, Django doesn't have access to the raw request, in fact it doesn't even have code to parse raw HTTP request.
This is because Django's (like many other Python web frameworks) HTTP request/response handling is, in it's core, a WSGI application (WSGI specification).
It's the job of the frontend/proxy server (like Apache or nginx) and application server (like uWSGI or gunicorn) to "massage" the request (like transforming and stripping headers) and convert it into an object that can be handled by Django.
As an experiment you can actually wrap Django's WSGI application yourself and see what Django gets to work with when a request comes in.
Edit your project's wsgi.py and add some extremely basic WSGI "middleware":
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
class MyMiddleware:
def __init__(self, app):
self._app = app
def __call__(self, environ, start_response):
import pdb; pdb.set_trace()
return self._app(environ, start_response)
# Wrap Django's WSGI application
application = MyMiddleware(get_wsgi_application())
Now if you start your devserver (./manage.py runserver) and send a request to your Django application. You'll drop into a debugger.
The only thing of interest here is the environ dict. Poke around it and you'll see that it's pretty much the same as what you'll find in Django's request.META. (The contents of the environ dict is detailed in this section of the WSGI spec.)
Knowing this, the best thing you can get is piecing together items form the environ dict to something that remotely resembles an HTTP request.
But why? If you have an environ dict, you have all the information you need to replicate a Django request. There's no actual need to translate this back to a HTTP request.
In fact, as you now known, you don't need a HTTP request at all to call Django's WSGI application. All you need is a environ dict with the required keys and a callable so that Django can relay the response.
So, to analyze requests (and even be able to replay them) you only need to be able to recreate a valid environ dict.
To do so in Django the easiest option would be to serialize request.META and request.body to a JSON dict.
If you really need something that resembles an HTTP request (and you are unable to go a level up to e.g. the webserver to log this information) you'll just have to piece this together from the information available in request.META and request.body, with the caveats that this is not a realistic representation of the original HTTP request.

Override settings in django tests

I try to:
#override_settings(EMAIL_HOST_PASSWORD='sdsds')
def test_email_not sent(self):
....
I want to brake down Django SMTP settings and test, but setting didn't get overrided.
By default, Django uses a local memory backend for emails when testing. See the docs for more info.
If you want to use the smtp backend, you can override the setting.
#override_settings(EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend', EMAIL_HOST_PASSWORD='sdsds')
def test_email_not sent(self):
...

Save django request logs in a file

Django shows the requests logs in command line, I want to save that logs in a file. I found a solution here but it saves the logs only that we print manually. Can we save all of the request and response logs in a separate file?
To log the Django request debug events, check out my answer here about using the fail-nicely-django config. You will also get 4xx and 5xx request events logged from Django's default loggers.
Note, though, that 2xx requests generally don't get logged by Django, if that's what you are after (only runserver shows them in the console). There is a workaround mentioned here, but you should generally use your server's access logs for these in production (e.g. access.log in nginx/Apache).
This should work on both linux and windows I hope:
python manage.py runserver > logs.txt

Missing logs in django

I have a django app on Webfaction, and it seems that logs it is sending do not get written in the log file.
I have the following code:
log.debug('batch, guid: %s' % request.POST['guid'])
events = request.POST.getlist('logbatch[]')
for event in events:
a = event.split(';')
userlog = UserLog(csq_id=CsqId.objects.get(guid=request.POST['guid']), elementId=a[1], event=a[0], counter=int(a[2]), clientTime=js_time_to_python(int(a[3])))
userlog.save()
In general, logging works and is configured correctly. However, when I look in the database, I can see UserLog objects that are not logged in. As the DB insertion happens after the logging, I am certain that django did log the code. So why is it not in the log file?
Read the Django documentation on logging. You need to configure logging in your settings.py first, before you start using it. There is also an example configuration provided in the documentation.