热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于java:什么是好的错误消息-讨论一下Java系统中的错误码设计

简介:一个好的ErrorMessage次要蕴含三个局部:Context:什么导致了谬误?产生谬误的时候代码想做什么?Theerroritself:到底是什么导致了失败?具体的起因和过后的数据是什么?Mitigation:有什么解决方案来克服这个谬误,也能够了解为Solutions。听起来还是有点形象,是否给点代码具体阐明下?

简介:一个好的Error Message次要蕴含三个局部:Context: 什么导致了谬误?产生谬误的时候代码想做什么?The error itself: 到底是什么导致了失败?具体的起因和过后的数据是什么?Mitigation: 有什么解决方案来克服这个谬误,也能够了解为 Solutions。听起来还是有点形象,是否给点代码具体阐明下?

作者 | 雷卷
起源 | 阿里技术公众号

一 什么是好的错误信息(Error Message)?

一个好的Error Message次要蕴含三个局部:

  • Context: 什么导致了谬误?产生谬误的时候代码想做什么?
  • The error itself: 到底是什么导致了失败?具体的起因和过后的数据是什么?
  • Mitigation: 有什么解决方案来克服这个谬误,也能够了解为 Solutions

听起来还是有点形象,是否给点代码? 刚好有一个 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没有问题,外围次要包含以下几个字段:

  • context: such as app name, component, status code,应用一个字符串形容过后的上下文,如利用名称 + 组件名称 +具体的谬误状态码等,这个由你本人决定,当然JSON字符串也能够,如 {“app”:”uic”, “component”: “login”, “code”:”111″}
  • description: Long(Short) to describe error 谬误形容,有Long和Short两者
  • because/reason: explain the reason with data 具体解释谬误的起因,当然必须蕴含相应的数据
  • documentedAt: error link 谬误对应的HTTP连贯,更具体地介绍该谬误
  • solutions: possible solutions 可能的解决方案,如提醒访问者查看email拼写是否正确,短信的Pass Code是否输出正确等。

有了这些具体的字段后,咱们了解起来就不便多啦。

二 错误码(Error Code)的设计

各种错误处理上都倡议应用错误码,错误码有十分多的劣势:唯一性、搜寻/统计更不便等,所以咱们还是要讨论一下错误码的设计。网上也有不少错误码的设计规范,当然这篇文章也少不了反复造轮子,该设计提供给大家参考,大家自行判断啊,当然也十分欢送留言斧正。

一个错误码通常蕴含三个局部:

  • System/App short name: 零碎或者利用的名称,如 RST, OSS等。如果你相熟Jira的话,根本也是这个标准,Java程序员应该都晓得HHH和SPR代表什么吧?
  • Component short name or code: 零碎外部的组件名称或者编码,如LOGIN, AUDIT,001 这些都能够,不便更快地定位谬误。
  • Status code: 谬误的状态码,这个是一个三位数字的状态码,如200,404,500,次要是借鉴自 HTTP Status Code,毕竟绝大多数开发者都理解HTTP状态码,咱们没有必要再从新设计。

有了上述的标准后,让咱们看一下典型的谬误编码长什么样子:

  • OSS-001-404: 你应该晓得是OSS的某一组件报告资源没有找到吧
  • RST-002-500:这个是一个组件的外部谬误
  • UIC-LOGIN-404:这个应该是会员登录时查找不到指定的账号

咱们采纳利用名缩写, 组件名或者编码, 状态值,而后以中划线连接起来。中划线比拟不便浏览,下划线有时候在显示的时候了解为空格。同时有了规范的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

解释一下:

  • 谬误的长形容间接书写,短形容应用括弧进行蕴含。这种写法在合同中十分常见,如阿里云计算有限公司(阿里云) ,你签订劳动合同时,公司的称呼根本也是全名(代称) 这种形式。好多同学会在谬误日志中书写登录失败,然而登录零碎中有多种登录形式,所以远不如Failed to log in with email and password(Login Failed), Failed to log in with phone and passcode(Login Failed), Failed to log in with oauth2(Login Failed) 更清晰。
  • 谬误具体起因: 接下来是冒号,而后书写具体的起因,如 email user@example.com not found ,gender field is not allowed in package.json 肯定要蕴含具体的数据信息,包含输出的,还是和劳动合同一样,低头之后就是你的具体岗位和薪水,尽管合同是格式化的,然而每一个人具体的岗位和薪水是不同的,这些参数都是从内部获取的。此处有平安同学提问,如何数据脱敏?这个是另外的问题,大多数开发者应该理解如何进行mask,这里咱们就跳过。当呈现劳动纠纷这个谬误时,具体起因中的数据,如岗位和薪水等,这样劳动仲裁局就能够疾速定位并解决该”谬误”。
  • document link: 接下来咱们应用三种划线—进行分隔,输出对应的error link。三划线作为分隔符在很多的场景中多有应用,如mdx, yaml等,大家不会太生疏。 如果没有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

