I want to design a structured exception handling

Motivation

I read various exception handling articles, but I couldn't help it, so I broadened my perspective and considered it. Summarize from the viewpoint of "how to make use of exceptions in the system to improve quality".

1. About exceptions

1.1. Features

Identify the characteristics of the exception.

--In principle, it occurs when the conditions required for processing are not met. --Exception classes can use inheritance to control catch targets by specialization and generalization. --Ignore unchecked exceptions --Data can be added --The exception class name gives some explanation (ex: NotFoundException) --Structured (exceptions can be nested)

1.2. Exception limits

The essence of an exception is in the relationship between the caller and the exception generator. It's just a function to notify the caller of a message. It is necessary to devise to track all the causes, such as remote programs, etc.

If you use it as it is

--The side that generated the exception can only notify the event that occurred. --The catcher of the exception only knows that the method failed --The method that occurred can be identified by the stack trace

2. Investigate the cause by exception handling

Think about how to implement it so that the developer does not have to read the code as much as possible. The viewpoint of cause investigation is as follows.

--Bug or failure (generally missing information) --Separation (who is bad) --Identification of the location of occurrence --Information for reproduction (generally missing information)

Organize based on the following sample implementation. Some methods are omitted for the sake of brevity.

//Bank transfer class
class BankTransfer implements BankProcess {
  //Remittance information
  Account from; //Remittance source account
  Account to;   //Remittance account
  long amount;  //Remittance amount

  //Transfer processing
  @override
  void process() throws BankException {
    try {
      from.withdrawal(amount);      //Withdrawal processing
      to.deposit(amount);           //Deposit processing
      log("transfer completed.");   //Log output
    } catch (TransferException e) {
      //Remittance failed
      log("transfer failed.");      //Log output
      rollback();
      log("rollback completed.");
      throw e;
    }
  }
}
//Account processing
abstract class Account {
  int no; //account number
  //Deposit processing
  void deposit(long amount) {
    try {
      //Assumption that an exception will occur
      deposit(amount);
    } catch (ConnectionTimeoutException e) {      //2.2.3 Carriage (who is bad)
      //Connection failed and account management system not available
      var error = new TransferException(e);       //2.2 Structure with logical exceptions
      error.setNo(this.no);                       //2.1 Utilization of additional information
      throw error;
    }
  }
  //Withdrawal processing
  void withdrawal(long amount) {
    try {
      //Assumption that an exception will occur
      withdrawal();
    } catch (ConnectionTimeoutException e) {      //2.2.3 Carriage (who is bad)
      //Connection failed and account management system not available
      var error = new TransferException(e);       //2.2 Structure with logical exceptions
      error.setNo(this.no);                       //2.1 Utilization of additional information
      throw error;
    }
  }

  //Abstraction because each account has a different bank
  abstract void deposit(int no, long amount);
  abstract void withdrawal(int no, long amount);
}

2.1. Utilize additional information when throwing an exception

When an exception is thrown, the information in the method that implements the catch statement can be collected. Set the related data from the beginning of the method to the catch occurrence point as an exception. Collect relevant data to the last.

--Class field --Method parameter values --Evaluation value of control statements such as IF / For (what data, what conditions) --Stack trace (added by default)

Solved the following viewpoints.

--Identification of the location of occurrence --Information for reproduction

2.2. Structure with logical exceptions

From the following viewpoints, we will provide the necessary information as a result of the cause investigation.

--Bug or obstacle --Separation (who is bad)

After that, we will use the easy-to-imagine "bank remittance processing" as an example and implement it using logical exceptions. Focus on the implementation part of exception handling.

2.2.1 Exception class inheritance tree

The exception class name tells you that "processing failed", but the class name acts as the subject. The following exception classes show the inheritance tree.

RuntimeException Unchecked exception
└── BankException Banking failure
└── NameResolveException Name addressing process failed (irrelevant below)
└── TransferException Remittance processing failed

2.2.2. Bug or failure

In order to judge it as a bug, it can be solved by making a rule for catching exceptions. To put it the other way around, it is important how you can pinpoint the expected exception. For that purpose, in the case of an abstract exception (SQLException, etc.), it is necessary to narrow down the exception instances to be processed by the additional information of the exception.

--Bug: Uncaught Exception --Failure: Logical exception (suggests an expected event)

2.2.3 Isolation (who is bad)

In the cause investigation, the necessary things are isolated, but the characters who are generally suspected are as follows.

