set time zone
This commit is contained in:
parent
821304f1c8
commit
e1f3d72ab7
6 changed files with 193 additions and 1 deletions
0
utils/__init__.py
Normal file
0
utils/__init__.py
Normal file
51
utils/exceptions.py
Normal file
51
utils/exceptions.py
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from rest_framework.exceptions import APIException
|
||||||
|
from rest_framework.views import exception_handler as drf_exception_handler
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
def exception_handler(exc, context):
|
||||||
|
logger.exception(exc)
|
||||||
|
# پاسخ پیشفرض DRF را دریافت میکنیم
|
||||||
|
response = drf_exception_handler(exc, context)
|
||||||
|
|
||||||
|
if response is not None:
|
||||||
|
# ساختار دلخواه خود را تعریف میکنیم
|
||||||
|
response_data = {
|
||||||
|
'success': False,
|
||||||
|
'status_code': response.status_code,
|
||||||
|
'message': 'An error occurred',
|
||||||
|
'details': {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isinstance(exc, APIException):
|
||||||
|
response_data['message'] = exc.detail if isinstance(exc.detail, str) else "Validation error"
|
||||||
|
response_data['details'] = exc.detail if isinstance(exc.detail, dict) else {}
|
||||||
|
|
||||||
|
response.data = response_data
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
# در فایل middleware.py
|
||||||
|
from django.http import JsonResponse
|
||||||
|
|
||||||
|
class ErrorMiddleware:
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
response = self.get_response(request)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def process_exception(self, request, exception):
|
||||||
|
logger.exception(exception)
|
||||||
|
return JsonResponse(
|
||||||
|
{
|
||||||
|
"success": False,
|
||||||
|
"status_code": 500,
|
||||||
|
"message": "Internal server error",
|
||||||
|
"details": str(exception) # فقط در حالت توسعه! در تولید بهتر است لاگ شود.
|
||||||
|
},
|
||||||
|
status=500
|
||||||
|
)
|
||||||
4
utils/logs/__init__.py
Normal file
4
utils/logs/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
from .formatters import LokiFormatter
|
||||||
|
from .handlers import LokiHandler
|
||||||
|
|
||||||
|
__all__ = ['LokiHandler', 'LokiFormatter']
|
||||||
77
utils/logs/formatters.py
Normal file
77
utils/logs/formatters.py
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
import logging
|
||||||
|
import socket
|
||||||
|
from datetime import datetime
|
||||||
|
from logging import BASIC_FORMAT
|
||||||
|
|
||||||
|
|
||||||
|
class LokiFormatter(logging.Formatter):
|
||||||
|
asctime_search = "%(asctime)"
|
||||||
|
tz = "UTC"
|
||||||
|
source = "Loki"
|
||||||
|
src_host = "localhost"
|
||||||
|
tags = {}
|
||||||
|
|
||||||
|
def __init__(self, fmt, dfmt, style, fqdn=False):
|
||||||
|
super(LokiFormatter, self).__init__()
|
||||||
|
|
||||||
|
self.fmt = fmt or BASIC_FORMAT
|
||||||
|
self.dfmt = dfmt or "%Y-%m-%d %H:%M:%S"
|
||||||
|
self.style = style
|
||||||
|
|
||||||
|
if fqdn:
|
||||||
|
self.host = socket.getfqdn()
|
||||||
|
else:
|
||||||
|
self.host = socket.gethostname()
|
||||||
|
|
||||||
|
def format_timestamp(self, time):
|
||||||
|
return str(int(time * 10**9))
|
||||||
|
|
||||||
|
def usesTime(self):
|
||||||
|
return self.fmt.find(self.asctime_search) >= 0
|
||||||
|
|
||||||
|
def formatMessage(self, record):
|
||||||
|
try:
|
||||||
|
return self.fmt % record.__dict__
|
||||||
|
except KeyError as e:
|
||||||
|
raise ValueError("Formatting field not found in record: %s" % e)
|
||||||
|
|
||||||
|
def format(self, record):
|
||||||
|
record.message = record.getMessage()
|
||||||
|
|
||||||
|
if self.usesTime():
|
||||||
|
record.asctime = self.formatTime(record, self.dfmt)
|
||||||
|
|
||||||
|
message = self.formatMessage(record)
|
||||||
|
|
||||||
|
if record.exc_info:
|
||||||
|
if not record.exc_text:
|
||||||
|
record.exc_text = self.formatException(record.exc_info)
|
||||||
|
|
||||||
|
if record.exc_text:
|
||||||
|
if message[-1:] != "\n":
|
||||||
|
message = message + "\n"
|
||||||
|
message = message + record.exc_text
|
||||||
|
|
||||||
|
if record.stack_info:
|
||||||
|
if message[-1:] != "\n":
|
||||||
|
message = message + "\n"
|
||||||
|
|
||||||
|
message = message + self.formatStack(record.stack_info)
|
||||||
|
|
||||||
|
message = {
|
||||||
|
"streams": [
|
||||||
|
{
|
||||||
|
"stream": {
|
||||||
|
**self.tags,
|
||||||
|
},
|
||||||
|
"values": [
|
||||||
|
[
|
||||||
|
self.format_timestamp(record.created),
|
||||||
|
message,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return message
|
||||||
58
utils/logs/handlers.py
Normal file
58
utils/logs/handlers.py
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class LokiHandler(logging.Handler):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
timeout=0.5,
|
||||||
|
url="http://localhost:3100/loki/api/v1/push",
|
||||||
|
auth=None,
|
||||||
|
tags={},
|
||||||
|
mode="sync",
|
||||||
|
):
|
||||||
|
super(LokiHandler, self).__init__()
|
||||||
|
|
||||||
|
self._url = url
|
||||||
|
self._timeout = timeout
|
||||||
|
self._auth = auth
|
||||||
|
self._tags = tags
|
||||||
|
|
||||||
|
self._mode = mode
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
try:
|
||||||
|
payload = self.formatter.format(record)
|
||||||
|
|
||||||
|
_push_message(
|
||||||
|
self._url, json=payload, timeout=self._timeout, auth=self._auth
|
||||||
|
)
|
||||||
|
except requests.exceptions.ReadTimeout:
|
||||||
|
sys.stderr.write("Loki connection timed out\n")
|
||||||
|
except Exception as e:
|
||||||
|
sys.stderr.write(f"Loki connection failed with: {e}\n")
|
||||||
|
|
||||||
|
def setFormatter(self, fmt):
|
||||||
|
fmt.tags = self._tags
|
||||||
|
|
||||||
|
self.formatter = fmt
|
||||||
|
|
||||||
|
def _push_message(self, *args, **kwargs):
|
||||||
|
print('hehe')
|
||||||
|
if self._mode == "sync":
|
||||||
|
return _push_message(*args, **kwargs)
|
||||||
|
|
||||||
|
if self._mode == "thread":
|
||||||
|
return Thread(target=_push_message, args=args, kwargs=kwargs).start()
|
||||||
|
|
||||||
|
|
||||||
|
def _push_message(*args, **kwargs):
|
||||||
|
response = requests.post(*args, **kwargs)
|
||||||
|
|
||||||
|
if response.status_code != 204:
|
||||||
|
sys.stderr.write(
|
||||||
|
f"Got status {response.status_code} from loki with message: {response.text}\n"
|
||||||
|
)
|
||||||
|
|
@ -67,6 +67,7 @@ MIDDLEWARE = [
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
'corsheaders.middleware.CorsMiddleware',
|
'corsheaders.middleware.CorsMiddleware',
|
||||||
|
'utils.exceptions.ErrorMiddleware',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -111,6 +112,7 @@ REST_FRAMEWORK = {
|
||||||
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
|
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
|
||||||
'PAGE_SIZE': 50,
|
'PAGE_SIZE': 50,
|
||||||
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
|
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
|
||||||
|
'EXCEPTION_HANDLER': 'utils.exceptions.exception_handler',
|
||||||
}
|
}
|
||||||
|
|
||||||
SPECTACULAR_SETTINGS = {
|
SPECTACULAR_SETTINGS = {
|
||||||
|
|
@ -195,7 +197,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en'
|
LANGUAGE_CODE = 'en'
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
TIME_ZONE = 'Asia/Tehran'
|
||||||
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue