在这里插入图片描述
Django实践-07日志调试与sql优化
官网:https://www.djangoproject.com/ 博客:https://www.liujiangblog.com/ 本博客内容参考git:https://gitcode.net/mirrors/jackfrued/Python-100-Days 一些细节问题,大家可以查看git连接。本文主要的改变为把代码升级为django4.1版本。
Django静态文件问题备注: 参考: Django测试开发-20-settings.py中templates配置,使得APP下的模板以及根目录下的模板均可生效 解决django 多个APP时 static文件的问题
django配置app中的静态文件步骤 Django多APP加载静态文件
django.short包参考:https://docs.djangoproject.com/en/4.1/topics/http/shortcuts/
配置日志
参考: https://docs.djangoproject.com/zh-hans/4.1/topics/logging/#configuring-logging https://docs.python.org/3/library/logging.config.html#logging-config-dictschema
项目开发阶段,显示足够的调试信息以辅助开发人员调试代码还是非常必要的; 项目上线以后,将系统运行时出现的警告、错误等信息记录下来以备相关人员了解系统运行状况并维护代码也是很有必要的。 与此同时,采集日志数据也是为网站做数字化运营奠定一个基础,通过对系统运行日志的分析,我们可以监测网站的流量以及流量分布,同时还可以挖掘出用户的使用习惯和行为模式。
Although Django’s logging configuration works out of the box, you can control exactly how your logs are sent to various destinations - to log files, external services, email and so on - with some additional configuration. 虽然Django的日志配置是开箱即用的,但是你可以通过一些额外的配置来控制你的日志如何被发送到不同的目的地——日志文件、外部服务、电子邮件等等。
你可以配置:
logger mappings, to determine which records are sent to which handlers 记录器映射,以确定将哪些记录发送到哪些处理程序 handlers, to determine what they do with the records they receive 处理程序,以确定如何处理接收到的记录 filters, to provide additional control over the transfer of records, and even modify records in-place 过滤器,以提供对记录传输的额外控制,甚至就地修改记录 formatters, to convert LogRecord objects to a string or other form for consumption by human beings or another system 格式化程序,用于将LogRecord对象转换为字符串或其他形式以供人类或其他系统使用
日志配置说明1
在settings.py中配置:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
'simple': {
'format': '{levelname} {message}',
'style': '{',
},
},
'filters': {
'special': {
'()': 'project.logging.SpecialFilter',
'foo': 'bar',
},
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'INFO',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'filters': ['special']
},
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': '/path/to/django/debug.log',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'propagate': True,
},
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
},
'myproject.custom': {
'handlers': ['console', 'mail_admins'],
'level': 'INFO',
'filters': ['special']
}
}
}
该日志配置做了以下事情:
识别配置为 ‘dictConfig 版本 1’ 格式。目前,这是唯一的 dictConfig 格式版本。
定义两个格式化程序: simple,输出日志级别名称(如 DEBUG)和日志信息。 format 字符串是一个普通的 Python 格式化字符串,它描述了每个日志行要输出的细节。可以输出的完整细节列表可以在 Formatter Objects 中找到。 verbose,输出日志级别名称、日志信息,以及生成日志信息的时间、进程、线程和模块。
定义两个过滤器: project.logging.SpecialFilter,使用别名 special。如果这个过滤器需要额外的参数,它们可以作为过滤器配置字典中的附加键提供。在这种情况下,当实例化 SpecialFilter 时,参数 foo 将被赋予一个 bar 的值。 django.utils.log.RequireDebugTrue,当 DEBUG 为 True 时,传递记录。
定义两个处理程序: console,一个 StreamHandler,它将任何 INFO (或更高)消息打印到 sys.stderr。该处理程序使用 simple 输出格式。 mail_admins, an AdminEmailHandler, which emails any ERROR (or higher) message to the site ADMINS. This handler uses the special filter. ‘filename’ 指向的路径改为当前运行 Django 应用的用户可写的路径
配置三个记录器。 django,将所有信息传递给 console 处理程序。 django.request,它将所有 ERROR 消息传递给 mail_admins 处理程序。此外,这个记录器被标记为 不 传播消息。这意味着写给 django.request 的日志信息不会被 django 日志处理程序处理。 myproject.custom,它将所有 INFO 或更高等级的消息传递给两个处理程序——console 和 mail_admins。这意味着所有 INFO 级别(或更高)的消息将被打印到控制台;ERROR 和 CRITICAL 消息也将通过电子邮件输出。
日志配置说明2
在settings.py中配置:
LOGGING = {
'version': 1,
# 是否禁用已经存在的日志器
'disable_existing_loggers': False,
# 日志格式化器
'formatters': {
'simple': {
'format': '%(asctime)s %(module)s.%(funcName)s: %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S',
},
'verbose': {
'format': '%(asctime)s %(levelname)s [%(process)d-%(threadName)s] '
'%(module)s.%(funcName)s line %(lineno)d: %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S',
}
},
# 日志过滤器
'filters': {
# 只有在Django配置文件中DEBUG值为True时才起作用
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
# 日志处理器
'handlers': {
# 输出到控制台
'console': {
'class': 'logging.StreamHandler',
'level': 'DEBUG',
'filters': ['require_debug_true'],
'formatter': 'simple',
},
# 输出到文件(每周切割一次)
'file1': {
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': 'access.log',
'when': 'W0',
'backupCount': 12,
'formatter': 'simple',
'level': 'INFO',
},
# 输出到文件(每天切割一次)
'file2': {
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': 'error.log',
'when': 'D',
'backupCount': 31,
'formatter': 'verbose',
'level': 'WARNING',
},
},
# 日志器记录器
'loggers': {
'django': {
# 需要使用的日志处理器
'handlers': ['console', 'file1', 'file2'],
# 是否向上传播日志信息
'propagate': True,
# 日志级别(不一定是最终的日志级别)
'level': 'DEBUG',
},
}
}
大家可能已经注意到了,上面日志配置中的formatters
是日志格式化器,它代表了如何格式化输出日志,其中格式占位符分别表示:
%(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 (整数)
日志配置中的handlers用来指定日志处理器,简单的说就是指定将日志输出到控制台还是文件又或者是网络上的服务器,可用的处理器包括:
logging.StreamHandler(stream=None)
- 可以向类似与sys.stdout
或者sys.stderr
的任何文件对象输出信息logging.FileHandler(filename, mode='a', encoding=None, delay=False)
- 将日志消息写入文件logging.handlers.DatagramHandler(host, port)
- 使用UDP协议,将日志信息发送到指定主机和端口的网络主机上logging.handlers.HTTPHandler(host, url)
- 使用HTTP的GET或POST方法将日志消息上传到一台HTTP 服务器logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False)
- 将日志消息写入文件,如果文件的大小超出maxBytes
指定的值,那么将重新生成一个文件来记录日志logging.handlers.SocketHandler(host, port)
- 使用TCP协议,将日志信息发送到指定主机和端口的网络主机上logging.handlers.SMTPHandler(mailhost, fromaddr, toaddrs, subject, credentials=None, secure=None, timeout=1.0)
- 将日志输出到指定的邮件地址logging.MemoryHandler(capacity, flushLevel=ERROR, target=None, flushOnClose=True)
- 将日志输出到内存指定的缓冲区中
上面每个日志处理器都指定了一个名为level
的属性,它代表了日志的级别,不同的日志级别反映出日志中记录信息的严重性。Python中定义了六个级别的日志,按照从低到高的顺序依次是:NOTSET、DEBUG、INFO、WARNING、ERROR、CRITICAL。
最后配置的日志记录器是用来真正输出日志的,Django框架提供了如下所示的内置记录器:
django
- 在Django层次结构中的所有消息记录器django.request
- 与请求处理相关的日志消息。5xx响应被视为错误消息;4xx响应被视为为警告消息django.server
- 与通过runserver调用的服务器所接收的请求相关的日志消息。5xx响应被视为错误消息;4xx响应被记录为警告消息;其他一切都被记录为INFOdjango.template
- 与模板渲染相关的日志消息django.db.backends
- 有与数据库交互产生的日志消息,如果希望显示ORM框架执行的SQL语句,就可以使用该日志记录器。
日志记录器中配置的日志级别有可能不是最终的日志级别,因为还要参考日志处理器中配置的日志级别,取二者中级别较高者作为最终的日志级别。
配置Django-Debug-Toolbar
参考: https://github.com/jazzband/django-debug-toolbar https://blog.csdn.net/cn_1937/article/details/82715983 Django-Debug-Toolbar是项目开发阶段辅助调试和优化的必备工具,只要配置了它,就可以很方便的查看到如下表所示的项目运行信息,这些信息对调试项目和优化Web应用性能都是至关重要的。
项目 | 说明 |
---|---|
Versions | Django的版本 |
Time | 显示视图耗费的时间 |
Settings | 配置文件中设置的值 |
Headers | HTTP请求头和响应头的信息 |
Request | 和请求相关的各种变量及其信息 |
StaticFiles | 静态文件加载情况 |
Templates | 模板的相关信息 |
Cache | 缓存的使用情况 |
Signals | Django内置的信号信息 |
Logging | 被记录的日志信息 |
SQL | 向数据库发送的SQL语句及其执行时间 |
配置方法
- 安装Django-Debug-Toolbar。
pip install django-debug-toolbar
- 配置 - 修改settings.py。
if DEBUG: MIDDLEWARE += [ 'debug_toolbar.middleware.DebugToolbarMiddleware', ] INSTALLED_APPS += [ 'debug_toolbar', ] INTERNAL_IPS = ['127.0.0.1', ]
# this is the main reason for not showing up the toolbar import mimetypes mimetypes.add_type("application/javascript", ".js", True) DEBUG_TOOLBAR_CONFIG = { # 'INTERCEPT_REDIRECTS': False, # 引入jQuery库 如果项目组包含jquery,这里可以为 "" 'JQUERY_URL': 'https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js', # 工具栏是否折叠 'SHOW_COLLAPSED': True, # 是否显示工具栏 'SHOW_TOOLBAR_CALLBACK': lambda x: True, }</code></pre></div></div><ol class="ol-level-0"><li>配置 - 修改urls.py。</li></ol><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0"># 在urls.py文件最后面添加
from django.conf import settings
if settings.DEBUG:
import debug_toolbar
urlpatterns.insert(0, path('debug/', include(debug_toolbar.urls)))
- 在配置好Django-Debug-Toolbar之后,页面右侧会看到一个调试工具栏,如下图所示,上面包括了如前所述的各种调试信息,包括执行时间、项目设置、请求、SQL、静态资源、模板、缓存、信号等,查看起来非常的方便。
优化ORM-excel导出函数export_teachers_excel(request):
在配置了日志或Django-Debug-Toolbar之后,我们可以查看一下之前将老师数据导出成Excel报表的视图函数执行情况,这里我们关注的是ORM框架生成的SQL查询到底是什么样子的,
相信这里的结果会让你感到有一些意外。执行Teacher.objects.all()之后我们可以注意到,在控制台看到的或者通过Django-Debug-Toolbar输出的SQL是下面这样的:
这里的问题通常被称为“1+N查询”(有的地方也将其称之为“N+1查询”),原本获取老师的数据只需要一条SQL,但是由于老师关联了学科,当我们查询到N条老师的数据时,Django的ORM框架又向数据库发出了N条SQL去查询老师所属学科的信息。 在一条SQL中可以基于连接查询完成老师和学科的查询。 在使用Django的ORM框架时可以基于如下方法:
对于多对一关联(如投票应用中的老师和学科),我们可以使用QuerySet的用select_related()方法来加载关联对象; 而对于多对多关联(如电商网站中的订单和商品),我们可以使用prefetch_related()方法来加载关联对象。
在导出老师Excel报表的视图函数 def export_teachers_excel(request): 中,我们可以按照下面的方式优化代码。
# 查询所有老师的信息
# queryset = Teacher.objects.all() # 注释掉这句话,
queryset = Teacher.objects.all().select_related('subject') # 修改为这句话
优化ORM-excel生成报表函数def get_teachers_data(request):
事实上,用ECharts生成前端报表的视图函数中,查询老师好评和差评数据的操作也能够优化,因为在这个例子中,我们只需要获取老师的姓名、好评数和差评数这三项数据,但是在默认的情况生成的SQL会查询老师表的所有字段。可以用QuerySet的only()方法来指定需要查询的属性,也可以用QuerySet的defer()方法来指定暂时不需要查询的属性,这样生成的SQL会通过投影操作来指定需要查询的列,从而改善查询性能, 函数中:def get_teachers_data(request): 代码如下所示:
# queryset = Teacher.objects.all()
queryset = Teacher.objects.all().only('name', 'good_count', 'bad_count')
当然,如果要统计出每个学科的老师好评和差评的平均数,利用Django的ORM框架也能够做到,代码如下所示:
queryset = Teacher.objects.values('subject').annotate(good=Avg('good_count'), bad=Avg('bad_count'))
这里获得的QuerySet中的元素是字典对象,每个字典中有三组键值对,分别是代表学科编号的subject、代表好评数的good和代表差评数的bad。如果想要获得学科的名称而不是编号,可以按照如下所示的方式调整代码:
queryset = Teacher.objects.values('subject__name').annotate(good=Avg('good_count'), bad=Avg('bad_count'))
可见,Django的ORM框架允许我们用面向对象的方式完成关系数据库中的分组和聚合查询。
总结
本文主要是Django系列博客。本文是Django日志配置与Django-Debug-Toolbar配置。 日志配置:
1.配置settings.py中的LOGGING
Django-Debug-Toolbar配置:
1.安装依赖库 2.修改settings.py文件 3.修改urls.py文件 4.测试运行