Add Logging

This commit is contained in:
sosokker 2023-09-15 01:33:09 +07:00
parent 0d42e56139
commit c3b5984e33
6 changed files with 170 additions and 6 deletions

View File

@ -6,8 +6,8 @@ on the [Django Tutorial project](https://docs.djangoproject.com/en/4.2/intro/tut
## Install and Run
### Run Setup.py Method
Clone this repository and Run `setup.py` to install and run the project
1. Install [Python 3.11 or later](https://www.python.org/downloads/)
2. Clone this repository and Run `setup.py` to install and run the project
**Don't forget to answer the question from `setup.py` to setup the project**
```bash

View File

@ -10,6 +10,8 @@ For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.2/ref/settings/
"""
import logging
import os
from pathlib import Path
from decouple import config, Csv
@ -70,6 +72,95 @@ TEMPLATES = [
WSGI_APPLICATION = 'mysite.wsgi.application'
# * Loggin template from https://www.youtube.com/watch?v=m_EkU56KdJg, Modify a bit.
LOGS_DIR = os.path.join(BASE_DIR, 'logs')
if not os.path.exists(LOGS_DIR):
os.makedirs(LOGS_DIR)
# ! LOGGERS -> entry point into the logging system.
LOGGERS = (
{
"django": {
"handlers": ["console_handler", "info_handler"],
"level": "INFO",
},
"django.request": {
"handlers": ["error_handler"],
"level": "INFO",
"propagate": True,
},
"django.template": {
"handlers": ["error_handler"],
"level": "DEBUG",
"propagate": True,
},
"django.server": {
"handlers": ["error_handler"],
"level": "INFO",
"propagate": True,
},
},
)
# ! FORMATTER -> a log record needs to be rendered as text. Formatters describe the exact format of that text.
FORMATTERS = (
{
"verbose": {
"format": "{levelname} {asctime:s} {name} {threadName} {thread:d} {module} {filename} {lineno:d} {name} {funcName} {process:d} {message}",
"style": "{",
},
"simple": {
"format": "{levelname} {asctime:s} {name} {module} {filename} {lineno:d} {funcName} {message}",
"style": "{",
},
},
)
# ! HANDLERS -> The handler is the engine that determines what happens to each message in a logger
HANDLERS = {
# StreamHandler -> sends log messages to the console
"console_handler": {
"class": "logging.StreamHandler",
"formatter": "simple",
"level": "DEBUG"
}, # RotatingFileHandlers -> write log messages to files
"info_handler": {
"class": "logging.handlers.RotatingFileHandler",
"filename": f"{BASE_DIR}/logs/blogthedata_info.log",
"mode": "a",
"encoding": "utf-8",
"formatter": "verbose",
"level": "INFO",
"backupCount": 5,
"maxBytes": 1024 * 1024 * 5, # 5 MB
},
"error_handler": {
"class": "logging.handlers.RotatingFileHandler",
"filename": f"{BASE_DIR}/logs/blogthedata_error.log",
"mode": "a",
"formatter": "verbose",
"level": "WARNING",
"backupCount": 5,
"maxBytes": 1024 * 1024 * 5, # 5 MB
},
}
# ! LOGGING
LOGGING = {
"version": 1,
"disable_existing_loggers": False, # If set to True, it disables all loggers from previous configurations
"formatters": FORMATTERS[0],
"handlers": HANDLERS,
"loggers": LOGGERS[0],
}
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases

View File

@ -4,4 +4,6 @@ from django.apps import AppConfig
class PollsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'polls'
def ready(self) -> None:
import polls.signals

View File

@ -1,3 +1,6 @@
import logging
from typing import Any
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
@ -5,7 +8,7 @@ from django.contrib.auth.models import User
class SignUpForm(UserCreationForm):
tailwind_class = "w-full border-2 border-gray-300 bg-gray-100 rounded-lg focus:ring focus:border-blue-300 focus:shadow-none"
logger = logging.getLogger('signup_form')
username = forms.CharField(widget=forms.TextInput(attrs={'class': tailwind_class}),
error_messages={
'unique': 'This username is already in use.',
@ -18,6 +21,16 @@ class SignUpForm(UserCreationForm):
)
password2 = forms.CharField(widget=forms.PasswordInput(attrs={'class': tailwind_class}),)
# commit -> default =True -> If commit is True -> want to save the user object to the database.
def save(self, commit: bool = True) -> Any:
user = super().save(commit=False)
if commit:
user.save()
self.logger.info(f"User registered with username: {user.username}")
return user
class Meta:
model = User
fields = ('username', 'password1', 'password2')

38
polls/signals.py Normal file
View File

@ -0,0 +1,38 @@
import logging
from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed
from django.dispatch import receiver
log = logging.getLogger("django")
def get_client_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip
#! https://stackoverflow.com/questions/37618473/how-can-i-log-both-successful-and-failed-login-and-logout-attempts-in-django
@receiver(user_logged_in)
def user_logged_in_callback(sender, request, user, **kwargs):
ip = get_client_ip(request)
log.info('Login User: {user} via ip: {ip}'.format(
user=user,
ip=ip
))
@receiver(user_logged_out)
def user_logged_out_callback(sender, request, user, **kwargs):
ip = get_client_ip(request)
log.info('Logout User: {user} via ip: {ip}'.format(
user=user,
ip=ip
))
@receiver(user_login_failed)
def user_login_failed_callback(sender, credentials, **kwargs):
log.warning('Login Failed for: {credentials}'.format(
credentials=credentials,
))

View File

@ -1,4 +1,5 @@
from django.http import HttpResponseRedirect
import logging
from django.shortcuts import get_object_or_404, render, redirect
from django.urls import reverse
from django.views import generic
@ -13,6 +14,9 @@ from .forms import SignUpForm
from .models import Choice, Question, Vote
logger = logging.getLogger("django")
class IndexView(generic.ListView):
"""View for index.html."""
@ -98,15 +102,19 @@ def vote(request, question_id):
A function that updates the database. Adds a vote count to the choice that the user votes for
in a specific question_id.
"""
ip = get_client_ip(request)
question = get_object_or_404(Question, pk=question_id)
if request.method == "POST":
try:
selected_choice = question.choice_set.get(pk=request.POST["choice"])
except (KeyError, Choice.DoesNotExist):
logger.error(f"User {request.user.username} ({ip}) didn't select choice.")
messages.error(request, "You didn't select a choice.")
return redirect("polls:detail", question_id)
logger.info(f"User {request.user.username} ({ip}) select choice {selected_choice}")
if question.can_vote():
# ! Return 1. object element 2. boolean status of creation
vote, created = Vote.objects.update_or_create(
@ -116,8 +124,10 @@ def vote(request, question_id):
)
if created:
logger.info(f"User {request.user.username} ({ip}) vote on choice {selected_choice}")
messages.success(request, "You voted successfully🥳")
else:
logger.info(f"User {request.user.username} ({ip}) update his answer to {selected_choice}")
messages.success(request, "You updated your vote🥳")
return redirect("polls:results", question_id)
@ -126,4 +136,14 @@ def vote(request, question_id):
return redirect("polls:index")
else:
messages.error(request, "Invalid request method.")
return redirect("polls:index")
return redirect("polls:index")
# https://stackoverflow.com/questions/4581789/how-do-i-get-user-ip-address-in-django
def get_client_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip