adding the logs service
This commit is contained in:
parent
1591802c8e
commit
22800ab230
4 changed files with 126 additions and 1 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -57,6 +57,7 @@ cover/
|
|||
|
||||
# Django stuff:
|
||||
*.log
|
||||
logs/
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
|
|
|||
10
api/views.py
10
api/views.py
|
|
@ -1,3 +1,4 @@
|
|||
import logging
|
||||
import random
|
||||
from datetime import date
|
||||
from django.contrib.auth import authenticate, login
|
||||
|
|
@ -13,6 +14,8 @@ from .serializers import (
|
|||
OrderSerializer, ReturnRequestSerializer, WalletSerializer
|
||||
)
|
||||
|
||||
logger = logging.getLogger('api')
|
||||
|
||||
def get_active_user(request):
|
||||
if request.user and request.user.is_authenticated:
|
||||
return request.user
|
||||
|
|
@ -30,7 +33,9 @@ class RegisterView(APIView):
|
|||
if serializer.is_valid():
|
||||
user = serializer.save()
|
||||
login(request, user)
|
||||
logger.info('New supplier registered: username=%s email=%s', user.username, user.email)
|
||||
return Response(UserSerializer(user).data, status=status.HTTP_201_CREATED)
|
||||
logger.warning('Registration failed: %s', serializer.errors)
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class LoginView(APIView):
|
||||
|
|
@ -40,7 +45,9 @@ class LoginView(APIView):
|
|||
user = authenticate(username=username, password=password)
|
||||
if user:
|
||||
login(request, user)
|
||||
logger.info('Login successful: username=%s', username)
|
||||
return Response(UserSerializer(user).data)
|
||||
logger.warning('Login failed: username=%s', username)
|
||||
return Response({'error': 'Invalid credentials'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class VerifyOtpView(APIView):
|
||||
|
|
@ -190,9 +197,11 @@ class WithdrawView(APIView):
|
|||
try:
|
||||
amount = float(amount_str)
|
||||
except (TypeError, ValueError):
|
||||
logger.error('Withdraw failed for user=%s: invalid amount value=%s', user.username, amount_str)
|
||||
return Response({'error': 'Invalid amount'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if amount <= 0 or amount > float(wallet.outstanding):
|
||||
logger.warning('Withdraw rejected for user=%s: amount=%.2f outstanding=%.2f', user.username, amount, float(wallet.outstanding))
|
||||
return Response({'error': 'Insufficient funds or invalid amount'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
wallet.outstanding = float(wallet.outstanding) - amount
|
||||
|
|
@ -207,4 +216,5 @@ class WithdrawView(APIView):
|
|||
status='Transferred'
|
||||
)
|
||||
|
||||
logger.info('Withdraw successful: user=%s amount=%.2f tx_id=%s', user.username, amount, tx.tx_id)
|
||||
return Response(WalletSerializer(wallet).data)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ For the full list of settings and their values, see
|
|||
https://docs.djangoproject.com/en/6.1/ref/settings/
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
|
|
@ -139,3 +140,106 @@ REST_FRAMEWORK = {
|
|||
'api.authentication.CsrfExemptSessionAuthentication',
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Logging
|
||||
# Writes to /app/logs/ (volume-mounted on host) and to stdout.
|
||||
# Log files are also picked up by the CloudWatch Agent on the EC2 host.
|
||||
# ---------------------------------------------------------------------------
|
||||
LOGS_DIR = BASE_DIR / 'logs'
|
||||
os.makedirs(LOGS_DIR, exist_ok=True)
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
|
||||
# ----- Formatters -----
|
||||
'formatters': {
|
||||
'verbose': {
|
||||
'format': '[{asctime}] {levelname} {name} {process:d} {thread:d} | {message}',
|
||||
'style': '{',
|
||||
'datefmt': '%Y-%m-%d %H:%M:%S',
|
||||
},
|
||||
'simple': {
|
||||
'format': '[{asctime}] {levelname} | {message}',
|
||||
'style': '{',
|
||||
'datefmt': '%Y-%m-%d %H:%M:%S',
|
||||
},
|
||||
},
|
||||
|
||||
# ----- Filters -----
|
||||
'filters': {
|
||||
'require_debug_false': {
|
||||
'()': 'django.utils.log.RequireDebugFalse',
|
||||
},
|
||||
'require_debug_true': {
|
||||
'()': 'django.utils.log.RequireDebugTrue',
|
||||
},
|
||||
},
|
||||
|
||||
# ----- Handlers -----
|
||||
'handlers': {
|
||||
# Writes every log line to stdout (visible via `docker logs`)
|
||||
'console': {
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'simple',
|
||||
},
|
||||
# Rotating file for all application logs (INFO+)
|
||||
'app_file': {
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'filename': str(LOGS_DIR / 'app.log'),
|
||||
'maxBytes': 10 * 1024 * 1024, # 10 MB
|
||||
'backupCount': 7,
|
||||
'formatter': 'verbose',
|
||||
'encoding': 'utf-8',
|
||||
},
|
||||
# Dedicated rotating file for ERROR+ only
|
||||
'error_file': {
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'filename': str(LOGS_DIR / 'error.log'),
|
||||
'maxBytes': 10 * 1024 * 1024, # 10 MB
|
||||
'backupCount': 7,
|
||||
'formatter': 'verbose',
|
||||
'level': 'ERROR',
|
||||
'encoding': 'utf-8',
|
||||
},
|
||||
# Django request/response log
|
||||
'request_file': {
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'filename': str(LOGS_DIR / 'requests.log'),
|
||||
'maxBytes': 10 * 1024 * 1024, # 10 MB
|
||||
'backupCount': 7,
|
||||
'formatter': 'simple',
|
||||
'encoding': 'utf-8',
|
||||
},
|
||||
},
|
||||
|
||||
# ----- Loggers -----
|
||||
'loggers': {
|
||||
# Root logger – catches everything not matched below
|
||||
'': {
|
||||
'handlers': ['console', 'app_file', 'error_file'],
|
||||
'level': 'INFO',
|
||||
'propagate': False,
|
||||
},
|
||||
# Your application code
|
||||
'api': {
|
||||
'handlers': ['console', 'app_file', 'error_file'],
|
||||
'level': 'DEBUG',
|
||||
'propagate': False,
|
||||
},
|
||||
# Django HTTP request log (4xx / 5xx)
|
||||
'django.request': {
|
||||
'handlers': ['console', 'request_file', 'error_file'],
|
||||
'level': 'INFO',
|
||||
'propagate': False,
|
||||
},
|
||||
# Suppress noisy security middleware warnings in prod
|
||||
'django.security': {
|
||||
'handlers': ['error_file'],
|
||||
'level': 'ERROR',
|
||||
'propagate': False,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,19 @@ services:
|
|||
web:
|
||||
build: .
|
||||
ports:
|
||||
- "8000:8000"
|
||||
- "8080:8000"
|
||||
volumes:
|
||||
- .:/app
|
||||
- ./logs:/app/logs # persists log files on the host
|
||||
environment:
|
||||
- DEBUG=1
|
||||
restart: always
|
||||
# Forward Gunicorn access + error logs to /app/logs
|
||||
command: >
|
||||
gunicorn config.wsgi:application
|
||||
--bind 0.0.0.0:8000
|
||||
--workers 2
|
||||
--access-logfile /app/logs/gunicorn-access.log
|
||||
--error-logfile /app/logs/gunicorn-error.log
|
||||
--log-level info
|
||||
--capture-output
|
||||
|
|
|
|||
Loading…
Reference in a new issue