阿里妹导读
打印日志是一门艺术,日志信息是开发人员排查线上问题最主要的手段之一,但规范打日志被开发同学经常所忽视。日志就像保险,平时正常的时候用不上,但是一旦出问题就都想看有没有保险可以用。一条良好的日志,是我们向外部证明的材料。
日志,维基百科中对其的定义是一个或多个由服务器自动创建和维护的日志文件,其中包含其所执行活动的列表。一个打印良好的日志文件可为开发人员提供精确的系统记录,可辅助开发人员定位到系统错误发生的详情及根源。在Java应用程序中,通常使用日志文件来记录应用程序运行过程中的重要逻辑参数及异常错误,辅之日志采集系统(ELK、DTM)构建系统监控体系。
上文中提到日志可以提供精准的系统记录方便根因分析,那为什么要记录日志,记录日志有哪些作用呢?打印调试:用日志来记录变量或者某一段逻辑,记录程序运行的流程,即程序运行了哪些代码,方便排查逻辑问题。问题定位:程序出异常或者出故障时快速的定位问题,方便后期解决问题。因为线上生产环境无法debug,在测试环境去模拟一套生产环境费时费力。所以依靠日志记录的信息定位问题,这点非常重要。监控告警 & 用户行为审计:格式化后日志可以通过相关监控系统(AntMonitor)配置多维度的监控视图,让我们可以掌握系统运行情况或者记录用户的操作行为并对日志采集分析,用于建设业务大盘使用。1.3 什么时候记录日志?
上文说了日志的重要性,那么什么时候需要记录日志。- 代码初始化时或进入逻辑入口时:系统或者服务的启动参数。核心模块或者组件初始化过程中往往依赖一些关键配置,根据参数不同会提供不一样的服务。务必在这里记录INFO日志,打印出参数以及启动完成态服务表述。
在我们日常开发中有四种比较常见的日志打印等级,不同的等级适合在不同的时机下打印日志。主要使用的有以下四个等级:
DEUBG级别的主要输出调试性质的内容,该级别日志主要用于在开发、测试阶段输出。该级别的日志应尽可能地详尽,开发人员可以将各类详细信息记录到DEBUG里,起到调试的作用,包括参数信息,调试细节信息,返回值信息等等,便于在开发、测试阶段出现问题或者异常时,对其进行分析。
INFO级别的主要记录系统关键信息,旨在保留系统正常工作期间关键运行指标,开发人员可以将初始化系统配置、业务状态变化信息,或者用户业务流程中的核心处理记录到INFO日志中,方便日常运维工作以及错误回溯时上下文场景复现。建议在项目完成后,在测试环境将日志级别调成INFO,然后通过INFO级别的信息看看是否能了解这个应用的运用情况,如果出现问题后是否这些日志能否提供有用的排查问题的信息。
WARN级别的主要输出警告性质的内容,这些内容是可以预知且是有规划的,比如,某个方法入参为空或者该参数的值不满足运行该方法的条件时。在WARN级别的时应输出较为详尽的信息,以便于事后对日志进行分析。
ERROR级别主要针对于一些不可预知的信息,诸如:错误、异常等,比如,在catch块中抓获的网络通信、数据库连接等异常,若异常对系统的整个流程影响不大,可以使用WARN级别日志输出。在输出ERROR级别的日志时,尽量多地输出方法入参数、方法执行过程中产生的对象等数据,在带有错误、异常对象的数据时,需要将该对象一并输出。如何选择WARN/ERROR当方法或者功能出现非正常逻辑执行情况时,需要打印WARN或者ERROR级别日志,那如何区分出现异常时打印WARN级别还是ERROR级别呢,我们可以从以下两个方面进行分析:
常见的WARN级别异常
常见的ERROR级别异常
不要滥用ERROR级别日志。一般来说在配置了告警的系统中,WARN级别一般不会告警,ERROR级别则会设置监控告警甚至电话报警,ERROR级别日志的出现意味着系统中发生了非常严重的问题,必须有人立即处理。
错误的使用ERROR级别日志,不区分问题的重要程度,只要是问题就采用ERROR级别日志,这是极其不负责任的表现,因为大部分系统中的告警配置都是根据单位时间内ERROR级别日志出现的数量来定的,随意打ERROR日志将会造成极大的告警噪音,造成重要问题遗漏。
摘要日志
摘要日志是格式化的标准日志文件,可用于监控系统进行监控配置和离线日志分析的日志,通常系统对外提供的服务以及集成的第三方服务都需要打印对应的服务摘要日志,摘要日志格式一般需包含以下几类关键信息:
2022-12-12 06:05:05,129 [0b26053315407142451016402xxxxx 0.3 - /// - ] INFO [SofaBizProcessor-4-thread-333] - [(interfaceName,methodName,1ms,Y,SUCCESS)(appName,ip地址,时间戳,Y)
详细日志详细日志是用于补充摘要日志中的一些业务参数的日志文件,用于问题排查。详细日志一般包含以下几类信息:
2022-12-12 06:05:05,129 [0b26053315407142451016402xxxxx 0.3 - /// - ] INFO [SofaBizProcessor-4-thread-333] - [(interfaceName,methodName,1ms,Y,SUCCESS)(appName,ip地址,时间戳,Y)(参数1,参数2)(xxxx)
业务执行日志
业务执行日志就是系统执行过程中输出的日志,一般没有特定格式,是开发人员用于跟踪代码执行逻辑而打印的日志,个人看来在摘要日志、详细日志、错误日志齐全的情况下,需要打印系统执行日志的地方比较少。
如果一定要打印业务执行日志,需要关注以下几个点:- 这个日志是否一定要打印?如果不打印是否会影响后续问题排查,如果打印这个日志后续输出频率是否会太高,造成线上日志打印过多。
建议格式:[日志场景][日志含义]带业务参数的具体信息
[scene_bind_feature][feature_exists]功能已经存在[tagSource='MIF_TAG',tagValue='123']
1. 打印日志的代码不允许失败,阻断流程!
一定要确保不会因为日志打印语句抛出异常造成业务流程中断,如下图所示,shop为null的会导致抛出NPE。
public void doSth(){
log.info("do sth and print log: {}", shop.getId());
// 业务逻辑
...
}
2. 禁止使用System.out.println()输出日志
反例:
public void doSth(){
System.out.println("doSth...");
// 业务逻辑
...
}
分析:
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
正例:
在日常开发或者调试的过程中,尽量使用标准日志记录系统log4j2或者logback(但不要直接使用其中的API),异步的进行日志统一收集。
public void doSth(){
log.info("doSth...");
// 业务逻辑
...
}
3. 禁止直接使用日志系统(Log4j、Logback)中的API
应用中不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架 (SLF4J、JCL--Jakarta Commons Logging)中的API。分析:直接使用Log4j或者Logback中的API会导致系统代码实现强耦合日志系统,后续需要切换日志实现时会产生比较大的改造成本,统一使用SLF4J或者JCL等日志框架的API,其是使用门面模式的日志框架,可以做到解耦具体日志实现的作用,有利于后续维护和保证各个类的日志处理方式统一。
正例:
// 使用 SLF4J:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(xxx.class);
// 使用 JCL:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
private static final Log log = LogFactory.getLog(xxx.class);
4. 声明日志工具对象Logger应声明为private static final
分析:
正例:
private static final Logger LOGGER = LoggerFactory.getLogger(xxx.class);
5. 对于trace/debug/info级别的日志输出,必须进行日志级别的开关判断
反例:
public void doSth(){
String name = "xxx";
logger.trace("print debug log" + name);
logger.debug("print debug log" + name);
logger.info("print info log" + name);
// 业务逻辑
//...
}
分析:
如果配置的日志级别是warn的话,上述日志不会打印,但是会执行字符串拼接操作,如果name是对象, 还会执行toString()方法,浪费了系统资源,执行了上述操作,最终日志却没有打印,因此建议加日志开关判断。正例:在trace、debug、info级别日志打印前加上对应级别的日志开关判断,通常可以将开关判断逻辑包装在日志工具类中,统一实现。
public void doSth(){
if (logger.isTraceEnabled()) {
logger.trace("print trace log {}", name);
}
if (logger.isDebugEnabled()) {
logger.debug("print debug log {}", name);
}
if (logger.isInfoEnabled()) {
logger.info("print info log {}", name;
}
// 业务逻辑
...
}
6. 捕获异常后不要使用e.printStackTrace()打印日志反例:-
public void doSth(){
try{
// 业务逻辑
...
} catch (Exception e){
e.printStackTrace();
}
}
分析:
正例:
public void doSth(){
try{
// 业务逻辑
...
} catch (Exception e){
log.error("execute failed", e);
}
}
7. 打印异常日志一定要输出全部错误信息
反例:
public void doSth(){
try{
// 业务逻辑
...
} catch (Exception e){
log.error("execute failed");
}
}
public void doSth(){
try{
// 业务逻辑
...
} catch (Exception e){
log.error("execute failed", e.getMessage());
}
}
正例:
一般日志框架中的warn、error级别均有存在传递Throwable异常类型的API,可以直接将抛出的异常传入日志API中。
void error(String var1, Throwable var2);
public void doSth(){
try{
// 业务逻辑
...
} catch (Exception e){
log.error("execute failed", e);
}
}
8. 日志打印时禁止直接用JSON工具将对象转换成String
反例:
public void doSth(){
log.info("do sth and print log, data={}", JSON.toJSONString(data));
// 业务逻辑
...
}
分析:
正例:可以使用对象的toString()方法打印对象信息,如果代码中没有对toString()有定制化逻辑的话,可以使用apache的ToStringBulider工具。-
public void doSth(){
log.info("do sth and print log, data={}", data.toString());
log.info("do sth and print log, data={}", ToStringBuilder.reflectionToString(data, ToStringStyle.SHORT_PREFIX_STYLE));
}
9. 不要打印无意义(无业务上下文、无关联日志链路id)的日志反例:- 不带任何业务信息的日志,对排查故障毫无意义。
public void doSth(){ log.info("do sth and print log"); // 业务逻辑 ...}
public void doSth(){ doIt1(); log.info("do sth 111"); doIt2(); log.info("do sth 222");}
正例:
public void doSth(){ log.info("do sth and print log, id={}", id); // 业务逻辑 ...}
10. 不要在循环中打印INFO级别日志
反例:
public void doSth(){ for(String s : strList) { log.info("do sth and print log: {}", s); // 业务逻辑 ... }}
11. 不要打印重复的日志
反例:
public void doSth(String s){ log.info("do sth and print log: {}", s); doStep(s);}private void doStep(String s){ log.info("do sth and print log: {}", s); // 业务逻辑 ...}
public void doSth(String s) {
try {
doStep(s);
} catch (Exception e){
log.error("something wrong", e);
}
}
private void doStep(String s){
try {
// 业务逻辑
} catch (Exception e){
log.error("something wrong", e);
throw e;
}
}
分析:
正例:直接干掉或者将日志降级成debug级别日志
12. 避免敏感信息输出
13. 日志单行大小必须不超过200K
1. 日志语言尽量使用英文
建议:尽量在打印日志时输出英文,防止中文编码与终端不一致导致打印出现乱码的情况,对故障定位和排查存在一定的干扰。
2. 重要方法可以记录调用日志
建议在重要方法入口记录方法调用日志,出口打印出参,对于排查问题会有很大的帮助。
public String doSth(String id, String type){
log.info("start: {}, {}", id, type);
String res = process(id, type);
log.info("end: {}, {}, {}", id, type, res};
}
3. 在核心业务逻辑中遇到if...else等条件,尽量每个分支首行都打印日志
在编写核心业务逻辑代码时,如遇到if...else...或者switch这样的条件,可以在分支的首行就打印日志,这样排查问题时,就可以通过日志,确定进入了哪个分支,代码逻辑更清晰,也更方便排查问题了。
建议:
public void doSth(){
if(user.isVip()){
log.info("该用户是会员,Id:{},开始处理会员逻辑",user,getUserId());
//会员逻辑
}else{
log.info("该用户是非会员,Id:{},开始处理非会员逻辑",user,getUserId())
//非会员逻辑
}
}
4. 建议只打印必要的参数,不要整个对象打印
反例:
public void doSth(){
log.info("print log, data={}", data.toString());
// 业务逻辑
...
}
分析:
首先分析下自己是否必须把所有对象里的字段打印出来?如果对象中有50个字段,但只需其中两个参数就可以定位具体的原因,那么全量打印字段将浪费内容空间且因为字段过多,影响根因排查。
正例:
public void doSth(){
log.info("print log, id={}, type={}", data.getId(), data.getType());
// 业务逻辑 ...
}
使用这个种方法需及时防止npe,并考虑是否核心场景,核心场景建议还是打全,避免漏打、少打影响线上问题定位&排查。
本文由微信公众号阿里云开发者原创,哈喽比特收录。
文章来源:https://mp.weixin.qq.com/s/V-TIT1Cw5fH8xSYAEMyukQ
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。