-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Expand file tree
/
Copy patherror.rs
More file actions
360 lines (330 loc) · 12.2 KB
/
error.rs
File metadata and controls
360 lines (330 loc) · 12.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
use std::sync::Arc;
use aws_sdk_s3::primitives::ByteStreamError;
use common_geoip::GeoIpError;
use common_kafka::kafka_producer::KafkaProduceError;
use common_redis::CustomRedisError;
use common_types::{CapturedEvent, ClickHouseEvent};
use posthog_symbol_data::SymbolDataError;
use rdkafka::error::KafkaError;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use uuid::Uuid;
#[derive(Debug, Error)]
pub enum ResolveError {
#[error(transparent)]
UnhandledError(#[from] UnhandledError),
#[error(transparent)]
ResolutionError(#[from] FrameError),
}
// An unhandled failure at some stage of the event pipeline, as
// well as the index of the item in the input buffer that caused
// the failure, so we can print the offset of problematic message
#[derive(Debug)]
pub struct PipelineFailure {
pub index: usize,
pub error: Arc<UnhandledError>,
}
// The result of running the pipeline against a single message. Generally,
// an error here indicates some expected/handled invalidity of the input,
// like a missing token, or invalid timestamp. The pipeline converts a
// vector of input items into a vector of these
pub type PipelineResult = Result<ClickHouseEvent, EventError>;
#[derive(Debug, Error)]
pub enum UnhandledError {
#[error("Config error: {0}")]
ConfigError(#[from] envconfig::Error),
#[error("Kafka error: {0}")]
KafkaError(#[from] KafkaError),
#[error("Produce error: {0}")]
KafkaProduceError(#[from] KafkaProduceError),
#[error("Sqlx error: {0}")]
SqlxError(#[from] sqlx::Error),
#[error("S3 error: {0}")]
S3Error(#[from] Box<aws_sdk_s3::Error>),
#[error(transparent)]
ByteStreamError(#[from] ByteStreamError), // AWS specific bytestream error. Idk
#[error("Unhandled serde error: {0}")]
SerdeError(#[from] serde_json::Error),
#[error("Unhandled geoip error: {0}")]
GeoIpError(#[from] GeoIpError),
#[error("Unhandled redis error: {0}")]
RedisError(#[from] CustomRedisError),
#[error("Unhandled error: {0}")]
Other(String),
}
// These are errors that occur during frame resolution. This excludes e.g. network errors,
// which are handled by the store - this is the error type that's handed to the frame to see
// if it can still make something useful out of it.
// NOTE - these are serialized and deserialized, so that when we fail to get a symbol set from
// some provider (e.g. we fail to look up a sourcemap), we can return the correct error in the future
// without hitting their infra again (by storing it in PG).
#[derive(Debug, Clone, PartialEq, Eq, Error, Serialize, Deserialize)]
pub enum FrameError {
#[error(transparent)]
JavaScript(#[from] JsResolveErr),
#[error(transparent)]
Hermes(#[from] HermesError),
#[error(transparent)]
Proguard(#[from] ProguardError),
#[error(transparent)]
Apple(#[from] AppleError),
#[error("No symbol set for chunk id: {0}")]
MissingChunkIdData(String),
}
#[derive(Debug, Clone, PartialEq, Eq, Error, Serialize, Deserialize)]
pub enum JsResolveErr {
#[error("This frame had no source url or chunk id")]
NoUrlOrChunkId,
// The frame has no source url. This might indicate it needs no further processing, who knows
#[error("No source url found")]
NoSourceUrl, // Deprecated, use NoUrlOrChunkId instead
// We failed to parse a found source map
#[error("Invalid source map: {0}")]
InvalidSourceMap(String),
// We found and parsed the source map, but couldn't find our frames token in it
#[error("Token not found for frame: {0}:{1}:{2}")]
TokenNotFound(String, u32, u32),
// We couldn't parse the source url of the frame
#[error("Invalid source url: {0}")]
InvalidSourceUrl(String),
// We couldn't find a sourcemap associated with the frames source url, after
// fetching the source, in either the headers or body. This might indicate
// the frame is not minified
#[error("Could not find sourcemap for source url: {0}")]
NoSourcemap(String),
// We made a request to the source URL, and got a source
// map header, but couldn't parse it to a utf8 string
#[error("Could not parse source-map header from url {0}")]
InvalidSourceMapHeader(String),
// We found a source map url, in the headers or body
// of the response from the source url, but couldn't
// parse it to a URL to actually fetch the source map
#[error("Invalid source url: {0}")]
InvalidSourceMapUrl(String),
// For timeout errors when fetching source or sourcemap
#[error("Request timed out while fetching: {0}")]
Timeout(String),
// For when the server returns a non-200 status code
#[error("HTTP error {0} while fetching: {1}")]
HttpStatus(u16, String),
// For DNS/connection/TLS errors
#[error("Network error while fetching: {0}")]
NetworkError(String),
// For redirect loops or too many redirects
#[error("Redirect error while fetching: {0}")]
RedirectError(String),
#[error("JSDataError: {0}")]
JSDataError(#[from] SymbolDataError),
#[error("Invalid Source and Map")]
InvalidSourceAndMap,
#[error("Invalid data url found at {0}. {1}")]
InvalidDataUrl(String, String),
#[error("No sourcemap uploaded for chunk id: {0}")]
NoSourcemapUploaded(String),
}
#[derive(Debug, Clone, PartialEq, Eq, Error, Serialize, Deserialize)]
pub enum HermesError {
#[error("Data error: {0}")]
DataError(#[from] SymbolDataError),
#[error("Invalid map: {0}")]
InvalidMap(String),
#[error("No sourcemap uploaded for chunk id: {0}")]
NoSourcemapUploaded(String),
#[error("No chunk id sent with frame")]
NoChunkId,
#[error("No token for column {0} on chunk {1}")]
NoTokenForColumn(u32, String),
}
#[derive(Debug, Clone, PartialEq, Eq, Error, Serialize, Deserialize)]
pub enum ProguardError {
#[error("Data error: {0}")]
DataError(#[from] SymbolDataError),
#[error("Invalid mapping")]
InvalidMapping,
#[error("No proguard map uploaded for id: {0}")]
MissingMap(String),
#[error("No map ID sent with frame")]
NoMapId,
#[error("No original frames could be derived from this raw frame")]
NoOriginalFrames,
#[error("No module provided")]
NoModuleProvided,
#[error("No class matched")]
MissingClass,
#[error("Invalid class format")]
InvalidClass,
}
#[derive(Debug, Clone, PartialEq, Eq, Error, Serialize, Deserialize)]
pub enum AppleError {
#[error("Data error: {0}")]
DataError(#[from] SymbolDataError),
#[error("No dSYM uploaded for debug_id: {0}")]
MissingDsym(String),
#[error("No debug_id found for frame")]
NoDebugId,
#[error("Invalid address format: {0}")]
InvalidAddress(String),
#[error("Symbol not found at address: {0:#x}")]
SymbolNotFound(u64),
#[error("Failed to parse dSYM: {0}")]
ParseError(String),
#[error("No matching debug image found for frame")]
NoMatchingDebugImage,
}
#[derive(Debug, Error, Clone, Serialize, PartialEq)]
pub enum EventError {
#[error("Wrong event type: {0} for event {1}")]
WrongEventType(String, Uuid),
#[error("No properties on event {0}")]
NoProperties(Uuid),
#[error("Invalid properties on event {0}, serde error: {1}")]
InvalidProperties(Uuid, String),
#[error("Empty exception list on event {0}")]
EmptyExceptionList(Uuid),
#[error("Invalid event timestamp: {0}, {1}")]
InvalidTimestamp(String, String),
#[error("No team for token: {0}")]
NoTeamForToken(String),
#[error("Suppressed issue: {0}")]
Suppressed(Uuid),
#[error("Suppressed by rule: {0}")]
SuppressedByRule(Uuid),
#[error("Could not deserialize event data: {1}")]
FailedToDeserialize(Box<CapturedEvent>, String),
#[error("Filtered by team id")]
FilteredByTeamId,
}
impl JsResolveErr {
pub fn metric_reason(&self) -> &'static str {
match self {
Self::NoUrlOrChunkId | Self::NoSourceUrl => "no_reference",
Self::NoSourcemap(_) | Self::NoSourcemapUploaded(_) => "no_symbol_set",
Self::TokenNotFound(..) => "symbol_not_found",
Self::Timeout(_)
| Self::HttpStatus(..)
| Self::NetworkError(_)
| Self::RedirectError(_) => "network_error",
Self::InvalidSourceMap(_)
| Self::InvalidSourceUrl(_)
| Self::InvalidSourceMapHeader(_)
| Self::InvalidSourceMapUrl(_)
| Self::InvalidDataUrl(..)
| Self::JSDataError(_)
| Self::InvalidSourceAndMap => "invalid_data",
}
}
}
impl HermesError {
pub fn metric_reason(&self) -> &'static str {
match self {
Self::NoChunkId => "no_reference",
Self::NoSourcemapUploaded(_) => "no_symbol_set",
Self::NoTokenForColumn(..) => "symbol_not_found",
Self::DataError(_) | Self::InvalidMap(_) => "invalid_data",
}
}
}
impl ProguardError {
pub fn metric_reason(&self) -> &'static str {
match self {
Self::NoMapId | Self::NoModuleProvided => "no_reference",
Self::MissingMap(_) => "no_symbol_set",
Self::NoOriginalFrames | Self::MissingClass => "symbol_not_found",
Self::DataError(_) | Self::InvalidMapping | Self::InvalidClass => "invalid_data",
}
}
}
impl AppleError {
pub fn metric_reason(&self) -> &'static str {
match self {
Self::NoDebugId | Self::NoMatchingDebugImage => "no_reference",
Self::MissingDsym(_) => "no_symbol_set",
Self::SymbolNotFound(_) => "symbol_not_found",
Self::DataError(_) | Self::InvalidAddress(_) | Self::ParseError(_) => "invalid_data",
}
}
}
impl FrameError {
pub fn metric_reason(&self) -> &'static str {
match self {
Self::JavaScript(e) => e.metric_reason(),
Self::Hermes(e) => e.metric_reason(),
Self::Proguard(e) => e.metric_reason(),
Self::Apple(e) => e.metric_reason(),
Self::MissingChunkIdData(_) => "no_symbol_set",
}
}
}
impl From<JsResolveErr> for ResolveError {
fn from(e: JsResolveErr) -> Self {
FrameError::JavaScript(e).into()
}
}
impl From<HermesError> for ResolveError {
fn from(e: HermesError) -> Self {
FrameError::Hermes(e).into()
}
}
impl From<ProguardError> for ResolveError {
fn from(e: ProguardError) -> Self {
FrameError::Proguard(e).into()
}
}
impl From<AppleError> for ResolveError {
fn from(e: AppleError) -> Self {
FrameError::Apple(e).into()
}
}
impl From<FrameError> for UnhandledError {
fn from(e: FrameError) -> Self {
// TODO - this should be unreachable, but I need to reconsider the error enum structure to make it possible to assert that
// at the type level. Leaving for a later refactor for now.
UnhandledError::Other(format!("Unhandled resolution error: {e}"))
}
}
impl From<reqwest::Error> for JsResolveErr {
fn from(e: reqwest::Error) -> Self {
if e.is_timeout() {
return JsResolveErr::Timeout(e.to_string());
}
if e.is_status() {
let status = e.status().unwrap();
return JsResolveErr::HttpStatus(status.as_u16(), e.to_string());
}
if e.is_redirect() {
return JsResolveErr::RedirectError(e.to_string());
}
// For connect errors, DNS errors, TLS errors, etc.
if e.is_connect() || e.is_request() || e.is_builder() {
return JsResolveErr::NetworkError(e.to_string());
}
// Fallback for any other errors
JsResolveErr::NetworkError(e.to_string())
}
}
impl From<aws_sdk_s3::Error> for UnhandledError {
fn from(e: aws_sdk_s3::Error) -> Self {
UnhandledError::S3Error(Box::new(e))
}
}
impl From<(usize, UnhandledError)> for PipelineFailure {
fn from((index, error): (usize, UnhandledError)) -> Self {
PipelineFailure {
index,
error: Arc::new(error),
}
}
}
impl From<(usize, Arc<UnhandledError>)> for PipelineFailure {
fn from((index, error): (usize, Arc<UnhandledError>)) -> Self {
PipelineFailure { index, error }
}
}
impl From<UnhandledError> for PipelineFailure {
fn from(error: UnhandledError) -> Self {
PipelineFailure {
index: 0,
error: Arc::new(error),
}
}
}