# 错误处理与日志记录

> 把 Debug.handle 作为异常的收敛点、对特定错误做出反应，以及向用户展示失败。

Source: https://crossfly.dev/zh/docs/best-practices/tratamento-de-erros-e-logging — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 错误处理与日志记录

核心要点是有**唯一的收敛点**来处理异常，并把"用户看到的错误"与"进日志的错误"区分开来。

## 收敛点：`Debug.handle(e)` 与 `Debug.log(...)`
```java
try {
   arriscado();
} catch (Exception e) {
   Debug.handle(e);        // 统一记录并处理（单一漏斗）
}
Debug.log("estado:", valor);   // 自由诊断
```
框架的动画和任务在内部已通过 `Debug.handle` 捕获 `Throwable`。**为什么要有一个收敛点：** 一个未处理的异常**在绘制线程上**会拖垮当前帧——把它汇集起来可避免崩溃，并提供一个统一的埋点位置。

## 对特定错误做出反应（不要吞掉一切）
```java
try (SqliteDb db = SqliteDb.open("app.db")) {
   db.execute("INSERT INTO cliente(email) VALUES(?)", email);
} catch (SqliteException e) {
   if (e.code == Sqlite.CONSTRAINT) Hint.show("E-mail já cadastrado", 2000);   // 19
   else if (e.code == Sqlite.BUSY)  Hint.show("Banco ocupado, tente de novo", 2000);  // 5
   else Debug.handle(e);
}
```
`SqliteException.code` 携带 SQLite 代码（`Sqlite.OK/BUSY/CONSTRAINT/LOCKED/...`）。同样适用于其他类型化结果（例如生物识别的 `AuthResult.ERROR`）。

## 后台回调的错误 → 用 `onUI` 在 UI 上展示
回调（网络、GPS、BLE）运行在绘制线程**之外**。要提醒用户，请用 `onUI` 编组：
```java
new Thread(() -> {
   try { sincronizar(); }
   catch (Exception e) {
      Component.onUI(() -> Hint.show("Falha ao sincronizar", 2000));   // Hint = 非阻塞
      Debug.handle(e);
   }
}).start();
```
- **`Hint.show(...)`** 用于非阻塞提示；当需要用户做决定时用 **`Alert`**。
- **区分开：** 用户需要看到的（`Hint`/`Alert`）vs. 只进日志的（`Debug.log`/`handle`）。

## 调试标志
`Debug.traceKeyboard`、`Debug.showFPS`、`Debug.beepOnGC` 有助于在开发期间进行诊断。

## 最佳实践
- 对于无法专门处理的情况，用**一个收敛点**（`Debug.handle`）。
- 用对用户有用的信息处理**特定**代码（`CONSTRAINT`、`BUSY`）。
- 后台错误 → `onUI` + `Hint`/`Alert`；绝不要在回调中直接触碰 UI。

## 常见错误
- 空的 `catch (Exception e) {}`——把问题吞掉了。
- 向用户展示原始堆栈跟踪（应使用带清晰信息的 `Hint`/`Alert`；堆栈进日志）。
- 未使用 `onUI` 就从后台线程触碰 UI。

## 另请参阅
- [对话框与模态框](/docs/ui/navigation/dialogs-and-modals) · [后台工作](/docs/data-and-services/background-work)
