关于python:Django笔记三十之log日志的记录详解

4次阅读

共计 10979 个字符,预计需要花费 28 分钟才能阅读完成。

这一节介绍在 Django 零碎里应用 logging 记录日志

以下是一个简略的 logging 模块示例,能够先预览一下,接下来会具体介绍各个模块的具体性能:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {'format': '%(levelname)s %(message)s',
        }
    },
    'handlers': {
        'file_1': {
            'level': 'INFO',
            'filename': '/Users/hunter/python/log_path/file_1.log',
            'formatter': 'verbose',
            'class': 'logging.FileHandler',
        },
        'file_2': {
            'level': 'INFO',
            'class': 'logging.FileHandler',
            'filename': '/Users/hunter/python/log_path/file_2.log',
            'formatter': 'verbose',
        },
        'custom_file': {
            'level': 'INFO',
            'class': 'logging.FileHandler',
            'filename': '/Users/hunter/python/log_path/custome_file.log',
            'formatter': 'verbose',
        }
    },
    'loggers': {'': {'handlers': ['file_1'],'level':'INFO','propagate': False,
        },
        'django': {'handlers': ['file_2'],
            'level': 'INFO',
            'propagate': True,
        },
        'custom': {'handlers': ['custom_file'],
            'level': 'INFO',
            'propagate': False,
        }
    }
}

以下是本篇笔记全部内容:

  1. 模块总览
  2. Loggers
  3. Handlers
  4. Filters
  5. Formatters
  6. 日志记录形式
  7. logger 参数解析
  8. handler 参数解析

    1. RotatingFileHandler 配置
    2. TimedRotatingFileHandler 配置
    3. HttpHandler 根本配置
    4. SMTPHandler 根本配置
    5. AdminEmailHandler 根本配置
  9. formatter 参数解析
  10. 指定 logger 输入
  11. 日志配置示例

1、模块总览

在 Django 零碎中,日志的记录也能够在 setting.py 中配置,key 为 logging,而后上面有几个次要的模块:

loggers、handlers、filters、formatters

零碎接管到日志信息后,进入 logger,而后依据指定的 handler 列表发送到 handler 中

依据 handler 的解决形式,将信息写入文件、发送邮件或者其余形式

这些信息能够通过 filter 进行进一步的过滤,依据 formatter 的信息组织模式通过 handler 的解决形式进行解决

2、Loggers

Loggers 是学习日志零碎的一个切入点,每个 logger 都是一个命名的桶,解决的信息能够作为日志写入到 logger 里

每一个 logger 都能够被配置一个日志等级,日志等级形容了 logger 记录的信息的重大水平,python 定义了如下几种日志等级:

  • DEBUG:低的、基于调试目标的零碎信息
  • INFO:个别零碎音讯
  • WARNING:产生了小问题的信息
  • ERROR:产生了大问题的信息
  • CRITICAL:产生了重大的问题的信息

每个被写入 logger 的音讯都被称为是一个 Log Record(日志记录)。

每个日志记录在被发送到 logger 的时候都有一个日志等级来示意信息的重大水平
比方:

logger.info("xxx")

这些日志记录应该蕴含一些有用的、蕴含了问题产生起因的信息

当一条音讯被发送到 logger,音讯的等级会和 logger 的日志等级做一个比拟,只有当音讯的等级大于或等于 logger 的记录等级时,音讯才会被以后 logger 进行更多的解决

如果这条音讯被 logger 接管,那么它会被发送到 Handlers

3、Handlers

咱们能够了解 handler 是一个处理器,用来决定每天发送到 logger 的信息应该怎么解决,也就是日志的记录模式

比如说写入一个文件,发送邮件等

跟 Logger 一样,handler 也有一个日志等级,只有当发送到 handler 的日志等级大于等于 handler 的日志记录时,handler 才会解决信息

一个 Logger 能够有多个 handler 处理器,每个 handler 都能够有本人的日志等级,因而能够依据信息的重要水平来决定不同的输入

比方你能够用一个 handler 把 ERROR 和 CRITICAL 等级的信息转发到服务页面,另一个 handler 记录所有的信息到一个文件,用作后续的剖析

4、Filters

过滤器常被用来提供额定的管制,解决从 logger 到 handler 的日志记录

