高阶应用-日志

2022-09-14 17:32:58 浏览数 (1)

一、Log简介

logging模块是Python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径、日志文件回滚等;相比print,具备如下优点:

通过log的分析,可以方便用户了解系统或软件、应用的运行情况;如果你的应用log足够丰富,也可以分析以往用户的操作行为、类型喜好、地域分布或其他更多信息;如果一个应用的log同时也分了多个级别,那么可以很轻易地分析得到该应用的健康状况,及时发现问题并快速定位、解决问题,补救损失。

二、Log的用途

不管是使用何种编程语言,日志输出几乎无处不再。总结起来,日志大致有以下几种用途:

  • 问题追踪:通过日志不仅仅包括我们程序的一些bug,也可以在安装配置时,通过日志可以发现问题。
  • 状态监控:通过实时分析日志,可以监控系统的运行状态,做到早发现问题、早处理问题。
  • 安全审计:审计主要体现在安全上,通过对日志进行分析,可以发现是否存在非授权的操作

三、Log等级

  • DEBUG最详细的日志信息,典型应用场景是 问题诊断
  • INFO信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作
  • WARNING当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的
  • ERROR由于一个更严重的问题导致某些功能不能正常运行时记录的信息 如IO操作失败或者连接问题
  • CRITICAL当发生严重错误,导致应用程序不能继续运行时记录的信息

四、日志记录级别

​ logging模块的重点在于生成和处理日志消息。每条消息由一些文本和指示其严重性的相关级别组成。级别包含符号名称和数字值。

级别

描述

CRITICAL

50

关键错误/消息

ERROR

40

错误

WARNING

30

警告消息

INFO

20

通知消息

DEBUG

10

调试

NOTSET

0

无级别

五、Log模块的四大组件

  • Loggers 提供应用程序代码直接使用的接口
  • Handlers 用于将日志记录发送到指定的目的位置
    • 内置处理器 logging模块提供了一些处理器,可以通过各种方式处理日志消息。使用addHandler()方法将这些处理器添加给Logger对象。另外还可以为每个处理器配置它自己的筛选和级别
      • handlers.DatagramHandler(host,port):发送日志消息给位于制定host和port上的UDP服务器
      • handlers.FileHandler(filename):将日志消息写入文件filename
      • handlers.HTTPHandler(host, url):使用HTTP的GET或POST方法将日志消息上传到一台HTTP 服务器
      • handlers.RotatingFileHandler(filename):将日志消息写入文件filename。如果文件的大小超出maxBytes制定的值,那么它将被备份为filename1
  • Filters 提供更细粒度的日志过滤功能,用于决定哪些日志记录将会被输出(其它的日志记录将会被忽略)
  • Formatters 用于控制日志信息的最终输出格式

六、记录器

​ 记录器负责管理日志消息的默认行为,包括日志记录级别、输出目标位置、消息格式以及其它基本细节。

关键字参数

描述

filename

将日志消息附加到指定文件名的文件

filemode

指定用于打开文件模式

format

用于生成日志消息的格式字符串

datefmt

用于输出日期和时间的格式字符串

level

设置记录器的级别

stream

提供打开的文件,用于把日志消息发送到文件。

七、format 日志消息格式

格式

描述

%(name)s

记录器的名称

%(levelno)s

数字形式的日志记录级别

%(levelname)s

日志记录级别的文本名称

%(filename)s

执行日志记录调用的源文件的文件名称

%(pathname)s

执行日志记录调用的源文件的路径名称

%(funcName)s

执行日志记录调用的函数名称

%(module)s

执行日志记录调用的模块名称

%(lineno)s

执行日志记录调用的行号

%(created)s

执行日志记录的时间

%(asctime)s

日期和时间

%(msecs)s

毫秒部分

%(thread)d

线程ID

%(threadName)s

线程名称

%(process)d

进程ID

%(message)s

记录的消息

八、logging

简单使用-控制台查看

示例

代码语言:javascript复制
<span class="hljs-keyword">import</span> logging

