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 define a function in Rails Console
I want to click a GoogleMap pin in RSpec
I want to create a generic annotation for a type
I want to add a delete function to the comment function
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
[Rails] How to write exception handling?
Exception handling with a fluid interface
I want to extract between character strings with a regular expression
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
try-catch-finally exception handling How to use java
[Android] I want to make QA easier ... That's right! Let's make a debug menu!
I want to recursively get the superclass and interface of a certain class
Even in Java, I want to output true with a == 1 && a == 2 && a == 3 (black magic edition)
rsync4j --I want to touch rsync in Java.
I want to play with Firestore from Rails
I want to be eventually even in kotlin
I want to write quickly from java to sqlite
I want to truncate after the decimal point
I tried to develop a website to record expenses.
I tried to implement a server using Netty
I want to reduce simple mistakes. To command yourself.
I tried to break a block with java (1)
I want to perform aggregation processing with spring-batch
[Rails] I want to load CSS with webpacker
I want to delete files managed by Git
I want to get the value in Ruby
I want to use PowerMock in a class that combines parameterized tests and ordinary tests
[Rails] I want to display the link destination of link_to in a separate tab
☾ Java / Exception handling
7 things I want you to keep so that it doesn't become a fucking code
Java exception handling
Java exception handling
I want to implement it additionally while using kotlin on a site running Java
[Android Studio] I want to use Maven library on Android
I want to transition screens with kotlin and java!
[Swift] I want to do something like C's sprinftf
I want to stop snake case in table definition
I want to call the main method using reflection
[Rough commentary] I want to marry the pluck method
I want to return the scroll position of UITableView!
I want to simplify the log output on Android
I want to Flash Attribute in Spring even if I set a reverse proxy! (do not do)
I want to get a list of the contents of a zip file and its uncompressed size
Implemented a strong API for "I want to display ~~ on the screen" with simple CQRS
I want to return a type different from the input element with Java8 StreamAPI reduce ()