实践上来说,任何日志音讯只有满足了日志等级的要求,都会被发送到 handler 解决,如果加了一个 filter 过滤器,你能够在日志解决上增加额定的规范

比如说你能够增加一个过滤器,只容许某个特定起源的 ERROR 等级的信息被解决

filter 也能够用来批改音讯的重大等级,比方一些特定的条件被满足的状况下,你能够将 ERROR 等级的日志降级为 WARNING

在本篇笔记中,将不介绍 filter 的应用办法,因为能简略就简略一点,临时不必那么多配置

5、Formatters

格式化,一个日志记录须要被渲染成一个文本,formatter 提供了一些格局器的属性,格式化器由一些 LogRecord 的属性值组成,你也能够本人定义一个属性

6、日志记录形式

当你配置了 loggers,handlers,filters 和 formatters 之后,你能够先获取一个 logger 的实例,而后通过 logger 来记录日志

以下是应用示例:

import logging

logger = logging.getLogger(__name__)

def my_view(request):
    logger.info("this is a log")

这个在调用 my_view 的时候,零碎就会记录一条日志

如果是其余等级的日志记录,则会是:

logger.debug()
logger.info()
logger.warning()
logger.error()
logger.critical()

以下是对日志的记录流程汇总一下:

  • 当有一条日志信息须要被记录,而后会被发送到对应的 logger
  • 而后 logger 依据指定的 handler 被发送到对应的 handler 处理器
  • 在 handler 中会依据日志的等级或者定义的 filter 进行肯定的过滤
  • 最终将符合条件的日志信息依据 formatter 的格局定义,将最终造成的日志信息,进行 console 操作、记录到文件、或者发送邮件等操作

在笔记开篇的 logging 示例中,日志的配置在这个 dict 里编写的程序和音讯解决的程序是相同的

这里没有设置 filter 的操作,所以音讯的解决就是从 logger 到 handler 再到 formatter

咱们手动在接口里写入一个日志音讯,别离在 urls.py 和 views.py 里如下定义:

# blog/urls.py
from django.urls.conf import path
from blog.views import time_view


urlpatterns = [path("curr_time", time_view),
]
# blog/views.py
import datetime
from django.http import HttpResponse

import logging

logger = logging.getLogger(__name__)

def time_view(request):
    now = datetime.datetime.now()
    html = "<h1>now: %s</h1>" % now
    logger.info("this is a log !")
    return HttpResponse(html)

启动零碎后,在浏览器中拜访 http://localhost:9898/blog/curr_time,能够看到定义的日志目录下曾经写入了数据:file_1.log 和 file_2.log

关上这两个日志文件,能够看到 loggers 下空字符串指定的 logger 对应的处理器写入的 file_1.log 写入的内容如下:

INFO this is a log ! xxxx
INFO "GET /blog/curr_time HTTP/1.1" 200 40 xxxx

其中蕴含接口访问信息和咱们在接口里自定义的 ‘this is a log !’ 信息

在 file_2.log 中,则只有接口的访问信息:

INFO  200 40 xxxx

在实例化 logger 的时候,如果不指定 logger 的名称,那么则会默认写入咱们定义的空字符串下的 logger

不指定 logger 名称的意思即为,getLogger 的时候不指定 logger 的参数:

logger = logging.getLogger(__name__)

7、logger 参数解析

在这里 loggers 里设置两个 key,一个为空字符串,一个是 django。

咱们能够了解 key 为 django’ 这个 logger 是一个固定的值,会接管到所有来自零碎的日志信息,比方一些接口的申请信息,然而不包含用户自定的 logger 输入。

空字符串这里的 logger,能够接管到用户自定义的 logger 输入,也能够接管到一些接口的申请信息,然而这个须要 propagate 的配置

在 loggers 的配置外面:

    'loggers': {'': {'handlers': ['file_1'],'level':'INFO','propagate': False,
        },
        'django': {'handlers': ['file_2'],
            'level': 'INFO',
            'propagate': True,
        }
    }

有如下几个参数:
handlers 是指定音讯处理器的,value 是一个列表,能够指定多个处理器,比如说一条音讯,你能够同时指定写入文件和发送邮件,或者写入不同的文件

level 参数示意日志的等级,这里设置的是 INFO 等级,如果接管到的音讯的等级小于 INFO,那么就会不解决,大于等于 INFO 才会被发送到 handler 处理器中解决

