cbtop/observability_backend/
types.rs1use std::collections::HashMap;
4use std::time::Instant;
5
6pub type ObservabilityResult<T> = Result<T, ObservabilityError>;
8
9#[derive(Debug, Clone, PartialEq)]
11pub enum ObservabilityError {
12 ConnectionFailed { backend: String, reason: String },
14 AuthenticationFailed { backend: String },
16 InvalidConfig { reason: String },
18 RateLimited {
20 backend: String,
21 retry_after_sec: u64,
22 },
23 ExportFailed { backend: String, reason: String },
25 BackendNotConfigured { backend: String },
27 SerializationError { reason: String },
29}
30
31impl std::fmt::Display for ObservabilityError {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 match self {
34 Self::ConnectionFailed { backend, reason } => {
35 write!(f, "Connection to {} failed: {}", backend, reason)
36 }
37 Self::AuthenticationFailed { backend } => {
38 write!(f, "Authentication failed for {}", backend)
39 }
40 Self::InvalidConfig { reason } => {
41 write!(f, "Invalid configuration: {}", reason)
42 }
43 Self::RateLimited {
44 backend,
45 retry_after_sec,
46 } => {
47 write!(
48 f,
49 "{} rate limited, retry after {}s",
50 backend, retry_after_sec
51 )
52 }
53 Self::ExportFailed { backend, reason } => {
54 write!(f, "Export to {} failed: {}", backend, reason)
55 }
56 Self::BackendNotConfigured { backend } => {
57 write!(f, "Backend {} not configured", backend)
58 }
59 Self::SerializationError { reason } => {
60 write!(f, "Serialization error: {}", reason)
61 }
62 }
63 }
64}
65
66impl std::error::Error for ObservabilityError {}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
70pub enum ObservabilityBackend {
71 Datadog,
73 NewRelic,
75 Honeycomb,
77 Otlp,
79 Webhook,
81}
82
83impl ObservabilityBackend {
84 pub fn name(&self) -> &'static str {
86 match self {
87 Self::Datadog => "Datadog",
88 Self::NewRelic => "NewRelic",
89 Self::Honeycomb => "Honeycomb",
90 Self::Otlp => "OTLP",
91 Self::Webhook => "Webhook",
92 }
93 }
94}
95
96#[derive(Debug, Clone)]
98pub struct DatadogConfig {
99 pub host: String,
101 pub port: u16,
103 pub api_key: Option<String>,
105 pub default_tags: Vec<String>,
107 pub prefix: String,
109}
110
111impl Default for DatadogConfig {
112 fn default() -> Self {
113 Self {
114 host: "localhost".to_string(),
115 port: 8125,
116 api_key: None,
117 default_tags: Vec::new(),
118 prefix: "cbtop".to_string(),
119 }
120 }
121}
122
123#[derive(Debug, Clone)]
125pub struct NewRelicConfig {
126 pub endpoint: String,
128 pub api_key: String,
130 pub account_id: String,
132 pub default_attributes: HashMap<String, String>,
134}
135
136impl NewRelicConfig {
137 pub fn new(api_key: impl Into<String>, account_id: impl Into<String>) -> Self {
139 Self {
140 endpoint: "https://metric-api.newrelic.com/metric/v1".to_string(),
141 api_key: api_key.into(),
142 account_id: account_id.into(),
143 default_attributes: HashMap::new(),
144 }
145 }
146
147 pub fn with_endpoint(mut self, endpoint: impl Into<String>) -> Self {
149 self.endpoint = endpoint.into();
150 self
151 }
152}
153
154#[derive(Debug, Clone)]
156pub struct HoneycombConfig {
157 pub endpoint: String,
159 pub api_key: String,
161 pub dataset: String,
163 pub service_name: String,
165}
166
167impl HoneycombConfig {
168 pub fn new(api_key: impl Into<String>, dataset: impl Into<String>) -> Self {
170 Self {
171 endpoint: "https://api.honeycomb.io/1/events".to_string(),
172 api_key: api_key.into(),
173 dataset: dataset.into(),
174 service_name: "cbtop".to_string(),
175 }
176 }
177}
178
179#[derive(Debug, Clone)]
181pub struct OtlpConfig {
182 pub endpoint: String,
184 pub use_http: bool,
186 pub headers: HashMap<String, String>,
188 pub resource_attributes: HashMap<String, String>,
190}
191
192impl Default for OtlpConfig {
193 fn default() -> Self {
194 let mut resource_attributes = HashMap::new();
195 resource_attributes.insert("service.name".to_string(), "cbtop".to_string());
196
197 Self {
198 endpoint: "http://localhost:4317".to_string(),
199 use_http: false,
200 headers: HashMap::new(),
201 resource_attributes,
202 }
203 }
204}
205
206#[derive(Debug, Clone)]
208pub struct WebhookConfig {
209 pub url: String,
211 pub method: String,
213 pub headers: HashMap<String, String>,
215 pub auth_token: Option<String>,
217}
218
219impl WebhookConfig {
220 pub fn new(url: impl Into<String>) -> Self {
222 Self {
223 url: url.into(),
224 method: "POST".to_string(),
225 headers: HashMap::new(),
226 auth_token: None,
227 }
228 }
229
230 pub fn with_auth(mut self, token: impl Into<String>) -> Self {
232 self.auth_token = Some(token.into());
233 self
234 }
235}
236
237#[derive(Debug, Clone)]
239pub struct ExportMetric {
240 pub name: String,
242 pub value: f64,
244 pub metric_type: MetricExportType,
246 pub tags: HashMap<String, String>,
248 pub timestamp_ns: u64,
250 pub unit: Option<String>,
252}
253
254impl ExportMetric {
255 pub fn gauge(name: impl Into<String>, value: f64) -> Self {
257 Self {
258 name: name.into(),
259 value,
260 metric_type: MetricExportType::Gauge,
261 tags: HashMap::new(),
262 timestamp_ns: std::time::SystemTime::now()
263 .duration_since(std::time::UNIX_EPOCH)
264 .map(|d| d.as_nanos() as u64)
265 .unwrap_or(0),
266 unit: None,
267 }
268 }
269
270 pub fn counter(name: impl Into<String>, value: f64) -> Self {
272 Self {
273 name: name.into(),
274 value,
275 metric_type: MetricExportType::Counter,
276 tags: HashMap::new(),
277 timestamp_ns: std::time::SystemTime::now()
278 .duration_since(std::time::UNIX_EPOCH)
279 .map(|d| d.as_nanos() as u64)
280 .unwrap_or(0),
281 unit: None,
282 }
283 }
284
285 pub fn with_tag(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
287 self.tags.insert(key.into(), value.into());
288 self
289 }
290
291 pub fn with_unit(mut self, unit: impl Into<String>) -> Self {
293 self.unit = Some(unit.into());
294 self
295 }
296}
297
298#[derive(Debug, Clone, Copy, PartialEq, Eq)]
300pub enum MetricExportType {
301 Gauge,
303 Counter,
305 Histogram,
307}
308
309#[derive(Debug, Clone)]
311pub struct ExportResult {
312 pub backend: ObservabilityBackend,
314 pub success: bool,
316 pub metrics_exported: usize,
318 pub duration_ms: u64,
320 pub error: Option<String>,
322}
323
324#[derive(Debug, Clone)]
326pub struct BackendHealth {
327 pub backend: ObservabilityBackend,
329 pub healthy: bool,
331 pub last_success: Option<Instant>,
333 pub consecutive_failures: u32,
335 pub avg_latency_ms: f64,
337}