更多日志!结构化日志记录和异常处理
mo-logs的Python项目详细描述
更多日志-结构化日志记录和异常处理
<表><广告>此库提供两个主要功能
- 结构化日志记录-输出全部为json(带有序列化为人类文本的选项)
- 编织在中的异常处理-良好的日志必须表示发生了什么, 如果日志库不熟悉 采取的(异常)代码路径。
动机
异常处理和日志记录是不可否认的联系在一起的。有很多例子 出现异常且必须记录的地方,以及包含 系统可以完全处理异常,不应发出日志。例外 处理语义很好,因为它们将原因与解决方案分离, 但这可能与清洁伐木不符,清洁伐木是夫妻饲养和捕获的 对要向日志中发送的内容做出适当的决定。
这个日志模块还负责引发异常, 收集跟踪和上下文,然后推断它是否必须被记录,或者 如果它可以被忽略,因为有些东西可以处理它。
基本用法
使用log.note()
进行所有日志记录
Log.note("Hello, World!")
不需要创建记录器对象。日志
模块将跟踪
每次通话的时间、地点和地点。
使用命名参数
所有日志记录调用都接受带有命名参数的字符串模板。关键字参数 可以添加到调用中以提供值。模板和参数不是 它们在调用时组合在一起,而不是保存在 结构化日志记录。只有当日志为人类序列化时,模板才会展开。
Log.note("Hello, {{name}}!",name="World!")
不要使用python的字符串格式功能:
- 字符串格式化运算符(
%
), - 函数
format()
- 文字字符串组合
使用其中任何一个都将在调用时展开字符串模板,这是一个解析 日志分析工具的噩梦。
参数参数
所有的日志
函数都接受一个默认参数
作为第二个参数,如下所示:
Log.note("Hello, {{name}}!",{"name":"World!"})
这是针对您的代码已经具有捆绑结构的情况 希望用作参数源。如果使用关键字参数,则 将覆盖默认值。发送全部数据时要小心 结构,它们将被记录!
格式化参数
有多种格式化程序,可以使用e
管道()符号。
在本例中,我们将名称
转换为大写
Log.note("Hello, {{name|upper}}!",name="World!")
一些格式化程序接受参数:
Log.note("pi is {{pi|round(places=3)}}!",pi=3.14159265)
您可以查看字符串
模块以查看可用的格式化程序。
请不要使用locals()
defworker(value):name="tout le monde!"password="123"Log.note("Hello, {{name}}",locals())# DO NOT DO THIS!
尽管使用locals()
是一个很好的日志记录快捷方式
很危险,因为它还会拾取敏感的局部变量。即使
{{name}
是模板中的唯一值,整个locals()
dict将
发送到结构化记录器进行记录。
异常处理
所有日志都是结构化日志;参数将包括在 原木结构。此库还希望所有参数值都是json- 可序列化,以便下游json工具可以存储/处理它们。
{//EXAMPLE STRUCTURED LOG"template":"Hello, {{name}}!","param":{"name":"World!"},"timestamp":1429721745,"thread":{"name":"Main thread"},"location":{"line":3,"file":"hello.py","method":"hello"},"machine":{"python":"CPython","os":"Windows10","name":"ekyle-win"}}
使用log.error()
Log.error("This will throw an error")
实际调用将始终引发异常,并操纵堆栈
跟踪以确保来电者受到适当指责。请随意使用
raise
关键字(如raise log.error("")
中所示),如果您觉得这样更好的话。
始终链接例外情况
cause
参数接受异常或异常列表。
链接通常是一种很好的做法,可以帮助您找到
失败。
try:# Do something that might raise exceptionexceptExceptionase:Log.error("Describe what you were trying to do",cause=e)
在错误描述中也使用命名参数
错误日志记录接受关键字参数,就像log.note()
那样
Log.note("Hello, World!")0
无需正式键入异常
异常可以由第一个参数字符串模板唯一标识 它是给定的;使用同一模板引发的异常是同一类型的。你 不需要创建新的异常类型。
测试异常"类型"
这个库提倡尽早并且经常地链接异常,这就隐藏了
长因果链中的重要异常类型。MO日志
允许您轻松地
通过使用
中的关键字:
Log.note("Hello, World!")1
对于那些讨厌使用魔弦的人,可以随意使用常量:
Log.note("Hello, World!")2
如果可以处理异常,则永远不会记录它
当调用方从被调用方捕获异常时,它是调用方的 处理或重新提出异常的责任。有很多 调用方可以处理异常的情况;在这种情况下 记录错误是骗人的。
Log.note("Hello, World!")3
如果您的代码可以处理异常,但您仍然希望将其记录为问题,请使用log.warning()
Log.note("Hello, World!")
4
不要散开你的堆迹!
注意您的except
子句也可以抛出异常:在
捕捉一个普通的python异常,可能会丢失它的堆栈跟踪。
要防止这种情况,请将异常包装在except
对象中,该对象将捕获
你的踪迹供以后使用。从这个日志
库引发的异常不需要
因为他们已经捕捉到了他们的踪迹。如果包装
除外
对象,只需返回传递的对象。
Log.note("Hello, World!")5
始终捕获所有异常
捕获所有异常优先于仅捕获您可以处理的异常 策略。首先,异常不会因为我们在链接而丢失。第二, 我们很早就捕捉到意外的异常,并用 当地法规打算做什么的描述。此批注 有效地将可能的错误(已知或未知)分组到一个类中,该类 可供呼叫者决定适当的缓解措施。
重复:当使用依赖注入时,调用方不能合理地 希望认识abo但是可能发生的故障类型 调用链。这使得方法总结所有 异常,包括已知的和未知的,因此它们的调用方拥有 就适当的行动做出更好的决定。
例如:一个抽象文档容器,在sql之上实现 数据库,不应发出任何类型的SqlException: 文档容器不需要知道如何处理SqlException(或任何 其他特定于实现的例外情况)。相反,在本例中, 应告知调用方"无法添加文档"或"无法删除 文件"。这样一来,当来电者做出合理的决定时 发生。原始原因(SqlException)在因果链中。
另一个例子涉及嵌套异常:如果捕获特定类型 对于异常,您可能会无意中捕获相同类型的异常 从呼叫链的深处。狭义异常处理是一种错觉。 广泛的异常处理将迫使您考虑各种失败 早期;迫使您考虑当一个代码块失败时它意味着什么;以及 强迫你为别人描述它。
不要制定不需要的方法
有一个论点建议你应该把代码分成方法, 而不是捕获异常:方法名将描述 失败,可以检查堆栈跟踪以作出缓解决策。 但这是一个糟糕的解决方案:
- 更多的方法意味着更复杂;程序员必须找到方法, 记住这个方法,并想知道它是否在其他地方使用。
- 方法可以在重构时被删除;异常可以清楚地表明 错误很重要
- 编译器优化可能会干扰调用堆栈
- 方法名可能很长,无法描述问题
- 检查堆栈跟踪会导致代码混乱。
- 堆栈跟踪不包括异常可以 捕获。
记录"级别"
mo logs
模块没有日志级别的概念,预计调试
变量(以debug为前缀的变量)用于控制日志记录
输出:
Log.note("Hello, World!")6
这些调试变量可以通过配置文件设置:
Log.note("Hello, World!")7
日志配置
默认情况下,日志
模块将登录到控制台。日志开始(设置)
将日志重定向到其他流,如设置所定义的:
- 日志-所有日志流及其参数的列表
- 跟踪-在每个日志行中显示更多详细信息(默认为false)
- cprofile-用于启用内置python c-profiler(默认为false)
- 配置文件-用于启用pylibrary的简单配置文件(默认为false) (例如使用profiler("某些说明"):)
- 常量-将模块常量的绝对路径映射到将 被分配。主要用于设置模块中的调试常数。
当然,日志记录应该是设置的第一件事(除了消化 当然是设置)。因此,应用程序应具有以下内容 结构:
Log.note("Hello, World!")8
Log.note("Hello, World!")9
python日志记录的问题
python的默认日志记录
模块
接近做正确的事情,但失败了:
- 它有关键字参数,但它们在调用时被扩展,因此值将丢失在字符串中。
- 它有
额外的
参数,但如果不被匹配的格式化程序使用,这些参数将丢失 - 它甚至还具有带有
exc_info
参数的堆栈跟踪,但只有在处理异常时才有效。 - python 2.x没有内置的异常链,但是python 3有>< >< > >