- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
本文首发于公众号:Hunter后端 原文链接: Django笔记三十之log日志的记录详解 。
这一节介绍在 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,
}
}
}
以下是本篇笔记全部内容:
在 Django 系统中,日志的记录也可以在 setting.py 中配置,key 为 logging,然后下面有几个主要的模块:
loggers、handlers、filters、formatters 。
系统接收到日志信息后,进入 logger,然后根据指定的 handler 列表发送到 handler 中 。
根据 handler 的处理方式,将信息写入文件、发送邮件或者其他方式 。
这些信息可以经过 filter 进行进一步的过滤,根据 formatter 的信息组织形式通过 handler 的处理方式进行处理 。
Loggers 是学习日志系统的一个切入点,每个 logger 都是一个命名的桶,处理的信息可以作为日志写入到 logger 里 。
每一个 logger 都可以被配置一个日志等级,日志等级描述了 logger 记录的信息的严重程度,python 定义了如下几种日志等级:
每个被写入 logger 的消息都被称为是一个 Log Record(日志记录).
每个日志记录在被发送到 logger 的时候都有一个日志等级来表示信息的严重程度 比如:
logger.info("xxx")
这些日志记录应该包含一些有用的、包含了问题产生原因的信息 。
当一条消息被发送到 logger,消息的等级会和 logger 的日志等级做一个比较,只有当消息的等级大于或等于 logger 的记录等级时,消息才会被当前 logger 进行更多的处理 。
如果这条消息被 logger 接收,那么它会被发送到 Handlers 。
我们可以理解 handler 是一个处理器,用来决定每天发送到 logger 的信息应该怎么处理,也就是日志的记录形式 。
比如说写入一个文件,发送邮件等 。
跟 Logger 一样,handler也有一个日志等级,只有当发送到 handler 的日志等级大于等于 handler 的日志记录时,handler 才会处理信息 。
一个 Logger 可以有多个 handler 处理器,每个 handler 都可以有自己的日志等级,因此可以根据信息的重要程度来决定不同的输出 。
比如你可以用一个 handler 把 ERROR 和 CRITICAL 等级的信息转发到服务页面,另一个 handler 记录所有的信息到一个文件,用作后续的分析 。
过滤器常被用来提供额外的控制,处理从 logger 到 handler 的日志记录 。
理论上来说,任何日志消息只要满足了日志等级的要求,都会被发送到 handler 处理,如果加了一个 filter 过滤器,你可以在日志处理上添加额外的标准 。
比如说你可以添加一个过滤器,只允许某个特定来源的 ERROR 等级的信息被处理 。
filter 也可以用来修改消息的严重等级,比如一些特定的条件被满足的情况下,你可以将ERROR等级的日志降级为 WARNING 。
在本篇笔记中,将不介绍 filter 的使用方法,因为能简单就简单一点,暂时不用那么多配置 。
格式化,一个日志记录需要被渲染成一个文本,formatter 提供了一些格式器的属性,格式化器由一些 LogRecord 的属性值组成,你也可以自己定义一个属性 。
当你配置了 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()
以下是对日志的记录流程汇总一下:
在笔记开篇的 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__)
在这里 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 仅能接收到用户自定义的消息 。
当一条消息从 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 的信息处理方式 。
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 表示每个日志文件最大的存储容量 。
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 。
这个配置表示是如果来了需要处理的日志消息就调用一个 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 表示调用的方法 。
这个配置用于发送邮件,如果日志消息发送到这个配置的 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("报错信息") 即可触发这个邮件发送的功能 。
这个配置也是用于日志发送邮件,但是是复用 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'],
] # 邮件接收地址
上面的参数都配置好之后也可以日志触发邮件了.
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 | 线程名称 |
之前我们设定的用户手动输入的日志被传送给了 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)
这样在对应的地方就可以实现专门的日志输出到专门的文件了.
接下来我们实现这样一个日志配置的功能:
以下是实现上面这个功能的 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笔记三十之log日志记录详解的文章就讲到这里了,如果你想了解更多关于Django笔记三十之log日志记录详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
不同的 LogCat 方法是: Log.v(); // Verbose Log.d(); // Debug Log.i(); // Info Log.w(); // Warning Log.e();
在android群里,经常会有人问我,android log是怎么用的,今天我就把从网上以及sdk里东拼西凑过来,让大家先一睹为快,希望对大家入门android log有一定的帮助. android
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 4 年前。 社区 12
我正在尝试使用 sonarlint 检查代码质量.上面的问题概要,我不明白为什么它要说要大写。但是 this discussion与上述建议相反。哪一个应该被认为是正确的? 最佳答案 这没有正确答案,
随着 n 变大,log*(log n) 和 log(log* n) 这两个函数会更快吗? 这里,log* 函数是迭代对数,定义如下: 我怀疑它们是相同的,只是写法不同,但它们之间有什么区别吗? 最佳答
作为家庭作业,我被要求在 O(log(n)) 中编写一个算法,我可以计算出我编写的算法的复杂度为 O(log(n) + log(n/2) + log(n/4) + log(n/8) + ... + l
我正在使用 Tomee。日志文件夹包含这样的文件 localhost_access_log.2016-12-02.txt localhost.2016-12-02.log catalina.2016-
Android Log.v、Log.d、Log.i、Log.e 等的 ios 等效项是什么?同样在 android 上,我使用 Android 设备监视器和 logcat 来访问我的手机日志,我需要在
我认为下面的代码是 O(log log n) 因为它里面有 i*i 但我对 log n 感到困惑> 和 log (log n)。 for (i=2; i*i<=number; i++) { if
我正在修改 kvm 模块,并在内核代码中添加了 printk 语句。运行虚拟机后,printk 为我提供了错误地址和有关 guest 操作系统的其他信息。 我需要从这个信息中生成统计信息。当我使用 d
我有一个部署为 Windows Azure Web 角色的 WCF 服务。 我正在使用 Enterprise Library 进行异常处理,并且在我的本地 Development Fabric 中,似
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 9 年前。 Improv
在 Go 的生产中使用 log.SetFlags(log.LstdFlags | log.Lshortfile) 是好的做法(至少是一般做法)吗?我想知道在生产中这样做是否存在性能或安全问题。因为它不
我想知道什么更快: double value = Math.log(a) - Math.log(b); 或 double value = Math.log(a/b); 我计算值的方式是否会对性能产生影
我有数百个子例程使用 log.Println() 写入日志文件 我正在使用 log.Println 写入 error.log 文件。 func main() { e, err := os.Open
我将 Nuxt 与 SSR 一起使用,并希望有类似于 apaches 的 access.log 和 error.log 的东西 我特别感兴趣的是每次调用的响应时间。 我在 nuxt 文档中找不到任何内
我知道以前有人问过这个问题,但我相信这是一个不同的问题。 Nginx 在 www-data 下运行: $ ps -eo "%U %G %a" | grep nginx root root
我在我的日志文件中发现了一个非常奇怪的条目 Jan 29 01:35:30 vs-proj-handy sshd[5316]: Received disconnect from 130.207.203
对于我正在开发的应用程序,我希望在开发过程中和发布时简化故障排除。我希望能够检索到对 Log 的调用,以了解在 USB 调试中没有连接手机的情况下运行应用程序时的调用,以便可以检索并发送给我。例如,当
我试图捕获 panic 并记录错误: func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloRep
我是一名优秀的程序员,十分优秀!