s = <span class="hljs-string">'0'</span>
n = int(s)
logging.info(<span class="hljs-string">'n = %d'</span> % n)
print(<span class="hljs-number">10</span> / n)

logging.info()就可以输出一段文本

配置级别

代码语言:javascript复制
<span class="hljs-keyword">import</span> logging
logging.basicConfig(level=logging.INFO)

输出结果

代码语言:javascript复制
$ python err.py
INFO:root:n = <span class="hljs-number">0</span>
Traceback (most recent call last):
  File <span class="hljs-string">"err.py"</span>, line <span class="hljs-number">8</span>, <span class="hljs-keyword">in</span> &lt;module&gt;
    print(<span class="hljs-number">10</span> / n)
ZeroDivisionError: division by zero

说明:

这就是logging的好处,它允许你指定记录信息的级别,有debuginfowarningerror等几个级别,当我们指定level=INFO时,logging.debug就不起作用了。同理,指定level=WARNING后,debuginfo就不起作用了。这样一来,你可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。

logging的另一个好处是通过简单的配置,一条语句可以同时输出到不同的地方,比如console和文件

logging进阶使用-控制台查看

配置logging基本的设置,然后在控制台输出日志

代码语言:javascript复制
<span class="hljs-keyword">import</span> logging
logging.basicConfig(level = logging.INFO,format = <span class="hljs-string">'%(asctime)s - %(name)s - %(levelname)s - %(message)s'</span>)
logger = logging.getLogger(__name__)

logger.info(<span class="hljs-string">"Start print log"</span>)
logger.debug(<span class="hljs-string">"Do something"</span>)
logger.warning(<span class="hljs-string">"Something maybe fail."</span>)
logger.info(<span class="hljs-string">"Finish"</span>)

运行时,控制台输出

代码语言:javascript复制
<span class="hljs-number">2088</span><span class="hljs-number">-10</span><span class="hljs-number">-09</span> <span class="hljs-number">19</span>:<span class="hljs-number">11</span>:<span class="hljs-number">19</span>,<span class="hljs-number">434</span> - __main__ - INFO - Start <span class="hljs-keyword">print</span> log
<span class="hljs-number">2088</span><span class="hljs-number">-10</span><span class="hljs-number">-09</span> <span class="hljs-number">19</span>:<span class="hljs-number">11</span>:<span class="hljs-number">19</span>,<span class="hljs-number">434</span> - __main__ - WARNING - Something maybe fail.
<span class="hljs-number">2088</span><span class="hljs-number">-10</span><span class="hljs-number">-09</span> <span class="hljs-number">19</span>:<span class="hljs-number">11</span>:<span class="hljs-number">19</span>,<span class="hljs-number">434</span> - __main__ - INFO - Finish

将logger的级别改为DEBUG,再观察一下输出结果

代码语言:javascript复制
logging.basicConfig(level = logging.DEBUG,format = <span class="hljs-string">'%(asctime)s - %(name)s - %(levelname)s - %(message)s'</span>)

控制台输出,可以发现,输出了debug的信息

代码语言:javascript复制
<span class="hljs-number">2088</span><span class="hljs-number">-10</span><span class="hljs-number">-09</span> <span class="hljs-number">19</span>:<span class="hljs-number">12</span>:<span class="hljs-number">08</span>,<span class="hljs-number">289</span> - __main__ - INFO - Start <span class="hljs-keyword">print</span> log
<span class="hljs-number">2088</span><span class="hljs-number">-10</span><span class="hljs-number">-09</span> <span class="hljs-number">19</span>:<span class="hljs-number">12</span>:<span class="hljs-number">08</span>,<span class="hljs-number">289</span> - __main__ - DEBUG - Do something
<span class="hljs-number">2088</span><span class="hljs-number">-10</span><span class="hljs-number">-09</span> <span class="hljs-number">19</span>:<span class="hljs-number">12</span>:<span class="hljs-number">08</span>,<span class="hljs-number">289</span> - __main__ - WARNING - Something maybe fail.
<span class="hljs-number">2088</span><span class="hljs-number">-10</span><span class="hljs-number">-09</span> <span class="hljs-number">19</span>:<span class="hljs-number">12</span>:<span class="hljs-number">08</span>,<span class="hljs-number">289</span> - __main__ - INFO - Finish

