23b64aee by Gruel

logging config & common app

1 parent a90b24be
certifi==2016.2.28
Django==2.1
djangorestframework==3.9.0
djangorestframework-jwt==1.11.0
PyJWT==1.7.1
PyMuPDF==1.17.0
pytz==2020.1
# simple-config @ http://gitlab.situdata.com/zhouweiqi/simple_config/repository/archive.tar.gz?ref=master
# situlogger @ http://gitlab.situdata.com/zhouweiqi/situlogger/repository/archive.tar.gz?ref=master
six==1.14.0
......
http://gitlab.situdata.com/zhouweiqi/simple_config/repository/archive.tar.gz?ref=master
\ No newline at end of file
http://gitlab.situdata.com/zhouweiqi/simple_config/repository/archive.tar.gz?ref=master
http://gitlab.situdata.com/zhouweiqi/situlogger/repository/archive.tar.gz?ref=master
\ No newline at end of file
......
from django.contrib import admin
# Register your models here.
from django.apps import AppConfig
class AccountConfig(AppConfig):
name = 'account'
from django.db import models
# Create your models here.
from rest_framework.permissions import DjangoModelPermissions
class Permissions(DjangoModelPermissions):
"""
copy from rest_framework/permissions.py
"""
def has_permission(self, request, view):
# Workaround to ensure DjangoModelPermissions are not applied
# to the root view when using DefaultRouter.
if getattr(view, '_ignore_model_permissions', False):
return True
if not request.user:
return False
if self.authenticated_users_only and not request.user.is_authenticated:
return False
# if not request.user.is_staff:
# return False
perms = getattr(view, 'method_perms', {}).get(request.method, [])
return request.user.has_perms(perms)
from django.test import TestCase
# Create your tests here.
from django.urls import path
from . import views
urlpatterns = [
# path(r'login/', views.LoginView.as_view()),
]
from django.shortcuts import render
# Create your views here.
......@@ -14,8 +14,9 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path(r'apis/v1/users/', include('apps.account.urls')),
]
......
File mode changed
from django.apps import AppConfig
class CommonConfig(AppConfig):
name = 'common'
import logging
import six
from django.core.exceptions import PermissionDenied
from django.utils.translation import ugettext_lazy as _
from django.http import Http404
from rest_framework import exceptions, status
from rest_framework.response import Response
from .response import MetaStatus, res_content, APIResponse
error_logger = logging.getLogger('exception')
def exception_handler(exc, context):
"""
Returns the response that should be used for any given exception.
By default we handle the REST framework `APIException`, and also
Django's built-in `Http404` and `PermissionDenied`
Any unhandled exceptions may return `None`, which will cause a 500 error
to be raised.
"""
if isinstance(exc, exceptions.APIException):
headers = {}
if getattr(exc, 'auth_header', None):
headers['WWW-Authenticate'] = exc.auth_header
if getattr(exc, 'wait', None):
headers['Retry-After'] = '%d' % exc.wait
if exc.status_code == 403:
data = res_content(MetaStatus.NEED_LOGIN.value, MetaStatus.NEED_LOGIN.verbose_name)
else:
data = res_content(MetaStatus.INTERNAL_ERROR.value, exc.detail)
return Response(data, status=200, headers=headers)
elif isinstance(exc, Http404):
msg = _('Not found.')
data = {'detail': six.text_type(msg)}
return Response(data, status=status.HTTP_404_NOT_FOUND)
elif isinstance(exc, PermissionDenied):
msg = _('Permission denied.')
data = {'detail': six.text_type(msg)}
return Response(data, status=status.HTTP_403_FORBIDDEN)
elif isinstance(exc, Exception) and hasattr(exc, 'API_META_STATUS'):
msg = exc.API_META_STATUS.verbose_name
return APIResponse(exc.API_META_STATUS.value, msg=msg)
error_logger.exception('[system error]')
return APIResponse(MetaStatus.INTERNAL_ERROR.value,
msg=MetaStatus.INTERNAL_ERROR.verbose_name)
class NeedLoginException(Exception):
API_META_STATUS = MetaStatus.NEED_LOGIN
class InvalidParamsException(Exception):
API_META_STATUS = MetaStatus.INVALID_PARAMS
class InternalErrorException(Exception):
API_META_STATUS = MetaStatus.INTERNAL_ERROR
class ObjectNotExitException(Exception):
API_META_STATUS = MetaStatus.NOT_EXIST
class AsyncWaitException(Exception):
API_META_STATUS = MetaStatus.ASYNC_WAIT
class NoPermissionException(Exception):
API_META_STATUS = MetaStatus.NO_PERMISSION
class IllegalOperationException(Exception):
API_META_STATUS = MetaStatus.ILLEGAL_OPERATION
import logging
from django.contrib.auth.models import AnonymousUser
from rest_framework.generics import GenericAPIView
from common.exceptions import (
NeedLoginException,
InvalidParamsException,
InternalErrorException,
ObjectNotExitException,
AsyncWaitException,
NoPermissionException,
IllegalOperationException)
class GenericExceptionMixin:
def need_login(self, msg='need login'):
raise NeedLoginException(msg)
def invalid_params(self, msg='invalid params'):
raise InvalidParamsException(msg)
def internal_error(self, msg='internal error'):
raise InternalErrorException(msg)
def object_not_exit(self, msg='object not exit'):
raise ObjectNotExitException(msg)
def async_wait(self, msg='async wait'):
raise AsyncWaitException(msg)
def no_permission(self, msg='no permission'):
raise NoPermissionException(msg)
def illegal_operation(self, msg='illegal operation'):
raise IllegalOperationException(msg)
class LoggerMixin:
running_log = logging.getLogger('running')
exception_log = logging.getLogger('exception')
cronjob_log = logging.getLogger('cronjob')
class GenericView(LoggerMixin, GenericExceptionMixin, GenericAPIView):
need_print_logger = True
def print_logger(self, request):
parameters = getattr(request, request.method, {})
if not parameters:
parameters = getattr(request, 'data', {})
if not parameters:
parameters = {}
parameters_string = ''
for key, value in parameters.items():
parameters_string += '[%s=%s] ' % (key, value)
for key, value in self.kwargs.items():
parameters_string += '[%s=%s] ' % (key, value)
if request.user and not isinstance(request.user, AnonymousUser):
user_id = request.user.id
else:
user_id = 0
self.running_log.info('[%s_%s_request] with parameters [user_id=%s] %s'
% (self.__class__.__name__, request.method,
user_id, parameters_string))
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
if self.need_print_logger:
self.print_logger(request)
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(
request, response, *args, **kwargs)
return self.response
def get_object(self):
return None
import enum
from functools import lru_cache
class NamedEnum(enum.Enum):
"""
>>> @enum.unique
... class Status(NamedEnum):
... CREATED = (3, '初始化')
... CANCELED = (2, '损坏')
... FINISHED = (1, '完成')
... REJECTED = (4, '拒绝')
... TEST = (6, '拒绝')
>>> assert Status.CREATED != 3
>>> assert Status.CREATED.value == 3
>>> assert Status.CREATED.name == 'CREATED'
>>> assert Status.CREATED.verbose_name == '初始化'
>>> assert Status(3) == Status.CREATED
>>> assert Status['CREATED'] == Status.CREATED
>>> assert Status.get_verbose_name_or_raise(3) == '初始化'
>>> assert Status.get_mappings() == {
... 3: '初始化', 2: '损坏', 1: '完成', 4: '拒绝', 6: '拒绝'}
>>> assert Status.get_mapping_lst() == [
... {'id': 3, 'name': '初始化'},
... {'id': 2, 'name': '损坏'},
... {'id': 1, 'name': '完成'},
... {'id': 4, 'name': '拒绝'},
... {'id': 6, 'name': '拒绝'}
... ]
>>> assert Status.get_verbose_name(5, '默认') == '默认'
>>> assert Status.get_value_or_raise('完成') == 1
>>> assert Status.get_value('完成完成', 100) == 100
>>> assert Status.get_value_or_raise('拒绝') == 4
>>> # 测试扩展功能
>>> extend_values = {
... 'EXTENDED': (6, '扩展')
... }
>>> ExtendedStatus = extend(Status, 'ExtendedStatus', extend_values)
>>> assert ExtendedStatus.get_verbose_name(5, '默认') == '默认'
>>> assert ExtendedStatus.get_value_or_raise('完成') == 1
>>> assert ExtendedStatus.get_value('完成完成', 100) == 100
>>> assert ExtendedStatus.get_value_or_raise('拒绝') == 4
>>> assert ExtendedStatus.EXTENDED.value == 6
>>> ExtendedStatus = extend(
... Status, 'ExtendedStatus', [('EXTENDED', (6, '扩展'))])
>>> assert ExtendedStatus.EXTENDED.value == 6
>>> try:
... ExtendedStatus = extend(
... Status, 'ExtendedStatus', extend_values, unique=True)
... except ValueError as err:
... assert 'duplicate' in str(err)
... else:
... raise Exception('except ValueError')
"""
def __new__(cls, *args):
value, verbose_name = args
res = object.__new__(cls)
res._value_ = value
return res
def __init__(self, *args, **kwargs):
self._value_ = args[0]
self.verbose_name = args[1]
@classmethod
def get_verbose_name_or_raise(cls, value):
return cls.get_verbose_name(value, raise_on_missing=True)
@classmethod
def get_verbose_name(cls, value, default=None, raise_on_missing=False):
"""尝试根据value获取verbose_name,如果失败,返回默认值
"""
try:
return cls(value).verbose_name
except ValueError as err:
if raise_on_missing:
raise err
else:
return default
@classmethod
def get_value_or_raise(cls, verbose_name):
return cls.get_value(verbose_name, raise_on_missing=True)
@classmethod
def get_value(cls, verbose_name, default=None, raise_on_missing=False):
try:
return cls.from_verbose_name(verbose_name).value
except ValueError as err:
if raise_on_missing:
raise err
else:
return default
@classmethod
def from_verbose_name(cls, verbose_name):
"""根据verbose_name获取NamedEnum,
如果verbose_name重复出现,会返回第一个定义的记录
"""
for member in cls._value2member_map_.values():
if member.verbose_name == verbose_name:
return member
raise ValueError('%s is not a valid "%s"' % (
verbose_name, cls.__name__))
@classmethod
@lru_cache()
def get_mappings(cls):
return {
item.value: item.verbose_name
for _, item in cls._member_map_.items()
}
@classmethod
@lru_cache()
def get_mapping_lst(cls):
return list([
{'id': item.value, 'name': item.verbose_name}
for item in cls
])
@classmethod
@lru_cache()
def get_value_lst(cls):
return list([
item.value
for item in cls
])
@property
def raw_value(self):
return (self.value, self.verbose_name)
def extend(cls, sub_cls_name, names, unique=False):
assert issubclass(cls, NamedEnum)
target_names = {
item.name: item.raw_value for item in cls
}
if not isinstance(names, dict):
names = {item[0]: item[1] for item in names}
target_names.update(names)
sub_cls = NamedEnum(sub_cls_name, names=target_names)
if unique:
sub_cls = enum.unique(sub_cls)
return sub_cls
import enum
from django.http import JsonResponse
from .named_enum import NamedEnum
def res_content(meta_status, msg, data=None):
if data is None:
data = {}
return {'code': meta_status, 'message': msg, 'data': data}
@enum.unique
class MetaStatus(NamedEnum):
SUCCESS = (0, 'success')
NEED_LOGIN = (1, 'need login')
INVALID_PARAMS = (2, 'invalid params')
INTERNAL_ERROR = (3, 'internal error')
NOT_EXIST = (4, 'object not exist')
ASYNC_WAIT = (5, 'async wait')
NO_PERMISSION = (6, 'no permission')
ILLEGAL_OPERATION = (7, 'illegal operation')
class APIResponse(JsonResponse):
def __init__(self, meta_status, data=None, msg='', json_dumps_params=None, **kwargs):
data = res_content(meta_status, msg, data)
json_dumps_params = json_dumps_params or {'ensure_ascii': False}
kwargs['json_dumps_params'] = json_dumps_params
super().__init__(data, **kwargs)
def ok(**kwargs):
return APIResponse(MetaStatus.SUCCESS.value, msg=MetaStatus.SUCCESS.verbose_name, **kwargs)
......@@ -12,6 +12,7 @@ https://docs.djangoproject.com/en/2.1/ref/settings/
import os
from . import conf
from logging import config
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = conf.BASE_DIR
......@@ -38,6 +39,8 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'common',
]
MIDDLEWARE = [
......@@ -119,3 +122,20 @@ USE_TZ = False
# https://docs.djangoproject.com/en/2.1/howto/static-files/
STATIC_URL = '/static/'
# RestFramework Settings
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
'apps.account.permissions.Permissions'
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
'EXCEPTION_HANDLER': 'common.exceptions.exception_handler'
}
# 日志配置
LOGGING_CONFIG = None
config.fileConfig(conf.LOGGING_CONFIG_FILE, disable_existing_loggers=False)
......
[loggers]
keys=root, running, exception, cronjob, django.db.backends
[handlers]
keys=consoleHandler, django_rotateFileHandler, exceptionFileHandler, cronjobFileHandler, djangodbFileHandler
[formatters]
keys=SituFormatter, dataLogFormatter
[formatter_SituFormatter]
format=[%(asctime)s] [%(process)d] [%(thread)d] [%(threadName)s] [%(filename)s:%(lineno)d] %(levelname)s %(message)s
datefmt=
[formatter_dataLogFormatter]
class=situlogger.JsonFormatter
format=%(asctime)s %(levelname)s %(funcName)s
[handler_consoleHandler]
class=StreamHandler
level=ERROR
formatter=SituFormatter
args=(sys.stdout,)
[handler_django_rotateFileHandler]
class=situlogger.SituRotatingFileHandler
level=INFO
formatter=SituFormatter
args=('../logs/running.log',)
[handler_exceptionFileHandler]
class=situlogger.SituRotatingFileHandler
level=ERROR
formatter=SituFormatter
args=('../logs/exception.log',)
[handler_cronjobFileHandler]
class=situlogger.SituRotatingFileHandler
level=DEBUG
formatter=SituFormatter
args=('../logs/cronjob.log',)
[handler_djangodbFileHandler]
class=situlogger.SituRotatingFileHandler
level=DEBUG
formatter=SituFormatter
args=('../logs/sql.log',)
[logger_root]
level=DEBUG
handlers=consoleHandler
[logger_running]
level=INFO
handlers=django_rotateFileHandler
qualname=running
propagate=0
[logger_exception]
level=ERROR
handlers=exceptionFileHandler
qualname=exception
propagate=0
[logger_cronjob]
level=INFO
handlers=cronjobFileHandler
qualname=cronjob
propagate=0
[logger_django.db.backends]
level=DEBUG
handlers=djangodbFileHandler
qualname=django.db.backends
propagate=0
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!