propagate 参数意义能够了解为是否传递传递,在这两个 logger 里,如果 django 这个 logger 的 propagate 的值设为了 True,django 这个 logger 的音讯是能够向 空字符串设置的 logger 传递的

换句话说,django 接管到的所有音讯都会给空字符串的 logger 再发一遍,应用它的 logger 再进行一遍解决,
如果 propagate 设为了 False,那么空字符串的 logger 仅能接管到用户自定义的音讯

8、handler 参数解析

当一条音讯从 logger 被发送到 handler,handlers 参数也能够定义多个,通过不同的 key 来辨别

在每个 handler 下咱们这里设置了四个值:

level 设置了 handler 解决的日志等级,只有当发送过去的日志的等级大于等于该等级时,这个 handler 才会解决

class 设置了日志解决的形式,这里咱们的值为 logging.FileHandler,示意是文件解决形式

此外还有比方 StreamHandler 输入到 Stream 打印到规范输入,还有 HttpHandler 通过 HTTP 协定向服务器发送 log, 还有 SMTPHandler 会通过 email 发送 log

filename 指定输入的日志地址,后面咱们的 class 定义为向文件输入,那么这里的 filename 就定义了输入的文件的地址

formatter 则是指定下一级日志文本的输入格局解决的 formatter

日志文件解决策略

对于日志文件,如果零碎始终运行,那么则会存在一个问题,那就是日志文件越来越大,这个对于零碎的存储和咱们查找日志都是不适合的

因而接下来咱们新增几个参数用来制订日志文件的解决策略

maxBytes,这个定义了一个日志文件最大的字节数,如果写满了就会新开一个文件持续写而不是持续在原有文件持续减少内容

如果咱们须要设置一个文件最大为 5M,就能够设为 5 1024 1024

backupCount,最大的日志文件数量,当文件的个数超出了咱们定义的,则会删除最早的日志文件,只保留 backupCount 个日志文件

然而应用下面这两个参数的话,class 的值就得换成 logging.handlers.RotatingFileHandler

接下来介绍几种 handler 的信息处理形式

1.RotatingFileHandler 配置

rotate 的是定期调换位子,轮换的意思

RotatingFileHandler 的作用是依据文件的大小决定是否写入新文件,以下是一个示例:

        'custom_file': {
            'level': 'INFO',
            'filename': '/home/hunter/python/log_path/custom.log',
            'class': 'logging.handlers.RotatingFileHandler',
            'formatter': 'verbose',
            'maxBytes': 5 * 1024 * 1024,
            'backupCount': 10
        }

这个示例示意是将日志写入文件,每个文件最大容量为 5 1024 1024,即 5M,当日志写入一个文件满了 5M 之后,将会新开一个文件持续写入日志信息,文件夹下保留最新的 10 个文件。

这里新增了两个配置项

backupCount 示意最多保留日志文件的个数

maxBytes 示意每个日志文件最大的存储容量

2.TimedRotatingFileHandler 配置

TimedRotatingFileHandler 示意是依据工夫距离来决定是否写入新文件,以下是示例:

        'time_file': {
            'level': 'INFO',
            'filename': '/home/hunter/python/log_path/custom.log',
            'class': 'logging.handlers.TimedRotatingFileHandler',  # 记录时间
            'formatter': 'verbose',
            'backupCount': 3,
            'when': 'M',
            'interval': 3,
        }

当 handler 的 class 的值为这个的时候,示意的是依据工夫来决定是否写入新文件,上一个是依据文件的容量大小来定的

这里新增了两个配置项,

一个是 when,示意的工夫距离的单位,S 为秒,M 为分钟,H 为小时,D 或者 MIDNIGHT 为天,W0-W6 为从周一到周日某个周几开始距离一周

另一个是 interval,间隔时间的倍数

日志换新文件持续写入的工夫为 when * interval

3.HttpHandler 根本配置

这个配置示意是如果来了须要解决的日志音讯就调用一个 HTTP 接口,这里咱们能够只做一个示例:

        'http_handler': {
            'level': 'INFO',
            'class': 'logging.handlers.HTTPHandler',
            'formatter': 'verbose',
            'host': '192.168.1.8:9898',
            'url': '/test_url',
            'method': 'POST',
        },

这个中央,多了几个配置项

host 示意须要调用接口的 ip 和 端口