将日志写入到文件

设置logging,创建一个FileHandler,并对输出消息的格式进行设置,将其添加到logger,然后将日志写入到指定的文件中

示例

代码语言:javascript复制
<span class="hljs-keyword">import</span> logging
logger = logging.getLogger(__name__)
logger.setLevel(level = logging.INFO)
handler = logging.FileHandler(<span class="hljs-string">"log.txt"</span>)
handler.setLevel(logging.INFO)
formatter = logging.Formatter(<span class="hljs-string">'%(asctime)s - %(name)s - %(levelname)s - %(message)s'</span>)
handler.setFormatter(formatter)
logger.addHandler(handler)

logger.info(<span class="hljs-string">"Start print log"</span>)
logger.debug(<span class="hljs-string">"Do something"</span>)
logger.warning(<span class="hljs-string">"Something maybe fail."</span>)
logger.info(<span class="hljs-string">"Finish"</span>)

log.txt中日志数据为

代码语言:javascript复制
<span class="hljs-number">2088</span><span class="hljs-number">-10</span><span class="hljs-number">-09</span> <span class="hljs-number">19</span>:<span class="hljs-number">01</span>:<span class="hljs-number">13</span>,<span class="hljs-number">263</span> - __main__ - INFO - Start <span class="hljs-keyword">print</span> log
<span class="hljs-number">2088</span><span class="hljs-number">-10</span><span class="hljs-number">-09</span> <span class="hljs-number">19</span>:<span class="hljs-number">01</span>:<span class="hljs-number">13</span>,<span class="hljs-number">263</span> - __main__ - WARNING - Something maybe fail.
<span class="hljs-number">2088</span><span class="hljs-number">-10</span><span class="hljs-number">-09</span> <span class="hljs-number">19</span>:<span class="hljs-number">01</span>:<span class="hljs-number">13</span>,<span class="hljs-number">263</span> - __main__ - INFO - Finish

将日志同时输出到屏幕和日志文件

logger中添加StreamHandler,可以将日志输出到屏幕上

代码语言:javascript复制
<span class="hljs-keyword">import</span> logging
logger = logging.getLogger(__name__)
logger.setLevel(level = logging.INFO)
handler = logging.FileHandler(<span class="hljs-string">"log.txt"</span>)
handler.setLevel(logging.INFO)
formatter = logging.Formatter(<span class="hljs-string">'%(asctime)s - %(name)s - %(levelname)s - %(message)s'</span>)
handler.setFormatter(formatter)

console = logging.StreamHandler()
console.setLevel(logging.INFO)

logger.addHandler(handler)
logger.addHandler(console)

logger.info(<span class="hljs-string">"Start print log"</span>)
logger.debug(<span class="hljs-string">"Do something"</span>)
logger.warning(<span class="hljs-string">"Something maybe fail."</span>)
logger.info(<span class="hljs-string">"Finish"</span>)

可以在log.txt文件和控制台中看到

代码语言:javascript复制
<span class="hljs-number">2088</span><span class="hljs-number">-10</span><span class="hljs-number">-09</span> <span class="hljs-number">19</span>:<span class="hljs-number">20</span>:<span class="hljs-number">46</span>,<span class="hljs-number">553</span> - __main__ - INFO - Start <span class="hljs-keyword">print</span> log
<span class="hljs-number">2088</span><span class="hljs-number">-10</span><span class="hljs-number">-09</span> <span class="hljs-number">19</span>:<span class="hljs-number">20</span>:<span class="hljs-number">46</span>,<span class="hljs-number">553</span> - __main__ - WARNING - Something maybe fail.
<span class="hljs-number">2088</span><span class="hljs-number">-10</span><span class="hljs-number">-09</span> <span class="hljs-number">19</span>:<span class="hljs-number">20</span>:<span class="hljs-number">46</span>,<span class="hljs-number">553</span> - __main__ - INFO - Finish

