简介:一个好的Error Message次要蕴含三个局部:Context: 什么导致了谬误?产生谬误的时候代码想做什么?The error itself: 到底是什么导致了失败?具体的起因和过后的数据是什么?Mitigation: 有什么解决方案来克服这个谬误,也能够了解为 Solutions。听起来还是有点形象,是否给点代码具体阐明下?
作者 | 雷卷
起源 | 阿里技术公众号
一个好的Error Message次要蕴含三个局部:
听起来还是有点形象,是否给点代码? 刚好有一个 jdoctor 的我的项目,作者来自Oracle Labs[1] 样例代码如下:
ProblemBuilder.newBuilder(TestProblemId.ERROR1, StandardSeverity.ERROR, "Hawaiian pizza")
.withLongDescription("Pineapple on pizza would put your relationship with folks you respect at risk.")
.withShortDescription("pineapple on pizza isn't allowed")
.because("the Italian cuisine should be respected")
.documentedAt("https://www.bbc.co.uk/bitesize/articles/z2vftrd")
.addSolution(s -> s.withShortDescription("eat pineapple for desert"))
.addSolution(s -> s.withShortDescription("stop adding pineapple to pizza"));
这里的Problem了解为Error没有问题,外围次要包含以下几个字段:
有了这些具体的字段后,咱们了解起来就不便多啦。
各种错误处理上都倡议应用错误码,错误码有十分多的劣势:唯一性、搜寻/统计更不便等,所以咱们还是要讨论一下错误码的设计。网上也有不少错误码的设计规范,当然这篇文章也少不了反复造轮子,该设计提供给大家参考,大家自行判断啊,当然也十分欢送留言斧正。
一个错误码通常蕴含三个局部:
有了上述的标准后,让咱们看一下典型的谬误编码长什么样子:
咱们采纳利用名缩写, 组件名或者编码, 状态值,而后以中划线连接起来。中划线比拟不便浏览,下划线有时候在显示的时候了解为空格。同时有了规范的HTTP Status Code反对,不必参考文档,你都能猜一个八九不离十。 错误码设计千万不要太简单,试图将所有的信息都增加进去,当然信息十分全,然而也减少了开发者了解和应用老本,这个可能要做一个取舍,当然我也不是说目前这种一键三连(打赏、点赞加转发)的构造就最正当,你也能够自行调整。有没有做心里钻研的同学来说一下,这种三局部组成的形式,是不是最合乎人们的认知习惯?如果超过三局部,如4和5,人们能记住和应用的概率是不是就降落的十分多?
还记得后面说的error的context吗?这里error code其实就是启动context的作用,如 UIC-LOGIN-404,谬误产生在哪里?错误码帮你定位啦。过后代码想干什么?错误码也阐明啦。尽管说错误码不能齐全代表谬误的上下文,然而其承载的信息曾经足够咱们帮咱们理解过后的上下文啦,所以这里error code就是起着context的作用。目前看来至多error code要比 ProblemBuilder.newBuilder(TestProblemId.ERROR1, StandardSeverity.ERROR, “Hawaiian pizza”) 中的Hawaiian pizza 作为context更具备说服力,也标准一些。
错误码设计结束后,咱们还不能用错误码+简短音讯形式输入谬误,不然就呈现相似 ORA-00942: table or view does not exist这种状况,你肯定会吐槽:”你为何不通知哪个表或者view?”。所以咱们还须要设计一个message格局,可能将谬误的context, description, reason, document link, solutions全副蕴含进来,这样对开发者会比拟敌对。这里我拟定了一个Message的标准,当然大家能够发表本人的意见啊,如下:
long description(short desc): because/reason --- document link -- solutions
解释一下:
看一个具体的音讯格局例子:
APP-100-400=Failed to log in system with email and password(Email login failed): can not find account with email {} --- please refer https://example.com/login/byemail --- Solutions: 1. check your email 2. check your password
上述的APP-100-400的错误码对应的形容根本笼罩到jdoctor中须要的信息,能够说对一个谬误的形容应该十分全啦,而且有肯定的格局,也不便后续的日志剖析。
有了错误码和message的标准,接下来咱们应该如何保留这些信息呢?如果是Java,是不是要创立对应的ErrorEnum,而后是一些POJO?这里集体倡议应用properties文件来保留错误码和message的信息。文件名能够间接为ErrorMessages.properties,当然是在某一package下,文件样例如下:
### error messages for your App
APP-100-400=Failed to log in system with email and password(Email login failed): can not find account with email {0} --- please refer https://example.com/login/byemail --- Solutions: 1. check your email 2. check your password
APP-100-401=Failed to log in system with phone and pass(Phone login failed): can not find account with phone {0} --- please refer https://example.com/login/byphone --- Solutions: 1. check your phone 2. check your pass code in SMS
为何要抉择properties文件来保留error code和message信息,次要有以下几个起因:
最初最要害的是IDE反对十分敌对 , 以Java开发者应用的IntelliJ IDEA来说,对Properties文件的反对能够说是到了极致,如下:
总之IntellIJ IDEA对properties文件的反对到了极致,咱们也没有理由不思考开发者体验的问题,到处跳来跳去地找错误码,这种挫伤程序员开发体验的事件不能做。 当然JetBrains的其余IDE,WebStorm等都有对proproperties文件编辑反对。
看起来性能挺酷炫的,是不是这种形式谬误治理要染指一个开发包啊?不须要,你只须要10行代码就搞定,如下:
import org.slf4j.helpers.MessageFormatter;
public class AppErrorMessages {
private static final String BUNDLE_FQN = "app.ErrorMessages";
private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_FQN, new Locale("en", "US"));
public static String message(@PropertyKey(resourceBundle = BUNDLE_FQN) String key, Object... params) {
if (RESOURCE_BUNDLE.containsKey(key)) {
String value = RESOURCE_BUNDLE.getString(key);
final FormattingTuple tuple = MessageFormatter.arrayFormat(value, params);
return key + " - " + tuple.getMessage();
} else {
return MessageFormatter.arrayFormat(key, params).getMessage();
}
}
}
这样在任何中央如果你要打印谬误音讯的时候,这样log.info(AppErrorMessages.message(“APP-100-400″,”xxx”));就能够。如果你还有想法和log进行一下Wrapper,如 log.info(“APP-100-400″,”xxx”); ,也没有问题,样例代码如下:
public class ErrorCodeLogger implements Logger {
private Logger delegate;
private static final String BUNDLE_FQN = "app.ErrorMessages";
private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle(BUNDLE_FQN, new Locale("en", "US"));
public ErrorCodeLogger(Logger delegate) {
this.delegate = delegate;
}
@Override
public void trace(@PropertyKey(resourceBundle = BUNDLE_FQN) String msg) {
delegate.trace(RESOURCE_BUNDLE.getString(msg));
}
}
接下来你就能够在log中间接整合error code,十分便捷。上述代码我曾经写好,你参考文章开端的我的项目地址即可。
最终的日志输入如下:
揭示:这里咱们应用了slf4j的MessageFormatter,次要是不便后续的Slf4j的整合,而且slf4j的MessageFormatter比Java的MessageFormat容错和性能上更好一些。
1 为何抉择3位的HTTP Status Code作为Error的Status Code?
大多数开发者对HTTP Status Code都比拟相熟,所以看到这些code就大抵明确什么意思,当然对利用开发者也有严格的要求,你千万别将404解释为外部谬误,如数据库连贯失败这样的,逆失常思维的事件不要做。HTTP status code归类如下,当然你也能够参考一下 HTTP Status Codes Cheat Sheet[2]。
然而Error Status Code不局限在HTTP Status Code,你也能够参考SMTP, POP3等Status Code,此外你也自行能够抉择诸如007,777这样的编码,只有能解释的正当就能够啦。
在日常的生存中,咱们会应用一些非凡意义的数字或者和数字谐音,以下是一些情谊揭示:
这种有非凡意义的数字或者数字谐音,如520,886,999,95等,如果能应用的失当十分不便了解或更敌对,如透传给用户UIC-REG-200(注册胜利),如果调整为UIC-REG-520可能更舒适一些。总的来说应用这些数字要留神场景,当然比拟保险的做法就是参考HTTP,SMTP等设计的status code。
2 properties文件存储error code和message,真的比enum和POJO好吗?
就Java和IntelliJ IDEA的反对来看,目前的配合还是比拟好的,如i18n,保护老本等,而且这些ErrorMessages.properties也能够提交到核心仓库进行Error Code集中管理,如果是Java Enum+POJO对i18n和集中管理都比拟麻烦,而且代码量也比拟大,你从上述的jdoctor的problem builder的就能够看出。当然在不同的语言中也未必是相对的,如在Rust中,因为enum的个性比拟丰盛,所以在Rust下应用enum来实现error code可能是比拟好的抉择。
#[derive(Debug)]
enum ErrorMessages {
AppLogin404 {
email: String,
},
AppLogin405(String),
}
impl fmt::Display for ErrorMessages {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// extract enum name parameter
// output message from java-properties
write!(f, "{:?}", self)
}
}
3 为何不在Error Code中提供谬误级别
不少错误码设计中会增加谬误级别,如 RS-001-404-9 这样,最初一位示意谬误的重大级别。这样做没有问题,然而也要思考事实因素,如下:
如果将谬误的根本固化到error code中,这个后续你就没法调整啦,你如果调整了谬误级别,那就是可能就是另外一个错误码,给统计和了解都会造成问题。我集体是倡议错误码中不要包含重大级别这些信息,而是通过外围的文档和形容进行阐明,当然你也能够通过诸如 log.info , log.error来确定谬误的级别。
4 是否提供共享库?
因为IntelliJ IDEA并不反对动静的properties文件名称,如果你用动静的properties文件名称,就不能进行代码提醒,查找等性能也都不能应用,所以必须是这种 @PropertyKey(resourceBundle = BUNDLE_FQN) 动态的properties文件名形式。就一个Java类,你就受累Copy一下这个Java类,毕竟是一次性的工作,当然你想个性化调整代码也更不便,如和Log4j 2.x或自定也的logging框架整合也简略些。 日志是我的项目最根本的需要,所以你创立的我的项目的时候,就把Error Code对应的代码增加到我的项目模板中,这样我的项目创立后就主动蕴含logging和error code的性能。
5 其余的考量
原文和Reddit上相干的探讨也进行了一些整顿和阐明:
采纳error code + 基于properties文件存储error message,这个设计其实就是一个综合的取舍。如果IDEA不能很好地反对properties文件,你看到一个Error Code,不能间接定位到谬误的音讯,相同还须要跳转来跳转去找对应的音讯,那么Enum + POJO可能就是好的抉择。此外error code的设计也十分偏差http status code计划,这个也是次要基于大家对HTTP都十分相熟,基本上就能猜出大略的意思,相同随机编码的数字就没有这办法的劣势,要去error code核心再去查找一下,无形中也是节约开发人员的工夫。
原文链接
本文为阿里云原创内容,未经容许不得转载。