Skip to content

Commit 73df360

Browse files
committed
address review feedback
1 parent 749454d commit 73df360

File tree

1 file changed

+71
-1
lines changed

1 file changed

+71
-1
lines changed

docs/adr/001_error_handling.md

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,28 @@ Some of the opentelemetry SDK interfaces are dictated by the specification in wa
2121
### 2. Consolidate error types within a trait where we can, let them diverge when we can't**
2222

2323
We aim to consolidate error types where possible _without indicating a function may return more errors than it can actually return_.
24-
Here's an example:
24+
25+
**Don't do this** - each function's signature indicates that it returns errors it will _never_ return, forcing the caller to write handlers for dead paths:
26+
```rust
27+
enum MegaError {
28+
TooBig,
29+
TooSmall,
30+
TooLong,
31+
TooShort
32+
}
33+
34+
trait MyTrait {
35+
36+
// Will only ever return TooBig,TooSmall errors
37+
fn action_one() -> Result<(), MegaError>;
38+
39+
// These will only ever return TooLong,TooShort errors
40+
fn action_two() -> Result<(), MegaError>;
41+
fn action_three() -> Result<(), MegaError>;
42+
}
43+
```
44+
45+
**Instead, do this** - each function's signature indicates only the errors it can return, providing an accurate contract to the caller:
2546

2647
```rust
2748
enum ErrorOne {
@@ -45,11 +66,40 @@ trait MyTrait {
4566
fn action_three() -> Result<(), ErrorTwo>;
4667
}
4768
```
69+
4870
## 3. Consolidate error types between signals where we can, let them diverge where we can't
4971

5072
Consider the `Exporter`s mentioned earlier. Each of them has the same failure indicators - as dicated by the OpenTelemetry spec - and we will
5173
share the error types accordingly:
5274

75+
**Don't do this** - each signal has its own error type, despite having exactly the same failure cases:
76+
77+
```rust
78+
#[derive(Error, Debug)]
79+
pub enum OtelTraceError {
80+
#[error("Shutdown already invoked")]
81+
AlreadyShutdown,
82+
83+
#[error("Operation failed: {0}")]
84+
InternalFailure(String),
85+
86+
/** ... additional errors ... **/
87+
}
88+
89+
#[derive(Error, Debug)]
90+
pub enum OtelLogError {
91+
#[error("Shutdown already invoked")]
92+
AlreadyShutdown,
93+
94+
#[error("Operation failed: {0}")]
95+
InternalFailure(String),
96+
97+
/** ... additional errors ... **/
98+
}
99+
```
100+
101+
**Instead, do this** - error types are consolidated between signals where this can be done appropriately:
102+
53103
```rust
54104

55105
/// opentelemetry-sdk::error
@@ -87,6 +137,20 @@ Note above that we do not box any `Error` into `InternalFailure`. Our rule here
87137

88138
If the caller may potentially recover from an error, we will follow the generally-accepted best practice (e.g., see [canonical's guide](https://canonical.github.io/rust-best-practices/error-and-panic-discipline.html) and instead preserve the nested error:
89139

140+
**Don't do this if the OtherError is potentially recoverable by a savvy caller**:
141+
```rust
142+
143+
#[derive(Debug, Error)]
144+
pub enum MyError {
145+
#[error("Error one occurred")]
146+
ErrorOne,
147+
148+
#[error("Operation failed: {0}")]
149+
OtherError(String),
150+
```
151+
152+
**Instead, do this**, allowing the caller to match on the nested error:
153+
90154
```rust
91155
#[derive(Debug, Error)]
92156
pub enum MyError {
@@ -101,3 +165,9 @@ pub enum MyError {
101165
}
102166
```
103167

168+
Note that at the time of writing, there is no instance we have identified within the project that has required this.
169+
170+
### 5. Use thiserror by default
171+
We will use [thiserror](https://docs.rs/thiserror/latest/thiserror/) by default to implement Rust's [error trait](https://doc.rust-lang.org/core/error/trait.Error.html).
172+
This keeps our code clean, and as it does not appear in our interface, we can choose to replace any particular usage with a hand-rolled implementation should we need to.
173+

0 commit comments

Comments
 (0)