九、Django中配置输出到文件

  • 配置setting.py配置文件
代码语言:javascript复制
LOGGING = {
    <span class="hljs-string">'version'</span>: <span class="hljs-number">1</span>,
    <span class="hljs-string">'disable_existing_loggers'</span>: <span class="hljs-keyword">True</span>,
    <span class="hljs-string">'formatters'</span>: {  <span class="hljs-comment"># 日志格式 </span>
       <span class="hljs-string">'standard'</span>: {
            <span class="hljs-string">'format'</span>: <span class="hljs-string">'%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s'</span>}  
    },
    <span class="hljs-string">'filters'</span>: {  <span class="hljs-comment"># 过滤器</span>
    },
    <span class="hljs-string">'handlers'</span>: {  <span class="hljs-comment"># 处理器</span>
        <span class="hljs-string">'null'</span>: {
            <span class="hljs-string">'level'</span>: <span class="hljs-string">'DEBUG'</span>,
            <span class="hljs-string">'class'</span>: <span class="hljs-string">'logging.NullHandler'</span>,
        },
        <span class="hljs-string">'debug'</span>: {  <span class="hljs-comment"># 输出到文件</span>
            <span class="hljs-string">'level'</span>:<span class="hljs-string">'DEBUG'</span>,
            <span class="hljs-string">'class'</span>:<span class="hljs-string">'logging.handlers.RotatingFileHandler'</span>,
            <span class="hljs-string">'filename'</span>: os.path.join(BASE_DIR, <span class="hljs-string">"log"</span>,<span class="hljs-string">'debug.log'</span>),  <span class="hljs-comment"># 日志输出文件</span>
            <span class="hljs-string">'maxBytes'</span>:<span class="hljs-number">1024</span>*<span class="hljs-number">1024</span>*<span class="hljs-number">5</span>,  <span class="hljs-comment"># 文件大小 </span>
            <span class="hljs-string">'backupCount'</span>: <span class="hljs-number">5</span>,  <span class="hljs-comment"># 备份份数</span>
            <span class="hljs-string">'formatter'</span>:<span class="hljs-string">'standard'</span>,  <span class="hljs-comment"># 使用哪种formatters日志格式</span>
        },
        <span class="hljs-string">'console'</span>:{  <span class="hljs-comment"># 输出到控制台</span>
            <span class="hljs-string">'level'</span>: <span class="hljs-string">'DEBUG'</span>,
            <span class="hljs-string">'class'</span>: <span class="hljs-string">'logging.StreamHandler'</span>,
            <span class="hljs-string">'formatter'</span>: <span class="hljs-string">'standard'</span>,
        },
    },
    <span class="hljs-string">'loggers'</span>: {  <span class="hljs-comment"># logging管理器</span>
        <span class="hljs-string">'django'</span>: {
            <span class="hljs-string">'handlers'</span>: [<span class="hljs-string">'console'</span>],
            <span class="hljs-string">'level'</span>: <span class="hljs-string">'DEBUG'</span>,
            <span class="hljs-string">'propagate'</span>: <span class="hljs-keyword">False</span> 
        },
        <span class="hljs-string">'django.request'</span>: {
            <span class="hljs-string">'handlers'</span>: [<span class="hljs-string">'debug'</span>],
            <span class="hljs-string">'level'</span>: <span class="hljs-string">'ERROR'</span>,
            <span class="hljs-string">'propagate'</span>: <span class="hljs-keyword">True</span>,
        },
    } 
}

说明:把所有的debug信息输出到控制台,把error级别错误信息输出到文件。当然要先在django网站创建我设置的日志目录:log,要不然会出错。

解析:

  • formatters:配置打印日志格式
  • handler:用来定义具体处理日志的方式,可以定义多种,"default"就是默认方式,"console"就是打印到控制台方式
  • loggers:用来配置用那种handlers来处理日志,比如你同时需要输出日志到文件、控制台

十、Django配置邮件发送错误信息

关闭Debug模式

