首頁 > Java > java教程 > 主體

錯誤處理不良做法

WBOY
發布: 2024-09-03 18:33:12
原創
575 人瀏覽過

Error Handling Bad Practices

本文是我之前的三步驟錯誤處理文章的後續文章。

在本文中,我想展示一些錯誤處理方面的不良做法,如果您遇到困難,也許還有一些快速修復
在你的程式碼庫中跨越它們。

免責聲明,這篇部落格文章僅代表個人意見,您需要根據您的特定用例調整建議。

1.吞嚥異常

我常常偶然發現異常被默默忽略的程式碼庫。也許開發人員很著急或想要稍後處理錯誤情況?無論如何,這是一個不好的做法,因為如果發生錯誤:

  • 使用者不會得到任何回饋,可能會認為操作成功。
  • 沒有要調查的堆疊追蹤或錯誤訊息。
  • 有時甚至沒有正確的日誌條目,幾乎不可能偵測到錯誤。

不好的例子:

  /**
   * Reads some data from another service or repo and returns it.
   * The repo might throw an exception, but we don't care.
   */
  public Mono<String> getHello() {
    try {
      return helloRepository.getHello()
          .map("Hello %s"::formatted);
    } catch (Exception e) {
      // do nothing
      return Mono.just("Hello World");
    }
  }
登入後複製

在這個例子中,我們可能想知道為什麼有些用戶抱怨沒有收到正確的問候訊息,但我們沒有看到任何錯誤,我們可能認為用戶只是感到困惑。

選項 1 - 保留 try-catch 並處理錯誤

在發生錯誤的地方進行處理,例如如果可能的話已經在儲存庫中。
如果您需要在這裡處理它,請至少記錄錯誤並傳回正確的錯誤訊息。

最少的重構

  • 記錄錯誤(也可以選擇堆疊追蹤)
  • 傳回正確的錯誤訊息
  public Mono<String> getHello() {
    try {
      return helloRepository.getHello()
          .map("Hello %s"::formatted);
    } catch (Exception e) {
      log.error("Error reading hello data from repository - {}", e.getMessage(), e);
      return Mono.error(new RuntimeException("Internal error reading hello data %s".formatted(e.getMessage()), e));
    }
  }
登入後複製

注意:

這可能會破壞上游程式碼,因為您現在會傳回錯誤而不是某些預設值。

選項 2 - 適當的反應流重構

更好的方法是處理反應式流 api 內的錯誤

  public Mono<String> getHelloHandling() {
    return helloRepository.getHello()
      .map("Hello %s"::formatted)
      // wrap the exception in a RuntimeException with a meaningful message
      .onErrorMap(e -> new RuntimeException("Internal error reading hello data from HelloRepository` %s".
          formatted(e.getMessage()), e));
  }
登入後複製

1b.吞嚥異常 - 使用反應流

範例一中的模式也出現在反應流中:

  public Mono<String> getHello() {
    return helloRepository.getHello()
        .map("Hello %s"::formatted)
        // do nothing on error, just return a default value
        .onErrorReturn("Hello world");
  }
登入後複製

看起來非常漂亮和乾淨,對吧?但我們將無法偵測到儲存庫中拋出了錯誤!
如果有預設值,至少應該寫一個錯誤日誌。

減輕

與前面的範例一樣,我們將異常包裝在另一個異常中,這次是在自訂異常中,這甚至可以更輕鬆地檢測拋出該異常的特定位置

  public Mono<String> getHello2() {
    return helloRepository.getHello()
        .map("Hello %s"::formatted)
        .onErrorMap(
            e -> new CustomException("Error reading hello-data from repository - %s".formatted(e.getMessage()), e));
  }
登入後複製

2. 重複記錄相同異常的堆疊追蹤

與默默丟棄異常相反的是多次記錄相同的異常。這是一種不好的做法,因為它會將日誌與數百或數千行堆疊追蹤混淆,而不提供任何附加含義。

在我最糟糕的例子中,我在日誌中發現了五次相同的堆疊跟踪,但根本沒有任何有意義的消息。

不好的例子:

控制器:

@RestController
@AllArgsConstructor
@Slf4j
public class HelloController {

  private final HelloService helloService;

