A good Error Message consists of three parts: Context: What caused the Error? What does the code want to do when an error occurs? The error itself: What causes failure? What were the specific reasons and the data at the time? The department is calling for Mitigation. What is the solution to the problem? That sounds a little abstract, but can you explain some of the code?

The author | ray volume source | ali technology to the public

What is a good Error Message?

A good Error Message consists of three parts:

  • Context: What caused the error? What does the code want to do when an error occurs?
  • The error itself: What causes failure? What were the specific reasons and the data at the time?
  • The Mitigation department: What is the solution to this problem

Sounds a little abstract, can you give me some code? There happens to be a JDoctor project written by Oracle Labs[1] with the following sample code:

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"));Copy the code

The Problem here is understood as Error, and the core includes the following fields:

  • context: Such as app name, component, status code, use a string to describe the context, such as app name + component name + specific error status code, this is up to you, of course JSON string can also be used. Such as {” app “:” uic “, “component” : “login”, “code” : “111”}
  • Description: Long(Short) to describe error
  • Because /reason: Explain the reason with data
  • DocumentedAt: Error Link The HTTP connection corresponding to the error, which is described in more detail
  • Prompt the visitor to check whether the spelling of email is correct or whether the Pass Code of SMS is entered correctly.

Now that we have these specific fields, it’s much easier to understand.

The design of Error Code

Error codes are recommended for all kinds of error handling. Error codes have many advantages: uniqueness, ease of search/statistics, etc., so we’ll discuss error code design. There are also a lot of error code design specifications on the Internet, of course, this article also not repeat the wheel, the design for your reference, you judge ah, of course, also very welcome comments.

An error code usually consists of three parts:

  • System/App Short Name: indicates the name of the System or application, such as RST or OSS. If you’re familiar with Jira, which is basically the same specification, Java programmers probably know what HHH and SPR stand for.
  • Component short name or code: Internal Component name or code, such as LOGIN, AUDIT, and 001, can be used to locate errors faster.
  • This is a three-digit Status code, such as 200,404,500, mainly borrowed from the HTTP Status code, after all, most developers know the HTTP Status code, we do not need to redesign.

With this specification in mind, let’s take a look at what a typical error code looks like:

  • OSS-001-404: You are aware that a component of OSS reported resource was not found
  • Rst-002-500: This is an internal error for a component
  • Uic-login-404: This should be a member LOGIN failed to find the specified account

We take the application name abbreviation, component name or code, state value, and then underline it. The underscore is easy to read, and the underscore is sometimes displayed as a space. And with standard HTTP Status Code support, you’ll be able to guess almost anything without reference documentation. Error code design must not be too complicated, try to add all the information, information is complete, of course, but also increase the cost of the developers to understand and use, this may need to make a choice, of course, I also not to say that the current three even a key (exceptional, thumb up and forward) structure is the most reasonable, you can also adjust itself. Are there any students who do psychological research to tell us whether this three-part method is the most consistent with people’s cognitive habits? If there are more than three parts, such as four and five, does the probability of people remembering and using them drop dramatically?

Remember the error context? So the error code is actually used to start the context, like uIC-login-404, where did the error occur? The error code will help you locate it. What was the code trying to do? The error code says so. Although the error code cannot completely represent the context of the error, it carries enough information to help us understand the context, so the error code is used as the context. For now at least. Error code than ProblemBuilder newBuilder (TestProblemId ERROR1, StandardSeverity. The error, The Hawaiian pizza in “Hawaiian Pizza “is more convincing and normative as context.

Error message format

Ora-00942: Table or View does not exist orA-00942: Table or view does not exist, orA-00942: Table or view does not exist . Therefore, we also need to design a message format that can include all the wrong context, description, reason, document link, and solutions, which will be more friendly to developers. Here I have drawn up a Message specification, of course you can express your opinion, as follows:

long description(short desc): because/reason --- document link -- solutions
Copy the code

Explain:

  • Incorrect long descriptions are written directly, short descriptions are contained in parentheses. This writing method is very common in the contract, such as Ali Cloud Computing Co., LTD. (Ali Cloud), when you sign the labor contract, the company’s title is basically the full name (name) this way. Many students will write a failed login in the error log, but there are multiple login methods in the login system, So 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)
  • Specific reasons for the error: Follow with a colon and write a detailed reason, such as email [email protected] not found, gender field is not allowed in package.json Be sure to include specific data information, including the input, Or the same as the labor contract, after the rise is your specific post and salary, although the contract is formatted, but each person’s specific post and salary is different, these parameters are obtained from the outside. Here are security students to ask, how data desensitization? This is a separate issue, and most developers should know how to mask, so we’ll skip it here. When there is a labor dispute this error, the specific cause of the data, such as position and salary, so that the labor arbitration bureau can quickly locate and solve the “error”.
  • Document Link: Next we use three lines to separate and enter the corresponding error link. The three-line separator is used in many scenarios, such as MDX and YAML. If there is no link, ignore it.
  • The solution: natural text can be expressed, can explain clearly, also put in the third line after.

