一起读源码:为什么 loguru 的时间 rotation 不能只精确到天
摄影:产品经理
猪耳朵与鹌鹑蛋做的皮蛋
今天的问题来自未闻 Code 粉丝交流群:
“loguru 每天自动生成的日志名字,可以只精确到日吗? ”
如下图所示:
这里的每天自动生成日志的名字是什么意思呢?实际上指的就是rotation
。我不知道他的中文名叫什么,但rotation 表示一种按特定条件自动切分日志的方式。
如果我们把日志一直不停写入某个文件中,那么这个文件可能会有几十上百 GB,显然这样是没有办法查看和检索的。所以我们需要一些方式来切分日志。例如,每个日志文件只有1GB,超过1GB 后,自动生成新的日志;又或者每天0点自动生成新的日志。
今天的这个问题,实际上就是在日志按天切分的时候,自动生成的文件名太长,包含了时分秒。但是提问的同学只需要精确到天。
我们来做一个测试,安装 loguru 以后,大家编写如下代码:
import time
import datetime
from loguru import logger
logger.add('我是日志.log', rotation="18:16")
while True:
logger.info(f'现在时间:{datetime.datetime.now()}')
time.sleep(1)
这段代码每一秒中会写入一条日志。并且日志在18:16分的时候会进行一次切分。运行效果如下图所示:
loguru 每次写入文件的时候,写入的都是我是日志.log
。但是当 rotation 的时间到了以后,会先把之前的我是日志.log
文件改名为我是日志.%Y-%m-%d_%H-%M-%S_%f.log
。然后创建一个新的我是日志.log
提问题的同学想知道,由于他的日志是每天切换一次的,所以实际上时间没有必要精确到毫秒。是否能够只精确到天?
为了回答这个问题,我去看了 loguru 的官方文档,发现确实没有相关的内容提到日志被 rotation 以后,重命名的格式相关的设置。
既然文档没有提到,那么我们就寻求源代码的帮助。日志重命名时,新的文件名是由函数generate_rename_path()
生成的。大家可以在 Github 中,loguru 的_file_sink.py
文件中找到这个函数:https://github.com/Delgan/loguru/blob/master/loguru/_file_sink.py#L16 。
决定了文件名的代码如下图所示:
代码第17行,得到一个 datetime 对象creation_datetime
。这个对象作为参数传入FileDateFormatter
类,得到一个 date 对象。
接下来,在第20行,通过字符串的.format()
方法,拼接出一个新的文件名。
这就是全部过程了。是不是讲的太快了,没有 get 到点?那我们从头开始。
这里我们来复习一下字符串的.format()
方法:
a = '我是{}, 你是{}'
b = a.format('青南', 'pm')
print(b)
运行效果如下图所示:
这三行代码特别简单,似乎一个不会 Python 的人,看到代码以后就能脱口而出:这不就是把 format 里面的参数,按顺序替换原来字符串中的大括号吗。
是的,要让它运行起来是特别简单。但是,如果 format 的括号里面,有一个参数是一个含有__format__()
魔法方法的对象会怎么样?我们再来看一段代码:
class People:
def __init__(self, name):
self.name = name
def __format__(self, spec):
return '你好世界!'
a = '我是{}, 你是{}'
kingname = People('青南')
b = a.format(kingname, 'pm')
print(b)
可以看到,__format__()
方法返回的字符串,可以替换大括号里面的内容。此时,__format__()
的能实现的效果,跟__str__()
方法没有什么两样。
接下来,不一样的地方来了——在大括号里面,实际上还可以传递参数!只需要在大括号里面先加上一个英文冒号,再加上参数就可以了,我们来看看:
class People:
def __init__(self, name):
self.name = name
def __format__(self, spec):
if 'pm' in spec:
return '产品经理到此一游'
else:
return '青南'
a = '我是{:kingnma}, 你是{:pm}'
people = People('随便谁')
b = a.format(people, people)
print(b)
运行效果如下图所示:
可以看到,b = a.format(people, people)
我们传入的是同一个对象,但是由于大括号里面,冒号后面的参数不同,于是最终生成的字符串也就不一样。
__format__()
方法始终会接收一个spec
参数,如果我们原字符串的大括号里面没有冒号,那么这个spec
是空字符串。如果我们原字符串的大括号里面有冒号,那么冒号后面的内容就会作为 spec 参数传入__format__()
方法中。
原理讲完了。我们回到loguru 的源代码中。可以看到,FileDateFormatter
类的__format__()
方法会做一个判断,如果 spec
参数为空,那么就会强制使用%Y-%m-%d_%H-%M-%S_%f
这个格式的时间。
我们再来看generate_rename_path
代码第20和25行,renamed_path = "{}.{}{}".format(root, date, ext)
和renamed_path = "{}.{}.{}{}".format(root, date, counter, ext)
,其中date
对象作为format 的第二个参数传入第二个大括号。但是第二个大括号是空的,里面没有冒号,date
对象的__format__()
方法的 spec 参数始终为空。所以始终会使用%Y-%m-%d_%H-%M-%S_%f
格式。无法人工自定义格式。
综上所述,对这位同学的回答是:无法自定义日志 rotation 时添加的时间格式。如果你想要强行修改的话,那么你只有修改 loguru 的源代码,例如:
- Linux shell 程序设计1——安装及入门
- 偶遇--《坑新人--前端专用面试题》
- 简单的说下,(function(){...})() 与 (function(){...}()) 有什么区别?
- ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline[上篇]
- Shell常用命令小结
- 插入法排序
- ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline - Part II
- 震惊了!这样的js面试题让所有人-男默女泪
- 前端知识学了却不会用,都是没走心
- var a="xx";a=a+"ss";a的值变了,但"xx"字符串并没有变
- 先行者计划--1109微课总结 | 《通过二个demo初识webPack》
- 先行者计划--1107微课 《什么是Vuex?》| 文字简版
- 脱离前端菜鸟层次的二个关键点
- 【课堂笔记】先行者 3.0版本的vueJs课程的第三次课
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android中默认系统的声音/大小修改和配置详解
- Android开发中计算器的sin、cos及tan值计算问题分析
- Android开发实现绘制淘宝收益图折线效果示例
- Android自定义View实现搜索框(SearchView)功能
- android 监听SD卡文件变化的实现代码
- Android监听手机短信的示例代码
- Android开发之图片压缩工具类完整实例
- Android6.0开发中屏幕旋转原理与流程分析
- Android中WebView的基本配置与填坑记录大全
- Android开发实现ListView异步加载数据的方法详解
- Android开发实现AlertDialog中View的控件设置监听功能分析
- 详解Android 语音播报实现方案(无SDK)
- Android实现中轴旋转特效 Android制作别样的图片浏览器
- Android使用DrawerLayout实现双向侧滑菜单
- Android实现3D推拉门式滑动菜单源码解析