  @GetMapping("/hello")
  public Mono<ResponseEntity<String>> hello() {
    return helloService.getHello()
        .map(ResponseEntity::ok)
        .defaultIfEmpty(ResponseEntity.notFound().build())
        .onErrorResume(e -> {
          log.error("Error:", e);
          return Mono.error(e);
        });
  }
}
登入後複製

在服務中:

@Service
@AllArgsConstructor
@Slf4j
public class HelloService {

  private final HelloRepository helloRepository;

  /**
   * Reads some data from another service or repo and returns it.
   */
  public Mono<String> getHello() {
    return helloRepository.getHello()
        .map("Hello %s"::formatted)
        .onErrorResume(e -> {
          log.error("Error:", e);
          return Mono.error(e);
        });
  }
}
登入後複製

...可能還有更多地方...

減輕

這在我之前的文章三步驟錯誤處理中已經解釋過,因此我不會再在這裡展示程式碼,而是推薦:

  • 擁有一個全域錯誤處理程序,用於記錄錯誤並向使用者傳回正確的錯誤訊息。
  • 在程式碼中,避免堆疊跟踪,但是
    • 使用有意義的訊息將異常包裝在自訂異常或運行時異常
    • 記錄錯誤訊息(不是整個堆疊追蹤)
    • 僅在全域錯誤處理程序中記錄堆疊追蹤

3. 捕獲通用異常

捕獲 Exception 或 Throwable 等通用異常可能會導致意外行為並使偵錯變得非常困難。最好捕獲特定的異常。

壞榜樣

  public Mono<String> getHello() {
    try {
      return helloRepository.getHello();
    } catch (Exception e) {
      log.error("Error while fetching hello data", e);
      return Mono.empty();
    }
  }
登入後複製

減輕

捕捉特定異常以適當處理不同的錯誤場景。

  public Mono<String> getHello() {
    try {
      return helloRepository.getHello();
    } catch (SQLException e) {
      return Mono.error(new HelloDataException("Database error while getting hello-data - %s".formatted(e.getMessage()), e));
    } catch (IOException e) {
      // maybe perform a retry?
      return Mono.error(new HelloDataException("IO error while getting hello-data - %s".formatted(e.getMessage()), e));
    }
  }
登入後複製

以及使用反應式流 API 的等效內容

  public Mono<String> getHello() {
    return helloRepository.getHello()
        .onErrorMap(SQLException.class, e -> new HelloDataException("Database error while getting hello-data - %s".formatted(e.getMessage()), e))
        .onErrorMap(IOException.class, e -> new HelloDataException("IO error while getting hello-data - %s".formatted(e.getMessage()), e));
  }
登入後複製

4.(過度)使用檢查異常

在我看來,檢查異常在 Java 中的錯誤,它們不是很有用,並且經常導致不良實踐和混亂的程式碼。

壞榜樣

public void someMethod() throws IOException, SQLException {
  // some code that might throw an exception
}
登入後複製

對於受檢查的異常,您必須在呼叫程式碼中處理它們,這使得使用其他模式變得更加困難,例如函數式程式設計或反應式串流或 Spring 中的全域錯誤處理程序。

例外:
檢查異常很有用,例如在庫程式碼中,您想要強制使用者處理異常。

減輕

對於呼叫者無法合理預期恢復的場景使用未經檢查的異常。

public void someMethod() {
  try {
    // some code that might throw an exception
  } catch (IOException | SQLException e) {
    throw new RuntimeException("An error occurred - %s".formatted(e.getMessage()), e);
  }
}
登入後複製

Using Exceptions for Control Flow

Using exceptions for control flow makes the code hard to understand and can lead to performance issues.

Bad Example

try {
  int value = Integer.parseInt("abc");
} catch (NumberFormatException e) {
  // handle the case where the string is not a number
}
登入後複製

Mitigation

Use a regular flow control mechanism like an if-statement.

String value = "abc";
if (value.matches("\\d+")) {
  int number = Integer.parseInt(value);
} else {
  // handle the case where the string is not a number
}
登入後複製

Conclusion

In this article I showed some bad practices in error handling and how to mitigate them.
I hope you found this article helpful and maybe you can use some of the recommendations in your codebase and in your next refactoring.

以上是錯誤處理不良做法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!