Take a look at a concrete message format example:

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
Copy the code

The description of the error code of app-100-400 above basically covers the information needed in jDoctor, so it can be said that the description of an error should be very complete, and there is a certain format, which is also convenient for subsequent log analysis.

Assemble and save error code + Message

Given the error code and message specification, how do we store this information? If it’s Java, do you create the corresponding ErrorEnum, followed by some POJOs? I recommend using the properties file to store error codes and message information. The file name can be directly errormessages. properties, of course, under a package, for example:

### 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
Copy the code

There are several reasons why you chose the properties file to hold error code and message messages:

  • Internationalization support: Java students know that if you want to change your error message to Chinese, create an errormessages-zh_cn.properties. Don’t localize error messages, but consider that most programmers in China may not be able to express them clearly in English, so Chinese is also ok. Off-topic: If Chinese programmers could clearly read articles and express their thoughts and ideas in English, our computer skills would be promoted to a higher level.
  • Properties file parsing is supported in a variety of languages, not just Java, but other languages, and the properties file itself is not complex, so the properties file can be used by other languages such as Node.js, Rust, etc. This is almost impossible with Java enUms and POJOs.
  • Properties file format rich: support comments, line breaks, multi-line escapes, etc.

Last but not least, IDE support is very friendly. IntelliJ IDEA, used by Java developers, supports Properties files to the extreme, as follows:

  • Error code is automatically displayed

  • Quick view: Mouse up can, press CMD mouse up can, Alt+Space can also, of course, click on direct positioning is not to mention.

  • Refactoring and lookup support: Error Code is a string, but it is also the key of the properties, so rename this Error Code, all references will be rename. It also supports find Usage, where the error code is referenced and so on, all very handy. Of course, if the Error Code is not used in the system, it will also be gray.
  • Folding automatic display function: When your code is folded, IDEA directly takes the message to display, which is much more convenient for you to review and understand the code.

  • Modify the value of message directly

In short, IntellIJ IDEA supports properties files to the extreme, and there is no reason for us not to consider the problem of developer experience, jumping around to find error codes, which harms programmer development experience. Of course JetBrains’ other ides, WebStorm, etc. have support for proProperties file editing.

Five Code implementation

Looks pretty cool, is this the way error management involves a development kit? No, all you need is 10 lines of code, like this:

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(); }}}Copy the code

So anywhere if you want to print error messages, do log.info(apperrormessages.message (” app-100-400 “,” XXX “)); You can. If you still have ideas to do a Wrapper with the log, such as log.info(” app-100-400 “,” XXX “); , there is no problem, the sample code is as follows:

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)); }}Copy the code

Then you can integrate Error code directly into the log, which is very convenient. I have written the above code, you can refer to the project address at the end of the article.

The final log output is as follows:

Note: We use SlF4J’s MessageFormatter to facilitate subsequent integration of SLF4J, and slF4J’s MessageFormatter is more fault tolerant and performs better than Java’s MessageFormat.

Six FAQ

1 Why do I select the 3-bit HTTP Status Code as the Error Status Code?

Most developers will be familiar with the HTTP Status Code, so they will have a general idea of what it means. There are also strict requirements for application developers that you should not interpret 404 as an internal error, such as a database connection failure. HTTP Status Codes are classified as follows, of course you can also refer to 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),

But Error Status Code is not limited to HTTP Status Code, you can also refer to SMTP, POP3 Status Code, in addition to your own choice of Code such as 007,777, as long as it can be explained reasonably.