--User (maybe the operation is bad) --Operator (Isn't the configuration / operation bad?) --Other connected component systems (DB, other web services, etc.)

This problem can be identified by the physical exception (causing exception) caught. An example is shown below.

--Exceptions that occur at the point of contact with user / operator operations -(Resolved by validation) --Exceptions caused by improper configuration

You can freely notify the upper method. It is also good to set it as additional information in the logical exception, You can also create a new OperatorException class (a class that suggests an operation error) and increase the number of abstract exception classes. You can also write it in the message. (Because the connection failed, XX failed.)

2.2.4 Survey results

As a result, a stack trace is added and the investigation result is completed. An example of the above sample will be described.

--Disability and certification (due to logical exception) --Due to a connection problem with an external service (an exception where the cause exception set as a logical exception occurs at the point of contact with another service) --Remittance source account number, remittance destination account number, amount (additional information) --The method that actually happened (stack trace)

3. What to do with exceptions

The exception that occurs must be caught somewhere and somehow processed. The motive of this design concept is "to make use of exceptions in the system to improve quality", so I thought that the following rules were necessary.

3.1. Principle rules for catching all exceptions

There are few reasons to use "Exception" to catch all exceptions.

--To not stop the system --When the provided library etc. forces it (when it is not cool)

The cases that can be implemented are shown below.

--Methods that can throw exceptions and shut down the system --main function --Endpoint for homebrew backend processing --Event loop --A method that cannot be stopped because there is a subsequent process --Job-based execution loop --HTTP request control

Some things, such as web application servers, handle exceptions properly, so In that case, you don't have to catch everything. It is necessary to verify the behavior when WAS catches an exception.

3.2. Report a bug to the developer

If you catch all the exceptions shown in 3.1, you will also catch unexpected physical exceptions. Therefore, it is necessary to record a bug report to the developer in a cohesive form. The possible methods are as follows.

--Record in log file --Display on a web screen, etc. --Send to log collection tool or platform --Output to standard error

Logical exceptions (expected exceptions) and physical exceptions (unexpected exceptions) can be easily distinguished. Exception class generalization and specialization come to life here. The code example is shown below.

try {
  var job = new BankTransfer(from, to);
  job.process();
} catch (BankException e) {
  //Handling of expected exceptions

} catch (RuntimeException e) {
  //Handling unexpected exceptions
}

4. Summary

If you do not create your own exception class, you cannot determine whether it is a bug or a failure based on the name of the exception class that occurred. Also, it is not possible to obtain information for reproduction. Therefore, by realizing the following things, it is possible to discover information effective for identifying the cause at an early stage.

--Create your own exception class and design it in a structured manner --Wrap the cause exception in your own exception class and set the data to reproduce the exception --Handle only the expected exception class to determine bugs / failures --Set information for isolation from other components --Exceptions are caught by the principles mentioned above

Recommended Posts

I want to design a structured exception handling
I want to develop a web application!
I want to write a nice build.gradle
I want to write a unit test!
[Ruby] I want to do a method jump!
I want to simply write a repeating string
I want to call a method of another class
I want to monitor a specific file with WatchService
I want to define a function in Rails Console
I want to add a reference type column later
I want to click a GoogleMap pin in RSpec
I want to connect to Heroku MySQL from a client
I want to create a generic annotation for a type
I want to add a delete function to the comment function
I want to convert characters ...
[Swift] I tried to implement exception handling for vending machines
[Java] I want to convert a byte array to a hexadecimal number
I want to find a relative path in a situation using Path
I want to implement a product information editing function ~ part1 ~
I want to make a specific model of ActiveRecord ReadOnly
I want to make a list with kotlin and java!
I want to call a method and count the number
I want to make a function with kotlin and java!
I want to create a form to select the [Rails] category
Even in Java, I want to output true with a == 1 && a == 2 && a == 3
I want to give a class name to the select attribute
I want to create a Parquet file even in Ruby
I want to use FireBase to display a timeline like Twitter
Swift: I want to chain arrays
I want to use FormObject well
I want to convert InputStream to String
I want to docker-compose up Next.js!
[Rails] How to write exception handling?
Exception handling with a fluid interface
I want to recursively search for files under a specific directory
I want to create a chat screen for the Swift chat app!
I want to make a button with a line break with link_to [Note]
I want to add a browsing function with ruby on rails
I want to use swipeback on a screen that uses XLPagerTabStrip
[Ruby] I want to put an array in a variable. I want to convert to an array
I want to extract between character strings with a regular expression
[Rails] I want to send data of different models in a form
I want to eliminate duplicate error messages
I want to make an ios.android app
I want to display background-ground-image on heroku.
I want to select multiple items with a custom layout in Dialog
I want to use DBViewer with Eclipse 2018-12! !!
Even in Java, I want to output true with a == 1 && a == 2 && a == 3 (PowerMockito edition)
I want to summarize Apache Wicket 8 because it is a good idea
I want to RSpec even at Jest!
I want to install PHP 7.2 on Ubuntu 20.04.
I want to create a dark web SNS with Jakarta EE 8 with Java 11
Exception handling
I want to display a PDF in Chinese (Korean) with thin reports
I want to stop Java updates altogether
I want to use @Autowired in Servlet
I want to ForEach an array with a Lambda expression in Java
I want to target static fields to @Autowired
I want to do team development remotely
"Teacher, I want to implement a login function in Spring" ① Hello World
Exception handling Exception