You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/adr/001_error_handling.md
+71-1Lines changed: 71 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,7 +21,28 @@ Some of the opentelemetry SDK interfaces are dictated by the specification in wa
21
21
### 2. Consolidate error types within a trait where we can, let them diverge when we can't**
22
22
23
23
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
+
enumMegaError {
28
+
TooBig,
29
+
TooSmall,
30
+
TooLong,
31
+
TooShort
32
+
}
33
+
34
+
traitMyTrait {
35
+
36
+
// Will only ever return TooBig,TooSmall errors
37
+
fnaction_one() ->Result<(), MegaError>;
38
+
39
+
// These will only ever return TooLong,TooShort errors
40
+
fnaction_two() ->Result<(), MegaError>;
41
+
fnaction_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:
25
46
26
47
```rust
27
48
enumErrorOne {
@@ -45,11 +66,40 @@ trait MyTrait {
45
66
fnaction_three() ->Result<(), ErrorTwo>;
46
67
}
47
68
```
69
+
48
70
## 3. Consolidate error types between signals where we can, let them diverge where we can't
49
71
50
72
Consider the `Exporter`s mentioned earlier. Each of them has the same failure indicators - as dicated by the OpenTelemetry spec - and we will
51
73
share the error types accordingly:
52
74
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
+
pubenumOtelTraceError {
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
+
pubenumOtelLogError {
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
+
53
103
```rust
54
104
55
105
/// opentelemetry-sdk::error
@@ -87,6 +137,20 @@ Note above that we do not box any `Error` into `InternalFailure`. Our rule here
87
137
88
138
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:
89
139
140
+
**Don't do this if the OtherError is potentially recoverable by a savvy caller**:
141
+
```rust
142
+
143
+
#[derive(Debug, Error)]
144
+
pubenumMyError {
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
+
90
154
```rust
91
155
#[derive(Debug, Error)]
92
156
pubenumMyError {
@@ -101,3 +165,9 @@ pub enum MyError {
101
165
}
102
166
```
103
167
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.
0 commit comments