Технический блог:GitHub.com/Делайте это с душой/Особые…
В то же время, вы также можете обратить внимание на мой публичный аккаунт WeChat.AlwaysBeta, вас ждет еще больше захватывающего контента.
Логи вещь хорошая, но не все готовы их вести, о них не жалеют, пока что-то не пойдет не так.
Но ведение дневника — это техническая работа, вы не можете запомнить все, но вы не можете запомнить все. Если вы будете запоминать много бесполезной информации, это добавит массу сложностей в процесс проверки логов и устранения неполадок.
Поэтому лог должен быть записан на ключевых узлах программы, а содержание должно быть лаконичным, а информация точной. Необходимо четко отражать состояние, время, сообщение об ошибке и т.д. программы в этот момент.
Только так мы сможем найти проблему и решить ее с первого раза.
структура регистрации
Используйте модуль ведения журнала стандартной библиотеки Python для записи журналов в Django.Я не буду здесь слишком много рассказывать о конфигурации ведения журнала, а только напишу четыре наиболее важные части:Loggers
,Handlers
,Filters
иFormatters
.
Loggers
Logger
которыйРекордер, является записью лог-системы. У него есть три важные задачи:
- Предоставьте приложению (также известному как ваш проект) несколько методов для регистрации сообщений во время выполнения.
- Согласно переходу на
Logger
серьезность сообщения, чтобы определить, нужно ли обрабатывать сообщение - Передайте сообщение, которое необходимо обработать, всем заинтересованным процессорам
Handler
каждый пишетLogger
Сообщение является записью журнала, и каждая запись журнала содержит уровень, который представляет серьезность соответствующего сообщения. Обычно используются следующие уровни:
-
DEBUG
: системная информация низкого уровня, используемая для устранения неполадок, обычно используемая во время разработки. -
INFO
: Общая системная информация, не проблема -
WARNING
: информация, описывающая незначительную проблему с системой, но обычно не влияющая на функциональность. -
ERROR
: Информация, описывающая серьезную проблему с системой, которая может привести к неисправности. -
CRITICAL
: информация, описывающая серьезную проблему с системой, приложение может аварийно завершить работу.
когдаLogger
При обработке сообщения он сравнивает свой уровень журнала с уровнем, настроенным для сообщения. Если уровень сообщения соответствует или превышаетLogger
log, оно будет обработано дальше, иначе сообщение будет проигнорировано.
когдаLogger
После определения того, что сообщение необходимо обработать, оно будет переданоHandler
.
Handlers
Handler
которыйпроцессор, его основная функция состоит в том, чтобы решить, что делать сLogger
Каждое сообщение в , например вывод сообщения на экран, в файл или по электронной почте.
иLogger
Такой же,Handler
Существует также понятие уровней. Если уровень записи журнала не соответствует или нижеHandler
уровень журнала, это будетHandler
пренебрегать.
ОдинLogger
может иметь несколькоHandler
,КаждыйHandler
Могут быть разные уровни логирования. Это позволяет предоставлять различные типы вывода в зависимости от важности сообщения. Например, вы можете добавитьHandler
ПучокERROR
иCRITICAL
сообщение на ваш Email, добавьте ещеHandler
положить все сообщения (включаяERROR
иCRITICAL
сообщение) в файл.
Filters
Filter
которыйфильтр. в журнале изLogger
распространиться наHandler
процесс, используяFilter
для дополнительного контроля. Например, разрешить толькоERROR
вывод сообщения.
Filter
Также используется для изменения записей журнала перед их выходом из системы. Например, при выполнении определенных условий изменить уровень журнала сERROR
вплоть доWARNING
.
Filter
существуетLogger
иHandler
можно добавить несколькоFilter
Можно объединить в цепочку для выполнения нескольких операций фильтрации.
Formaters
Formatter
которыйформатер, основной функцией которого является определение формы и содержания конечного вывода.
Метод реализации
Сказав так много теорий, пришло время посмотреть, как это реализовано.
На самом деле проще всего прямо в начале файлаimport
, а затем вызывается в программе следующим образом:
# import the logging library
import logging
# Get an instance of a logger
logging.basicConfig(
format='%(asctime)s - %(pathname)s[%(lineno)d] - %(levelname)s: %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
def my_view(request, arg1, arg):
...
if bad_mojo:
# Log an error message
logger.error('Something went wrong!')
Но этот способ не годится.Если так писать в начале каждого файла, то первое беда, а второе то, что если вы хотите однажды изменить формат лога вывода, то каждый файл надо менять заново, и он не исчерпан.
Очевидно, что если вы можете инкапсулировать его в класс, вызывать этот класс, когда используете его, и вам нужно будет только изменить это место, когда вы его модифицируете, будет ли решена эта проблема?
пользовательский класс
Давайте посмотрим на то, как конкретная категория пакет:
class CommonLog(object):
"""
日志记录
"""
def __init__(self, logger, logname='web-log'):
self.logname = os.path.join(settings.LOGS_DIR, '%s' % logname)
self.logger = logger
self.logger.setLevel(logging.DEBUG)
self.logger.propagate = False
self.formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s: %(message)s', '%Y-%m-%d %H:%M:%S')
def __console(self, level, message):
# 创建一个FileHandler,用于写到本地
fh = logging.handlers.TimedRotatingFileHandler(self.logname, when='MIDNIGHT', interval=1, encoding='utf-8')
# fh = logging.FileHandler(self.logname, 'a', encoding='utf-8')
fh.suffix = '%Y-%m-%d.log'
fh.setLevel(logging.DEBUG)
fh.setFormatter(self.formatter)
self.logger.addHandler(fh)
# 创建一个StreamHandler,用于输出到控制台
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(self.formatter)
self.logger.addHandler(ch)
if level == 'info':
self.logger.info(message)
elif level == 'debug':
self.logger.debug(message)
elif level == 'warning':
self.logger.warning(message)
elif level == 'error':
self.logger.error(message)
# 这两行代码是为了避免日志输出重复问题
self.logger.removeHandler(ch)
self.logger.removeHandler(fh)
# 关闭打开的文件
fh.close()
def debug(self, message):
self.__console('debug', message)
def info(self, message):
self.__console('info', message)
def warning(self, message):
self.__console('warning', message)
def error(self, message):
self.__console('error', message)
Это фрагмент кода, который я все еще использую в проекте, и сгенерированные файлы разделены по дням.
В то время, когда я писал этот код, была проблема, которая меня давно беспокоила, то есть проблема отображения количества строк ошибок в коде. когдаformatter
настроить%(lineno)d
Каждый раз, вместо отображения фактической строки ошибки, он отображает строку кода в классе журнала, но это отображение бессмысленно, поэтому нет настройки, используйте%(name)s
чтобы показать фактический вызывающий файл.
На самом деле, если это просто для удобства устранения неполадок и записи некоторых журналов, этот класс может в основном соответствовать требованиям. Но если вы хотите записывать все логи запросов на доступ к системе, то тут ничего не поделаешь, потому что вручную добавлять логи в каждый код интерфейса нельзя, да и не нужно.
В это время я, естественно, думаю о промежуточном программном обеспечении Django.
Промежуточное ПО Django
Код журнала промежуточного программного обеспечения разделен на три части:Filters
код,middleware
код,settings
Настройте следующим образом:
local = threading.local()
class RequestLogFilter(logging.Filter):
"""
日志过滤器
"""
def filter(self, record):
record.sip = getattr(local, 'sip', 'none')
record.dip = getattr(local, 'dip', 'none')
record.body = getattr(local, 'body', 'none')
record.path = getattr(local, 'path', 'none')
record.method = getattr(local, 'method', 'none')
record.username = getattr(local, 'username', 'none')
record.status_code = getattr(local, 'status_code', 'none')
record.reason_phrase = getattr(local, 'reason_phrase', 'none')
return True
class RequestLogMiddleware(MiddlewareMixin):
"""
将request的信息记录在当前的请求线程上。
"""
def __init__(self, get_response=None):
self.get_response = get_response
self.apiLogger = logging.getLogger('web.log')
def __call__(self, request):
try:
body = json.loads(request.body)
except Exception:
body = dict()
if request.method == 'GET':
body.update(dict(request.GET))
else:
body.update(dict(request.POST))
local.body = body
local.path = request.path
local.method = request.method
local.username = request.user
local.sip = request.META.get('REMOTE_ADDR', '')
local.dip = socket.gethostbyname(socket.gethostname())
response = self.get_response(request)
local.status_code = response.status_code
local.reason_phrase = response.reason_phrase
self.apiLogger.info('system-auto')
return response
settings.py
Конфигурация файла:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
# 自定义中间件添加在最后
'lib.log_middleware.RequestLogMiddleware'
]
LOGGING = {
# 版本
'version': 1,
# 是否禁止默认配置的记录器
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '{"time": "%(asctime)s", "level": "%(levelname)s", "method": "%(method)s", "username": "%(username)s", "sip": "%(sip)s", "dip": "%(dip)s", "path": "%(path)s", "status_code": "%(status_code)s", "reason_phrase": "%(reason_phrase)s", "func": "%(module)s.%(funcName)s:%(lineno)d", "message": "%(message)s"}',
'datefmt': '%Y-%m-%d %H:%M:%S'
}
},
# 过滤器
'filters': {
'request_info': {'()': 'lib.log_middleware.RequestLogFilter'},
},
'handlers': {
# 标准输出
'console': {
'level': 'ERROR',
'class': 'logging.StreamHandler',
'formatter': 'standard'
},
# 自定义 handlers,输出到文件
'restful_api': {
'level': 'DEBUG',
# 时间滚动切分
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': os.path.join(LOGS_DIR, 'web-log.log'),
'formatter': 'standard',
# 调用过滤器
'filters': ['request_info'],
# 每天凌晨切分
'when': 'MIDNIGHT',
# 保存 30 天
'backupCount': 30,
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': 'ERROR',
'propagate': False
},
'web.log': {
'handlers': ['restful_api'],
'level': 'INFO',
# 此记录器处理过的消息就不再让 django 记录器再次处理了
'propagate': False
},
}
}
Таким образом, будет журнал для каждого запроса, который проходит через Django, будь то Интернет или администратор Django. В частности, запишите, какие поля можно получить и настроить в соответствии с потребностями проекта.
Следует отметить, что поrequest.user
получить имя пользователя работает только дляsession
метод проверки подлинности, посколькуsession
После аутентификации имя пользователя будет присвоеноrequest.user
, поэтому его можно получить.
предполагается использоватьjwt
способ аутентификации,request.user
не представляет никакой ценности. Есть два способа получить имя пользователя: один — проанализировать его в промежуточном программном обеспечении журнала.jwt cookie
получить имя пользователя, но этот способ не годится, лучше переписатьjwt
Аутентификация, назначьте имя пользователяrequest.user
, чтобы его можно было вызывать в любом другом местеrequest.user
чтобы получить значение.
Выше приведено все содержание логирования в Django, я надеюсь, что каждый сможет хорошо логировать, потому что это определенно будет полезно.
Справочная документация:
docs.Django project.com/en/2.1/topi…