系统日志


日志能确保 Web 应用的稳定和健壮,它不仅能用于调试错误,而且能用于跟踪性能

记录异常活动并定期检查对服务器的安全也是至关重要的。

应用日志 VS 其它日志 Application Logs vs. Other Logs

Any log file containing data logged from your Python web application is considered an application log

应用日志是项目的 Python 代码产生的日志,其它的日志包括:服务器日志数据库日志网络日志

各种日志都很重要

使用不同的日志 DEBUG, INFO, WARNING, ERROR, and CRITICAL

使用 CRITICAL 记录灾难性的日志

Use the CRITICAL log level only when something catastrophic occurs that requires urgent attention.

This log level is never used in core Django code, but you should certainly use it in your code anywhere
that an extremely serious problem can occur.

生产环境下的错误 使用 ERROR 日志

DEBUG 设置为 False的时候, ADMINS管理员列表中的用户回收到 错误信息邮件

➤ A description of the error
错误描述

➤ A complete Python traceback from where the error occurred
错误发生的 traceback

➤ Information about the HTTP request that caused the error
HTTP request

Django 源码中log实现


# Taken directly from core Django code.
# Used here to illustrate an example only, so don't
# copy this into your project.
logger.error("Internal Server Error: %s", request.path,
    exc_info=exc_info,
    extra={
        "status_code": 500,
        "request": request
    }
)

捕捉到错误, 尽可能多的将错误信息放在异常信息中, 给admin邮件提示

低危的问题WARNING

Django uses the log level in several parts of CsrfViewMiddleware, to log events that result in a 403 Forbidden error.

Django 在 CsrfViewMiddleware 中使用该日志,记录 403 Forbidden 错误。例如,当 POST 请求没有 csrf_token 时,记录如下

# Taken directly from core Django code.
# Used here to illustrate an example only, so don't
# copy this into your project.
logger.warning("Forbidden (%s): %s",
                REASON_NO_CSRF_COOKIE, request.path,
    extra={
        "status_code": 403,
        "request": request,
    }
)

有用的陈述性信息INFO

包括:

➤ Startup and shutdown of important components not logged elsewhere
关键组件的开启和关闭

➤ State changes that occur in response to important events
重要事件的状态修改

➤ Changes to permissions, e.g. when users are granted admin access
权限更新,比如用户被授予了管理员权限

同时也可以用于性能分析中,用于查找性能瓶颈。

调试相关信息DEBUG

用于替换 print 语句


# bad
from django.views.generic import TemplateView
from .helpers import pint_counter

class PintView(TemplateView):
    def get_context_data(self, *args, **kwargs):
        context = super(PintView, self).get_context_data(**kwargs)
        pints_remaining = pint_counter()
        print('Only %d pints of ice cream left.' % (pints_remaining))
        return context




# good
import logging

from django.views.generic import TemplateView

from .helpers import pint_counter

logger = logging.getLogger(__name__)

class PintView(TemplateView):
    def get_context_data(self, *args, **kwargs):
        context = super(PintView, self).get_context_data(**kwargs)
        pints_remaining = pint_counter()
        logger.debug('Only %d pints of ice cream left.' % pints_remaining)
        return context

使用 print 语句存在的问题:

➤ Depending on the web server, a forgotten print statement can bring your site down.
有可以会使站点崩溃

➤ Print statements are not recorded. If you don’t see them, then you miss what they were trying to
say.
print 语句是无法记录的,一旦错过就不能回溯

➤ As the Django world migrates more and more to Python 3, old-style print statements like
print IceCream.objects.flavor() will break your code.
py2的 print 语法与 py3不同

捕获异常后,记录 Traceback

Python 的 logging 模块支持:

  • Logger.exception() 自动包含 traceback, 并且记录为 ERROR

import logging
import requests
logger = logging.getLogger(__name__)
def get_additional_data():
    try:
        r = requests.get('http://example.com/something-optional/')
    
    except requests.HTTPError as e:
        logger.exception(e)
        return None
    return r
  • 其它级别的日志,指定 exc_info 参数, logger.debug(msg, exc_info=True)

import logging
import requests

logger = logging.getLogger(__name__)

def get_additional_data():
    try:
        r = requests.get("http://example.com/something-optional/")
    except requests.HTTPError as e:
        logger.debug("Could not get additional data", exc_info=True)
        return None
    return r

每个module中使用一个 logger One Logger Per Module

不要重复使用某个 module中定义好的 logger, 在每个module中定义一个logger

Whenever you use logging in another module, don’t import and reuse a logger from elsewhere. Instead, define a new logger specific to the module like this:

# You can place this snippet at the top
# of models.py, views.py, or any other
# file where you need to log.
import logging
logger = logging.getLogger(__name__)

可以更好的设置每个module中的日志等级, 调试信息

在本地将日志记录到回滚文件 Log Locally to Rotating Files(防止email或者网络故障, 存在本地)

When you create a new Django project with startproject, your default settings file is configured to email ERROR and higher log messages to whomever you list in ADMINS. This occurs via a handler called AdminEmailHandler that comes with Django

Django 默认将 ERROR 及以上的日志信息以邮件形式发送给 ADMINS 中的管理员,这是通过 Django 内置的 AdminEmailHandler 实现。

In addition to this, we recommend also writing logs of level INFO and higher to rotating log files on disk. On-disk log files are helpful in case the network goes down or emails can’t be sent to your admins for some reason. Log rotation keeps your logs from growing to fill your available disk space.

在本地,推荐将 INFO 及以上的日志记录到本地一个回滚文件中,在 UNIX 上创建回滚文件用 logrotate 工具配置日志的处理,并使用 logging.handlers.WatchedFileHandler

其他的logging建议

调试时,使用 logger 的 DEBUG

在 DEBUG 下运行测试后,再在 INFO 和 WARNING 级再次运行测试

尽早增加日志功能

当收到 ERROR 的邮件信息时,可以配置一个 PagerDuty 账号来对该项问题进行重复提醒,直到处理完毕。
Buy me a 肥仔水!