mirror of
https://github.com/Sosokker/ku-polls.git
synced 2025-12-18 13:04:05 +01:00
Add Logging
This commit is contained in:
parent
0d42e56139
commit
c3b5984e33
@ -6,8 +6,8 @@ on the [Django Tutorial project](https://docs.djangoproject.com/en/4.2/intro/tut
|
|||||||
|
|
||||||
## Install and Run
|
## Install and Run
|
||||||
### Run Setup.py Method
|
### Run Setup.py Method
|
||||||
|
1. Install [Python 3.11 or later](https://www.python.org/downloads/)
|
||||||
Clone this repository and Run `setup.py` to install and run the project
|
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**
|
**Don't forget to answer the question from `setup.py` to setup the project**
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@ -10,6 +10,8 @@ For the full list of settings and their values, see
|
|||||||
https://docs.djangoproject.com/en/4.2/ref/settings/
|
https://docs.djangoproject.com/en/4.2/ref/settings/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from decouple import config, Csv
|
from decouple import config, Csv
|
||||||
|
|
||||||
@ -70,6 +72,95 @@ TEMPLATES = [
|
|||||||
|
|
||||||
WSGI_APPLICATION = 'mysite.wsgi.application'
|
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
|
# Database
|
||||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
|
||||||
|
|||||||
@ -4,4 +4,6 @@ from django.apps import AppConfig
|
|||||||
class PollsConfig(AppConfig):
|
class PollsConfig(AppConfig):
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
name = 'polls'
|
name = 'polls'
|
||||||
|
|
||||||
|
def ready(self) -> None:
|
||||||
|
import polls.signals
|
||||||
@ -1,3 +1,6 @@
|
|||||||
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.auth.forms import UserCreationForm
|
from django.contrib.auth.forms import UserCreationForm
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
@ -5,7 +8,7 @@ from django.contrib.auth.models import User
|
|||||||
|
|
||||||
class SignUpForm(UserCreationForm):
|
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"
|
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}),
|
username = forms.CharField(widget=forms.TextInput(attrs={'class': tailwind_class}),
|
||||||
error_messages={
|
error_messages={
|
||||||
'unique': 'This username is already in use.',
|
'unique': 'This username is already in use.',
|
||||||
@ -18,6 +21,16 @@ class SignUpForm(UserCreationForm):
|
|||||||
)
|
)
|
||||||
password2 = forms.CharField(widget=forms.PasswordInput(attrs={'class': tailwind_class}),)
|
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:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ('username', 'password1', 'password2')
|
fields = ('username', 'password1', 'password2')
|
||||||
|
|||||||
38
polls/signals.py
Normal file
38
polls/signals.py
Normal 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,
|
||||||
|
))
|
||||||
@ -1,4 +1,5 @@
|
|||||||
from django.http import HttpResponseRedirect
|
import logging
|
||||||
|
|
||||||
from django.shortcuts import get_object_or_404, render, redirect
|
from django.shortcuts import get_object_or_404, render, redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.views import generic
|
from django.views import generic
|
||||||
@ -13,6 +14,9 @@ from .forms import SignUpForm
|
|||||||
from .models import Choice, Question, Vote
|
from .models import Choice, Question, Vote
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger("django")
|
||||||
|
|
||||||
|
|
||||||
class IndexView(generic.ListView):
|
class IndexView(generic.ListView):
|
||||||
"""View for index.html."""
|
"""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
|
A function that updates the database. Adds a vote count to the choice that the user votes for
|
||||||
in a specific question_id.
|
in a specific question_id.
|
||||||
"""
|
"""
|
||||||
|
ip = get_client_ip(request)
|
||||||
question = get_object_or_404(Question, pk=question_id)
|
question = get_object_or_404(Question, pk=question_id)
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
try:
|
try:
|
||||||
selected_choice = question.choice_set.get(pk=request.POST["choice"])
|
selected_choice = question.choice_set.get(pk=request.POST["choice"])
|
||||||
except (KeyError, Choice.DoesNotExist):
|
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.")
|
messages.error(request, "You didn't select a choice.")
|
||||||
return redirect("polls:detail", question_id)
|
return redirect("polls:detail", question_id)
|
||||||
|
|
||||||
|
logger.info(f"User {request.user.username} ({ip}) select choice {selected_choice}")
|
||||||
|
|
||||||
if question.can_vote():
|
if question.can_vote():
|
||||||
# ! Return 1. object element 2. boolean status of creation
|
# ! Return 1. object element 2. boolean status of creation
|
||||||
vote, created = Vote.objects.update_or_create(
|
vote, created = Vote.objects.update_or_create(
|
||||||
@ -116,8 +124,10 @@ def vote(request, question_id):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if created:
|
if created:
|
||||||
|
logger.info(f"User {request.user.username} ({ip}) vote on choice {selected_choice}")
|
||||||
messages.success(request, "You voted successfully🥳")
|
messages.success(request, "You voted successfully🥳")
|
||||||
else:
|
else:
|
||||||
|
logger.info(f"User {request.user.username} ({ip}) update his answer to {selected_choice}")
|
||||||
messages.success(request, "You updated your vote🥳")
|
messages.success(request, "You updated your vote🥳")
|
||||||
|
|
||||||
return redirect("polls:results", question_id)
|
return redirect("polls:results", question_id)
|
||||||
@ -126,4 +136,14 @@ def vote(request, question_id):
|
|||||||
return redirect("polls:index")
|
return redirect("polls:index")
|
||||||
else:
|
else:
|
||||||
messages.error(request, "Invalid request method.")
|
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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user