In everyday life, we may use numbers with special meanings or sound like numbers. Here are some friendly reminders:

  • Uic-login-666: Great, perfect LOGIN. But if you have a European or American foreigner in your team, he may understand it as malicious login and login failure
  • App-login-062: Don’t use 62 if you have hangzhou natives on your team
  • App-001-013: If this error code is to be passed through to end users, please do not use the number 13, as it may cause discomfort

This number or number homophony with special meaning, such as 520, 886, 999, 95, is very easy to understand or more friendly if it can be used properly, such as transparent to the user UIC-REG-200(registration success), if adjusted to UIC-REG-520 may be more warm. In general, use these numbers to pay attention to the scenario, of course, it is safe to refer to HTTP, SMTP and other design status code.

The properties file stores error code and message. Is it really better than enum and POJO?

From the perspective of Java and IntelliJ IDEA support, the current cooperation is relatively good, such as I18N and maintenance cost, etc. In addition, these Errormessages. properties can also be submitted to the central warehouse for centralized management of Error Code. Java Enum+ POJOs are cumbersome for i18N and centralized management, as you can see from jDoctor’s Problem Builder above. Of course, it may not be absolute in different languages. For example, in Rust, it may be a good choice to use enum to implement Error code because of its rich features.

#[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) } }Copy the code

3 Why not provide the Error level in Error Code

Many error code designs add error levels, such as RS-001-404-9, the last digit indicates the severity level of the error. There’s no problem with that, but there are also practical factors to consider, such as:

  • The level of errors will be adjusted dynamically: for example, the level of errors that were previously very serious will now be less serious as time and space change. If the resource is not found, it may be very serious before, but now that you have added the backup scheme, you can look again from the backup server, so the error on the master service may not be so serious now.
  • Different teams have different perceptions of the error level. For example, OSS-404 cannot be found on the DATA server of the OSS team, because the metadata information exists, and the corresponding data cannot be found on the data server. This is a very serious error. Thunder volume in the business team, such as responsible for Serverless Jamstack, one of the missing files, such as HTML, CSS, image, may not be a big problem, wait for a moment to try again, not upload again. What I want to show is that the same mistakes are not equally important in different teams.

If you fix the basic error in error code, you can’t change it later. If you change the error level, it may be another error code, causing statistical and understanding problems. I personally recommend that the severity level not be included in the error code. Instead, use external documentation and descriptions. Of course, you can also use log.info, log.error to determine the level of the error.

4 Can you provide shared libraries?

Since IntelliJ IDEA does not support dynamic properties file names, if you use dynamic properties file names, you will not be able to code prompt, lookup and other functions will not be used. Therefore, @propertyKey (resourceBundle = BUNDLE_FQN) must be static. It’s a one-time job, but it’s also easier to customize the code if you want to, for example, integrate it with the Log4j 2.x or custom logging framework. Logging is a basic requirement of a project, so when you create a project, add Error Code to the project template. This will automatically include logging and Error Code functions after the project is created.

5. Other considerations

The original article and the related discussion on Reddit were also cleaned up and explained:

  • Internal and external differences: for example, errors of internal developers may include specific information of the server. Of course, final consumers, such as FaaS developers of the platform, may not be able to output such information, which has certain security risks.
  • Be careful about exposing sensitive data to errors: The output to the error log must be masked, but it does not affect your ability to locate errors, depending on the situation.
  • Do not use error messages as API contracts: In API scenarios, there are two ways to respond to errors: by responding to error codes, as in REST apis; The other is to respond to the message, like GraphQL, so you can choose that.
  • Error Code consistency: Error messages are sent to different consumers, such as REST apis and interfaces. The error messages may be different, such as internationalization or desensitization. However, it is best to use the same error code, that is, front End + Backend shares the same error code. Easy to locate errors and statistics.

Seven summarizes

Using Error code + storing error messages based on properties files is a comprehensive trade-off. If IDEA doesn’t support properties files well and you see an Error Code that doesn’t directly locate the Error message but instead needs to jump to find the corresponding message, then Enum + POJO may be a good choice. In addition, the design of Error code also prefers HTTP status code. This is mainly based on the fact that people are familiar with HTTP, so they can guess the general meaning. On the contrary, randomly coded numbers do not have the advantage of this method, so you need to go to the Error Code center to find out. It is also a waste of developers’ time.

The original link

This article is the original content of Aliyun and shall not be reproduced without permission.