关闭调试模式,调试模式响应速度相对有些慢。关闭调试模式可以加快访问速度,而且可以避免敏感的调试信息泄漏

打开settings.py文件,把DEBUG设置False。接着还需要设置 ALLOWED_HOSTS (被运行的主机),如果这个设置的话,会提示错误。偷懒的人可以设置为 ALLOWED_HOSTS = [’*’] 但这种不建议,不够安全。应该根据自己的域名来设置。设置为 ALLOWED_HOSTS = [‘xxx.com’]

动态设置Debug模式

为了方便我开发和生产部署,不想每次都手动修改DEBUG这个值,导入socket模块,根据ip地址来设置。判断是否是127开头的IP地址

代码语言:javascript复制
<span class="hljs-keyword">import</span> socket 
<span class="hljs-keyword">if</span> socket.gethostbyname(socket.gethostname())[:<span class="hljs-number">3</span>]==<span class="hljs-string">'127'</span>:    
    DEBUG = <span class="hljs-keyword">True</span>
<span class="hljs-keyword">else</span>:    
    DEBUG = <span class="hljs-keyword">False</span> 
ALLOWED_HOSTS = [<span class="hljs-string">'*'</span>]

完整的代码

代码语言:javascript复制
<span class="hljs-keyword">import</span> socket
 
<span class="hljs-comment"># 根据IP地址判断是否是开发环境</span>
<span class="hljs-keyword">if</span> socket.gethostbyname(socket.gethostname())[:<span class="hljs-number">3</span>]==<span class="hljs-string">'127'</span>:    
    DEBUG = <span class="hljs-keyword">True</span>
<span class="hljs-keyword">else</span>:    
    DEBUG = <span class="hljs-keyword">False</span> 
 
ALLOWED_HOSTS = [<span class="hljs-string">'*'</span>]  <span class="hljs-comment"># 设置允许访问的主机</span>
 
<span class="hljs-comment"># 管理员邮箱</span>
ADMINS = (
    (<span class="hljs-string">'xialigang'</span>,<span class="hljs-string">'793390457@qq.com'</span>),
)
 
<span class="hljs-comment"># 非空链接,却发生404错误,发送通知MANAGERS</span>
SEND_BROKEN_LINK_EMAILS = <span class="hljs-keyword">True</span>
MANAGERS = ADMINS


<span class="hljs-comment"># Email</span>
EMAIL_BACKEND = <span class="hljs-string">'django.core.mail.backends.smtp.EmailBackend'</span>
EMAIL_HOST = <span class="hljs-string">'smtp.1000phone.com'</span>  <span class="hljs-comment"># QQ邮箱SMTP服务器</span>
EMAIL_HOST_USER = <span class="hljs-string">'xialigang@1000phone.com'</span>  <span class="hljs-comment"># 我的邮箱帐号</span>
EMAIL_HOST_PASSWORD = <span class="hljs-string">'******'</span>  <span class="hljs-comment"># 密码</span>
DEFAULT_FROM_EMAIL = SERVER_EMAIL = EMAIL_HOST_USER  <span class="hljs-comment"># 设置发件人</span>

