- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
众所周知,日子作为最强的甩锅神器,合理合适的记录日志就是一个问题。今天我想总结下我对日志的看法 。
写这一行日志的时候思考下,生产出现异常的时候,这一行日志是否能提供有效的帮助去解决问题 。
这是最重要,也是最难做到的一个总结。在处理问题的时候,尤其是难以复现的问题,能协助处理问题的就只有日志文件,开发人员必须能从中了解到当时发生了什么! 。
当开发人员看到日志时,通常会根据日志推断问题发生时的上下文消息,不幸的时候日志本身并没有这个上下文,因此需要开发者手动将这些信息补充到日志中,如果不能也可加入操作的目的或者结果,便于理解.
比如 ERROR: save error - SQL Excepetion 这条日志,当你排查问题的时候价值几乎为0,除了知道时 SQL 执行出错外,没有任何信息。更好,更有用的信息应该是这样 ERROR: save error, data com.xxx.Cat(name = name, age=1) - SQL Excepetion ,其中解释了 Cat 是一个类,并且记录了对应示例的 相关 内容,注意是相关内容,而不是全部,无用的信息应该被去除,避免一些无用信息混淆日志文件.
英文可能没有中文读起来方便,但是英文依然是记录日志最好的语言,没有之一,原因如下:
ASCII
字符被记录,如果日志消息使用特殊字符集乃至 UTF-8
,当被阅读者打开的时候都有可能无法正确呈现。并且还可能存在用户的输入采用不同的字符集或者编码的问题 犹记得在我刚开始写代码的时候,为了记录日志信息使用了 System.out.println() ,尽管这个解决了问题,但是这是一个非常愚蠢的方式。缺点数不胜数。 尽管 java 生态种提供了诸如 Log4j 、 JCL 、 slf4j 和 logback 等诸多第三方日志框架,但请尽量使用 slf4j 的门面模式来记录,有利于维护和各个类的日志处理方式统一,并且可以在保证不修改代码的情况下,很方便的实现底层日志框架的更换.
这句话听起来似乎很矛盾,但我们确实应该平衡日志的数量。试想在凌晨3点,你要在漫天日志中寻找问题的根源,如果被大量日志混乱你的思路这并不是一件好事。但如果日志太少,找不到问题根源,这更是一个问题.
到底多少合适,没有一个准确的数字,有一个解决思路是,前期可以多打日志,功能上线后,对日志进行分析,并根据问题的减少而减少日志,或者补上缺失的日志 。
看这个标题,如果问我什么是合适的地方,我也不知道,但是如果从日志的用出来看,那将会有一些答案 。
API
开始与结束:众所周知, 日志可以作为一个甩锅利器 ,当对方告诉你他做了什么而你认为没有或者不对的时候,日志就是最有力的证据 API
开始与结束:这块就是自己系统的大门,谁来都得留下影子,别人说没给的时候,这里同样也是证据。另外在日志排错、 性能分析 、 链路追踪 方面很有帮助 SQL
查询的结果 java 生态种可以使用的日志框架都有日志级别的概念,常见的日志级别如下:
DEBUG:
主要输出调试性质的内容,该级别的日志主要用于在开发、测试阶段输出,该级别的日志应尽可能的详细,比如各类调试信息、输入输出信息等等。 INFO:
业务系统记录日志的关键信息。开发可以将初始化系统配置,业务状态变化信息、流程核心记录到这个几倍种,方便运维工作及错误回溯时上下问场景复现 WARN:
主要输出警告性质的内容,该内容是可以预知,并且有规划的,比如乐观锁更新失败、方法空参等等 ERROR:
主要针对一些不可预知的、对应用影响打的错误、异常信息。比如 catch
代码块抓取的数据通信异常、文件异常。在输出 ERROR
级别的日志时,尽量的多输入方法参数,方法过程中产生的对象、错误、异常对象数据。 不同的环境应当使用不同日志级别,这样可以过滤掉级别过低的消息我通常使用的配置如下: -生产:自己编写的代码的 Info 级别和第三方库的 warn 级别 -开发、测试:自己编写的代码的 debug 级别和第三方库的 warn 级别 。
在开发、测试期间,通常会打印很多的日志来记录应用过程中发生了什么,比如:
public void method(String str){
log.info("method start, str{}", str);
if("NO".equals(str)){
log.debug("the param is no");
// do other
}
}
这些日志的主要目的是通过显示的调用及内部的方法数值来跟踪程序的行为,但是当这些代码发布后, log.debug("the param is no"); 将没有什么意义。所以在开发完成后,在代码合并到发版分支之前应当删除不必要的日志信息 。
选取不合适日志字符串构造方式可能会导致额外的开销,比如下面的两行代码 。
log.debug(String.formt("name=%s, age=%s", "test", 1));
log.debug("name={}, age={}", "test", 1);
这两行代码的最后日志输出都是:
name=test, age=1
但是当日志输出级别为 INFO 的时候却有巨大的时效差距.
name=test, age=1
, 并传参给 log.debug()
函数,之后判断出日志输出级别为 INFO
,不再输出日志。 INFO
级别的日志输出,从而减少了构造字符串的时间。 关于 SLF4J 的构造方式可以参考https://www.slf4j.org/faq.html#logging_performance 。
我对日志的要求第一要义是解决生产问题,所以一定是要人能读懂的日志。但有时对于大量的日志,我们需要自动化脚本进行批处理,那么我们就需要程序能够理解.
假设有这样一行语句 。
log.info("User {} plays {} in game {}", userId, card, gameId);
他将产生这样的文本 。
2013-01-12 17:49:37,656 [T1] INFO c.d.g.UserRequest User 1334563 plays 4 of spades in game 23425656
如果想要进行解析,正则表达式可能会是这样(没有验证) /User (d+) plays (.+?) in game (d+)$/ 这样很容易出错,玩意用户就叫 play 怎么办呢?
我们可以将语句改成这样 。
log.info("User [{}] plays [{}] in game [{}]", userId, card, gameId);
所有的参数值都放在 [] 之间,这样就能更好的让程序分析代码 。
基于各国、各行业的法律法规,不要打印用户的敏感信息,例如:银行卡号、身份证号、手机号 。
参考文档: [1] The 5 Java logging rules [2] Logging Best Practices: The 13 You Should Know [3] logging_performance [4] 日志粒度总结 。
最后此篇关于日志打印的碎碎念总结的文章就讲到这里了,如果你想了解更多关于日志打印的碎碎念总结的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
有没有更好的方法用 PHP 将数据输出到 html 页面? 如果我想在 php 中用一些 var 制作一个 div,我会写类似的东西 print (''.$var.''); 或 echo "''.$v
我可以使用 java awt print 来打印文档/文件而不是使用 javax print 吗?我发现在 java awt print 中有一个选项可以使用 AttributedString 将内容
目前我通过以下方式运行 R 脚本: R --slave argument1 argument2 ... 我想知道 R 中关于如何退出脚本并发出警告的最佳实践,q() 会这样做吗? if(!file.
谁能告诉我如何编写一个程序,用 gcc 编译时打印 c ,用 g++ 编译时打印 c++? 最佳答案 #ifdef __cplusplus printf("c++\n"); #else
我需要支持在 KitKat 设备上打印,但我的目标 SDK 是 13(无法更改)。 特别是我需要打印一个 webview。 这是用于打印 webview 的 API: http://developer
我正在尝试创建一个简单的函数,其中 python 将根据您的年份输入计算年龄。我已经尝试了几种方法,但我没有运气 atm。 附:对不起,我是新手。 ame = input(" Enter your n
JavaFX 2.0 是否支持打印?我有一个文本区域,我从中获取文本然后我想打印它,但似乎没有这个功能。 当然,这里我说的是打印到打印机。 :) 最佳答案 尚不支持。作为一种解决方法,您可以使用 Ja
我试图找出printOn的重点。我查看了一些实现它的类,看起来它只是帮助打印不同数据类型的单位。这是准确的吗? 如果是这样,有人能指出我如何为我自己的类(class)实现这一点的正确方向吗?我将在可能
我无法让 IE 打印我的 Canvas (使用 excanvas 生成)...我使用的是最新版本的 excanvas。 http://dl.dropbox.com/u/997831/canvas.ht
我搜索了很多但没有人回答我的问题,我读到在这样的信号处理程序中使用 cout 是不安全的: void ctrlZHandler(int sig_num) { //SIGTSTP-18
我有兴趣打印一系列查询。我有以下代码。 start = datetime.datetime(2012, 2, 2, 6, 35, 6, 764) end = datetime.datetime(201
public class javaClass { public static void main(String [] arg) { String row1 = "A____A"
我需要写入前一行的命令,例如不带\n 的 print()。 下面是一些示例代码: a=0 print("Random string value") if a==0: print_to_prev
我有一个使用 UIKit 和 Objective C 的旧 iOS 应用程序,我目前正在将其移植到 SwiftUI 和 Swift。一切都很顺利,我喜欢 Swift 和 SwiftUI。该应用程序已经
我创建了一个求和函数,它接受一个开始编号和一个结束编号,并返回这两点之间的总和答案 def print_sum_equations(start_number,end_number):
在 Perl 6 中,print 和有什么区别? , put和 say ? 我怎么看 print 5不同,但 put 5和 say 5看起来一样。 最佳答案 put $a就像 print $a.Str
我正在使用 here 中的 getOrgChart 库,我正在尝试打印整个图表,而不仅仅是可见部分。不幸的是,当使用标准库打印功能时,它只会打印出第一部分,而我不知道如何打印整个图表(该图表相当宽,大
我制作了一个非常适合 A4 页面的 View 。现在我想打印它。请注意,我没有使用drawRect或类似的东西,只是一个带有 subview 和文本标签的普通 View 。我的问题是,我对该 View
由于 Cocoa-Java 已弃用,我正在将 Cocoa-Java 代码迁移到 Cocoa + JNI。该代码打印存储在文件中的图像。新的 Cocoa 代码基本上是: NSImage *image =
这个问题已经有答案了: Printing a TDBGrid (4 个回答) 已关闭 6 年前。 如何在不安装或下载组件的情况下打印 DBGrid? 或者 如何将 DBGrid 的数据放入 RichE
我是一名优秀的程序员,十分优秀!