url 示意调用的接口门路

method 示意调用的办法

4.SMTPHandler 根本配置

这个配置用于发送邮件,如果日志音讯发送到这个配置的 handler,零碎会依据邮件的配置零碎发送邮件给指定的邮箱

以下是一个应用示例:

        'email': {
            'level': 'WARNING',
            'class': 'logging.handlers.SMTPHandler',
            'mailhost': ('smtp.163.com', 25),
            'fromaddr': 'xxxxxxxx@163.com',
            'toaddrs': 'xxxxxxx@qq.com',
            'subject': '零碎出错啦!!!',
            'credentials': ('xxxxxxx@163.com', 'JBD******'),
            'timeout': 20
        },

在这个配置中,多的配置项的介绍如下:

mailhost 是零碎发送邮件的邮箱的主机和端口,这里咱们配置的是 163 邮箱

fromaddr 是从哪个邮箱收回来,咱们能够创立一个 163 邮箱而后指定该值

toaddrs 是发送到哪个邮箱,即日志音讯的邮件接管地址

subject 是咱们发送邮件的题目,而邮件的注释内容即为咱们在 logger.warning(“ 报错信息 ”) 中输出的信息

credentials 是 163 邮箱的验证信息,两个值,前一个值与 fromaddr 保持一致,前面的是一串验证码,是 163 邮箱开启 SMTP 服务之后 163 邮箱零碎页面给咱们的一串受权明码,这个能够本人去理解一下

这样配置好之后,在 logger 的 handler 列表指定这个 handler,而后通过 logger.warning(“ 报错信息 ”) 即可触发这个邮件发送的性能

5.AdminEmailHandler 根本配置

这个配置也是用于日志发送邮件,然而是复用 Django 的默认邮箱的性能

在 logging 中的配置是:

        'mail_admins': {
            'level': 'WARNING',
            'class': 'django.utils.log.AdminEmailHandler',
            'include_html': True,
        },

然而这个还须要一些额定的在 settings.py 中的邮箱配置,相当于是复用 Django 零碎的性能

以下是 settings.py 中邮箱的配置项:

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com'   # 163 邮箱的配置地址
EMAIL_PORT = 465  # SMTP 端口
EMAIL_HOST_USER = 'xxxxxx@163.com'   #这个是用来发送邮件的邮箱,与最初一个填写的邮箱地址统一
EMAIL_HOST_PASSWORD = 'JBDM******'  #这里就是后面提到的受权明码
EMAIL_USE_SSL = True
EMAIL_FROM = SERVER_EMAIL = 'xxxxxxx@163.com' # 这个是发送邮件的地址,与下面的 163 邮箱雷同即可
ADMINS = [['Hunter', 'xxxxxx@qq.com'],
]  # 邮件接管地址

下面的参数都配置好之后也能够日志触发邮件了。

9、formatter 参数解析

formatter 的参数就简略一点,通过不同的 key 来辨别不同的 formatter,其下设置一个 format 参数即可对信息进行格式化解决

    'formatters': {
        'verbose': {'format': '%(levelname)s %(message)s',
        }
    },

在示例中只设置了 levelname 和 message 两个参数,levelname 即为该日志音讯的等级,message 为音讯内容

对于申请接口的 message 信息,返回的内容是固定的,比方后面的示例:

"GET /blog/curr_time HTTP/1.1" 200 40

后面是接口的申请形式、接口门路和 HTTP 协定,而后是接口返回的状态码,这里是 200,前面跟着的 40 这个数字则是接口返回的字符长度

如果是用户在零碎里手动写入的 message,则是定义的什么内容,输入的就是什么内容

对于 format 的定义的参数还有很多,以下是几个罕用的汇总:

参数名称 参数用法 含意
levelname %(levelname)s 日志等级
message %(message)s 音讯内容
asctime %(asctime)s 工夫,格局为 ’2022-01-01 00:00:00,000′
pathname %(pathname)s 日志输入所在文件的全门路
filename %(filename)s 日志输入所在的文件名
module %(module)s 日志输入所在的模块,能够了解成不带后缀的文件名
name %(name)s 调用日志应用的名称,logging.getLogger(__name__)时为从模块到函数,比方 blog.views
funcName %(funcName)s 日志输入的函数名称
lineno %(lineno)d 日志输入所在的文件的行数
process %(process)d 过程 id
processName %(processName)s 过程名称
thread %(thread)d 线程 id
threadName %(threadName)s 线程名称