有了错误码和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信息,次要有以下几个起因:

  • 国际化反对:Java的同学都晓得,如果你的谬误音讯想调整为中文,创立一个ErrorMessages-zh_CN.properties 即可。原文中的倡议是Don’t localize error messages,然而思考到国内大多数程序员未必能用英文表白分明,所以中文也是能够的。题外话:如果中国的程序员都能用英文清晰地阅读文章和表白本人的思维和观点,咱们在计算机方面的程度可能会晋升到更高的台阶。
  • 各种语言对properties的文件解析都有反对,不只是Java,其余语言也有,而且properties文件自身也不简单,所以该properties文件能够给Node.js, Rust等其余语言应用,如果是Java enum和POJO根本就不可能啦。
  • properties文件格式丰盛:反对正文,换行符,多行本义等也都没有问题。

最初最要害的是IDE反对十分敌对 , 以Java开发者应用的IntelliJ IDEA来说,对Properties文件的反对能够说是到了极致,如下:

  • error code的主动提醒

  • 疾速查看:鼠标移上去就能够,按下CMD鼠标移上去也能够, Alt+Space也能够,当然点击间接定位就更不用说啦。

  • 重构和查找反对:尽管Error Code是字符串,然而也是properties的key,所以rename这个error code,所有援用的中央都会rename。还反对find usage,那些中央援用了该error code等,都十分不便。当然如果Error Code在零碎中没有被应用,也会灰色标识。
  • 折叠主动显示性能:当你的代码处于折叠状态时,IDEA间接将message拿过去进行显示,你在code review的时候不便多啦,也便于你了解代码。

  • 间接批改message的值

总之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容错和性能上更好一些。

六 FAQ

1 为何抉择3位的HTTP Status Code作为Error的Status Code?

大多数开发者对HTTP Status Code都比拟相熟,所以看到这些code就大抵明确什么意思,当然对利用开发者也有严格的要求,你千万别将404解释为外部谬误,如数据库连贯失败这样的,逆失常思维的事件不要做。HTTP status code归类如下,当然你也能够参考一下 HTTP Status Codes Cheat Sheet[2]。

  • Informational responses (100–199)
  • Successful responses (200–299)
  • Redirection messages (300–399)
  • Client error responses (400–499)
  • Server error responses (500–599)

然而Error Status Code不局限在HTTP Status Code,你也能够参考SMTP, POP3等Status Code,此外你也自行能够抉择诸如007,777这样的编码,只有能解释的正当就能够啦。

在日常的生存中,咱们会应用一些非凡意义的数字或者和数字谐音,以下是一些情谊揭示:

  • UIC-LOGIN-666: 太顺利啦,完满登录。然而你团队中有欧美老外的话,他可能了解为了解为歹意登录,登录失败
  • APP-LOGIN-062: 如果你团队有杭州土著的话,不要应用62这个数字
  • APP-001-013: 如果该error code要透传给最终用户,请不要应用13这个数字,会引发不适

这种有非凡意义的数字或者数字谐音,如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 这样,最初一位示意谬误的重大级别。这样做没有问题,然而也要思考事实因素,如下:

  • 谬误的级别会动静调整的:如随着时空的变动,之前十分重大的谬误级别,当初并不那么重大啦。如果资源找不到可能之前十分重大,然而当初增加了备份计划,能够从备份服务器中再查找一次,所以这个谬误呈现在主服务上可能当初就不是那么重大啦。
  • 不同团队对谬误级别的认知不一样:如OSS-404在OSS团队的data server上找不到,元信息都是有的,后果在data server上没有找到对应的数据,这个是十分重大的谬误。雷卷在业务团队,如负责Serverless Jamstack,其中的一个文件缺失,如html, css, image,可能并不是一个大问题,等一会重试下,不行就再上传一下。我想表白的是同样的谬误,在不同团队中的重要性并不一样。

