otelite_receiver/
error.rs1use axum::{
4 http::StatusCode,
5 response::{IntoResponse, Response},
6 Json,
7};
8use serde_json::json;
9
10pub type Result<T> = std::result::Result<T, ReceiverError>;
12
13#[derive(Debug, thiserror::Error)]
15pub enum ReceiverError {
16 #[error("Invalid OTLP protocol version: {0}")]
18 InvalidProtocolVersion(String),
19
20 #[error("Failed to parse Protobuf message: {0}")]
22 ProtobufParseError(#[from] prost::DecodeError),
23
24 #[error("Failed to parse JSON message: {0}")]
26 JsonParseError(#[from] serde_json::Error),
27
28 #[error("Invalid content type: {0}")]
30 InvalidContentType(String),
31
32 #[error("Message too large: {size} bytes (max: {max} bytes)")]
34 MessageTooLarge { size: usize, max: usize },
35
36 #[error("Missing required field: {0}")]
38 MissingField(String),
39
40 #[error("Invalid signal type: {0}")]
42 InvalidSignalType(String),
43
44 #[error("gRPC server error: {0}")]
46 GrpcError(#[from] tonic::transport::Error),
47
48 #[error("HTTP server error: {0}")]
50 HttpError(String),
51
52 #[error("Compression error: {0}")]
54 CompressionError(String),
55
56 #[error("Configuration error: {0}")]
58 ConfigError(String),
59
60 #[error("Internal error: {0}")]
62 Internal(String),
63
64 #[error("Storage error: {0}")]
66 StorageError(#[from] otelite_core::storage::StorageError),
67}
68
69impl ReceiverError {
70 pub fn http_error(msg: impl Into<String>) -> Self {
72 Self::HttpError(msg.into())
73 }
74
75 pub fn compression_error(msg: impl Into<String>) -> Self {
77 Self::CompressionError(msg.into())
78 }
79
80 pub fn config_error(msg: impl Into<String>) -> Self {
82 Self::ConfigError(msg.into())
83 }
84
85 pub fn internal(msg: impl Into<String>) -> Self {
87 Self::Internal(msg.into())
88 }
89
90 pub fn to_grpc_status(&self) -> tonic::Status {
92 match self {
93 Self::InvalidProtocolVersion(_) => tonic::Status::invalid_argument(self.to_string()),
94 Self::ProtobufParseError(_) => tonic::Status::invalid_argument(self.to_string()),
95 Self::JsonParseError(_) => tonic::Status::invalid_argument(self.to_string()),
96 Self::InvalidContentType(_) => tonic::Status::invalid_argument(self.to_string()),
97 Self::MessageTooLarge { .. } => tonic::Status::resource_exhausted(self.to_string()),
98 Self::MissingField(_) => tonic::Status::invalid_argument(self.to_string()),
99 Self::InvalidSignalType(_) => tonic::Status::invalid_argument(self.to_string()),
100 Self::GrpcError(_) => tonic::Status::internal(self.to_string()),
101 Self::HttpError(_) => tonic::Status::internal(self.to_string()),
102 Self::CompressionError(_) => tonic::Status::internal(self.to_string()),
103 Self::ConfigError(_) => tonic::Status::failed_precondition(self.to_string()),
104 Self::Internal(_) => tonic::Status::internal(self.to_string()),
105 Self::StorageError(_) => tonic::Status::internal(self.to_string()),
106 }
107 }
108
109 pub fn to_http_status(&self) -> u16 {
111 match self {
112 Self::InvalidProtocolVersion(_) => 400,
113 Self::ProtobufParseError(_) => 400,
114 Self::JsonParseError(_) => 400,
115 Self::InvalidContentType(_) => 415, Self::MessageTooLarge { .. } => 413, Self::MissingField(_) => 400,
118 Self::InvalidSignalType(_) => 400,
119 Self::GrpcError(_) => 500,
120 Self::HttpError(_) => 500,
121 Self::CompressionError(_) => 500,
122 Self::ConfigError(_) => 500,
123 Self::Internal(_) => 500,
124 Self::StorageError(_) => 500,
125 }
126 }
127}
128
129impl IntoResponse for ReceiverError {
131 fn into_response(self) -> Response {
132 let status = StatusCode::from_u16(self.to_http_status())
133 .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
134
135 let body = Json(json!({
136 "error": self.to_string(),
137 "status": status.as_u16(),
138 }));
139
140 (status, body).into_response()
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn test_error_display() {
150 let err = ReceiverError::InvalidProtocolVersion("1.0.0".to_string());
151 assert_eq!(err.to_string(), "Invalid OTLP protocol version: 1.0.0");
152 }
153
154 #[test]
155 fn test_grpc_status_conversion() {
156 let err = ReceiverError::InvalidProtocolVersion("1.0.0".to_string());
157 let status = err.to_grpc_status();
158 assert_eq!(status.code(), tonic::Code::InvalidArgument);
159 }
160
161 #[test]
162 fn test_http_status_conversion() {
163 let err = ReceiverError::InvalidContentType("text/plain".to_string());
164 assert_eq!(err.to_http_status(), 415);
165
166 let err = ReceiverError::MessageTooLarge {
167 size: 20_000_000,
168 max: 10_000_000,
169 };
170 assert_eq!(err.to_http_status(), 413);
171 }
172}