10、指定 logger 输入

之前咱们设定的用户手动输出的日志被传送给了 key 为空字符串下的 logger,如果咱们想把某一些日志信息专门输入到某个文件怎么解决呢?

在获取 logger 的时候就须要依据 logger 的 key 来指定对应的 logger,比方咱们新建一个名为 custom 的 logger 和 对应的 handler,而后输入的中央指定即可,如下:

        'custom': {'handlers': ['custom_file'],
            'level': 'INFO',
            'propagate': False,
        }

指定 logger 输入:

import datetime
from django.http import HttpResponse

import logging

custom_logger = logging.getLogger("custom")  # 对应 logging 配置中的 key 为 custom 的 logger 


def time_view(request):
    now = datetime.datetime.now()
    html = "<h1>now: %s</h1>" % now
    custom_logger.info("this is a custom log")
    return HttpResponse(html)

这样在对应的中央就能够实现专门的日志输入到专门的文件了。

11、日志配置示例

接下来咱们实现这样一个日志配置的性能:

  1. 实现用户所有一般的手动输入都写入一个 manual.log 文件
  2. 所有接口的申请数据都输出到一个 request.log 文件
  3. 设置一个独自的日志输入,能够输入到指定文件
  4. 所有 INFO 级别的日志都输入到文件,高于 INFO 的都发送邮件告诉指定联系人
  5. 对于日志文件要求每个文件最大容量为 50M,且文件夹下每个类型的日志最多只有 10 个
  6. 日志的信息结构为:日志等级 - 工夫 - 日志输入所在文件名 - 日志输入所在函数名 - 日志输入所在文件的行数 - 音讯内容

以下是实现下面这个性能的 logging 配置:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {'format': '%(levelname)s %(asctime)s %(filename)s %(funcName)s %(lineno)d %(message)s',
        }
    },
    'handlers': {
        'manual_file': {
            'level': 'INFO',
            'filename': '/Users/hunter/python/log_path/manual.log',
            'formatter': 'verbose',
            'class': 'logging.handlers.RotatingFileHandler',
            'maxBytes': 5 * 1024 * 1024,
            'backupCount': 10
        },
        'request_file': {
            'level': 'INFO',
            'filename': '/Users/hunter/python/log_path/request.log',
            'class': 'logging.handlers.RotatingFileHandler',
            'formatter': 'verbose',
            'maxBytes': 5 * 1024 * 1024,
            'backupCount': 10
        },
        'custom_file': {
            'level': 'INFO',
            'filename': '/Users/hunter/python/log_path/custom.log',
            'class': 'logging.handlers.RotatingFileHandler',
            'formatter': 'verbose',
            'maxBytes': 5 * 1024 * 1024,
            'backupCount': 10
        },
        'email': {
            'level': 'WARNING',
            'class': 'logging.handlers.SMTPHandler',
            'mailhost': ('smtp.163.com', 25),
            'fromaddr': 'xxxxxx@163.com',
            'toaddrs': 'xxxxxxx@qq.com',
            'subject': '零碎出错啦!!!',
            'credentials': ('xxxxxx@163.com', 'JBD*******'),
            'timeout': 20
        },
    },
    'loggers': {'': {'handlers': ['manual_file','email'],'level':'INFO','propagate': False,
        },
        'django': {'handlers': ['request_file'],
            'level': 'INFO',
            'propagate': True,
        },
        'custom': {'handlers': ['custom_file'],
            'level': 'INFO',
            'propagate': False,
        },
    },
}

而后咱们定义一个接口内容:

import datetime
from django.http import HttpResponse, JsonResponse

import logging

logger = logging.getLogger(__name__)
custom_logger = logging.getLogger("custom")


def time_view(request):
    now = datetime.datetime.now()
    html = "<h1>now: %s</h1>abc\nabc" % now
    logger.info("this is a log !")
    custom_logger.info("this is a custom log")
    logger.warning("报错啦!!!")
    return HttpResponse(html)

调用这个接口即可发现实现了咱们想要的性能啦!

本文首发于自己微信公众号:Django 笔记。

原文链接:Django 笔记三十之 log 日志的记录详解

如果想获取更多相干文章,可扫码关注浏览:

正文完
 0