如果将谬误的根本固化到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上相干的探讨也进行了一些整顿和阐明:

  • 内外有别:如外部开发者的谬误中可能会包含服务器的具体信息,当然给最终消费者,如平台的FaaS开发者,可能就不能输入这样的信息,有肯定的平安危险。
  • 小心在谬误中裸露敏感数据:输入到谬误日志的数据肯定要进行mask,当然也不要影响你定位谬误,这个要看具体的场景。
  • 不要将谬误音讯作为 API 契约:在API的场景中,响应谬误有两种形式:依据错误码做响应,如REST API;另外一种是依据音讯做出响应,如GraphQL,所以这个你自行抉择。
  • Error Code的一致性:谬误音讯会输入给不同的消费者,如REST API,界面等,可能谬误的提醒音讯有所不同,如国际化、脱敏等,然而最好都是雷同的error code,也就是front end + backend 共享雷同的error code,不便定位谬误和统计。

七 总结

采纳error code + 基于properties文件存储error message,这个设计其实就是一个综合的取舍。如果IDEA不能很好地反对properties文件,你看到一个Error Code,不能间接定位到谬误的音讯,相同还须要跳转来跳转去找对应的音讯,那么Enum + POJO可能就是好的抉择。此外error code的设计也十分偏差http status code计划,这个也是次要基于大家对HTTP都十分相熟,基本上就能猜出大略的意思,相同随机编码的数字就没有这办法的劣势,要去error code核心再去查找一下,无形中也是节约开发人员的工夫。

原文链接
本文为阿里云原创内容,未经容许不得转载。


推荐阅读
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
  • 开发笔记:DevOps Gitlab环境部署
    本文由编程笔记#小编为大家整理,主要介绍了DevOpsGitlab环境部署相关的知识,希望对你有一定的参考价值。DevOps介绍 ... [详细]
  • 将单条消息的大小设置为大于服务端可以接受的消息大小,模拟发送异常的场景:消息大小1500message.max.bytes1000batch.size16384日志一直刷:[201 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文是一位90后程序员分享的职业发展经验,从年薪3w到30w的薪资增长过程。文章回顾了自己的青春时光,包括与朋友一起玩DOTA的回忆,并附上了一段纪念DOTA青春的视频链接。作者还提到了一些与程序员相关的名词和团队,如Pis、蛛丝马迹、B神、LGD、EHOME等。通过分享自己的经验,作者希望能够给其他程序员提供一些职业发展的思路和启示。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • 解决php错误信息不显示在浏览器上的方法
    本文介绍了解决php错误信息不显示在浏览器上的方法。作者发现php中的各种错误信息并不显示在浏览器上,而是需要在日志文件中查看。为了解决这个问题,作者提供了一种解决方式:通过修改php.ini文件中的display_errors参数为On,并重启服务。这样就可以在浏览器上直接显示php错误信息了。 ... [详细]
  • Java 11相对于Java 8,OptaPlanner性能提升有多大?
    本文通过基准测试比较了Java 11和Java 8对OptaPlanner的性能提升。测试结果表明,在相同的硬件环境下,Java 11相对于Java 8在垃圾回收方面表现更好,从而提升了OptaPlanner的性能。 ... [详细]
  • RMAN中的不完整恢复是指通过还原所有数据文件将整个数据库回退,然后执行不完全恢复的操作。不完整恢复的场景包括完整恢复不可行或故意要丢失数据。完整恢复需要备份后生成的所有归档日志和联机重做日志,而如果这些日志缺失或损坏,恢复将在该点停止。决定故意丢失数据是在用户错误发生后采取的行动,例如忘了where条件导致整个表受影响。对于已提交的事务来说,这样的更改是不可逆的。 ... [详细]
  • 本文介绍了在Linux下安装和配置Kafka的方法,包括安装JDK、下载和解压Kafka、配置Kafka的参数,以及配置Kafka的日志目录、服务器IP和日志存放路径等。同时还提供了单机配置部署的方法和zookeeper地址和端口的配置。通过实操成功的案例,帮助读者快速完成Kafka的安装和配置。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • HIVE 移动数据 (从表到分区表,分区表到子分区表)
    为什么80%的码农都做不了架构师?ExchangePartitionSkiptoendofmetadataCreatedbyNamitJain,lastmodi ... [详细]
author-avatar
梦之舞的微薄极_922
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有