<span class="hljs-comment">#logging日志配置</span>
LOGGING = {
    <span class="hljs-string">'version'</span>: <span class="hljs-number">1</span>,
    <span class="hljs-string">'disable_existing_loggers'</span>: <span class="hljs-keyword">True</span>,
    <span class="hljs-string">'formatters'</span>: {  <span class="hljs-comment"># 日志格式</span>
       <span class="hljs-string">'standard'</span>: {
            <span class="hljs-string">'format'</span>: <span class="hljs-string">'%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s'</span>}
    },
    <span class="hljs-string">'filters'</span>: {  <span class="hljs-comment"># 过滤器</span>
        <span class="hljs-string">'require_debug_false'</span>: {
                <span class="hljs-string">'()'</span>: <span class="hljs-string">'django.utils.log.RequireDebugFalse'</span>,
            }
    },
    <span class="hljs-string">'handlers'</span>: {  <span class="hljs-comment"># 处理器</span>
        <span class="hljs-string">'null'</span>: {
            <span class="hljs-string">'level'</span>: <span class="hljs-string">'DEBUG'</span>,
            <span class="hljs-string">'class'</span>: <span class="hljs-string">'logging.NullHandler'</span>,
        },
        <span class="hljs-string">'mail_admins'</span>: {<span class="hljs-comment"># 发送邮件通知管理员</span>
            <span class="hljs-string">'level'</span>: <span class="hljs-string">'ERROR'</span>,
            <span class="hljs-string">'class'</span>: <span class="hljs-string">'django.utils.log.AdminEmailHandler'</span>,
            <span class="hljs-string">'filters'</span>: [<span class="hljs-string">'require_debug_false'</span>],  <span class="hljs-comment"># 仅当 DEBUG = False 时才发送邮件</span>
            <span class="hljs-string">'include_html'</span>: <span class="hljs-keyword">True</span>,
        },
        <span class="hljs-string">'debug'</span>: {  <span class="hljs-comment"># 记录到日志文件(需要创建对应的目录,否则会出错)</span>
            <span class="hljs-string">'level'</span>:<span class="hljs-string">'DEBUG'</span>,
            <span class="hljs-string">'class'</span>:<span class="hljs-string">'logging.handlers.RotatingFileHandler'</span>,
            <span class="hljs-string">'filename'</span>: os.path.join(BASE_DIR, <span class="hljs-string">"log"</span>,<span class="hljs-string">'debug.log'</span>),  <span class="hljs-comment"># 日志输出文件</span>
            <span class="hljs-string">'maxBytes'</span>:<span class="hljs-number">1024</span>*<span class="hljs-number">1024</span>*<span class="hljs-number">5</span>,  <span class="hljs-comment"># 文件大小</span>
            <span class="hljs-string">'backupCount'</span>: <span class="hljs-number">5</span>,  <span class="hljs-comment"># 备份份数</span>
            <span class="hljs-string">'formatter'</span>:<span class="hljs-string">'standard'</span>,  <span class="hljs-comment"># 使用哪种formatters日志格式</span>
        },
        <span class="hljs-string">'console'</span>:{  <span class="hljs-comment"># 输出到控制台</span>
            <span class="hljs-string">'level'</span>: <span class="hljs-string">'DEBUG'</span>,
            <span class="hljs-string">'class'</span>: <span class="hljs-string">'logging.StreamHandler'</span>,
            <span class="hljs-string">'formatter'</span>: <span class="hljs-string">'standard'</span>,
        },
    },
    <span class="hljs-string">'loggers'</span>: {  <span class="hljs-comment"># logging管理器</span>
        <span class="hljs-string">'django'</span>: {
            <span class="hljs-string">'handlers'</span>: [<span class="hljs-string">'console'</span>,<span class="hljs-string">'debug'</span>], <span class="hljs-comment"># 控制台输出并写入debug文件</span>
            <span class="hljs-string">'level'</span>: <span class="hljs-string">'DEBUG'</span>,
            <span class="hljs-string">'propagate'</span>: <span class="hljs-keyword">False</span>
        },
        <span class="hljs-string">'django.request'</span>: {
            <span class="hljs-string">'handlers'</span>: [<span class="hljs-string">'debug'</span>,<span class="hljs-string">'mail_admins'</span>],
            <span class="hljs-string">'level'</span>: <span class="hljs-string">'ERROR'</span>,
            <span class="hljs-string">'propagate'</span>: <span class="hljs-keyword">True</span>,
        },
        <span class="hljs-comment"># 对于不在 ALLOWED_HOSTS 中的请求不发送报错邮件</span>
        <span class="hljs-string">'django.security.DisallowedHost'</span>: {
            <span class="hljs-string">'handlers'</span>: [<span class="hljs-string">'null'</span>],
            <span class="hljs-string">'propagate'</span>: <span class="hljs-keyword">False</span>,
        },
    }
}

注意:如果发送失败 注意查看debug是否关闭为False

0 人点赞