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
//! Envelope: a typed unit of work the transport delivers to a single ingest
//! endpoint. Each variant carries an already-serialized JSON body plus its
//! data category (used for rate-limit accounting).
use serde_json::Value;
/// Logical data category, used by the rate limiter to bucket limits.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DataCategory {
/// Error events.
Error,
/// Performance transactions / spans.
Transaction,
/// Release-health session updates.
Session,
/// Structured log records.
Log,
/// Inbound/outbound HTTP request records.
HttpRequest,
/// Database query records.
Db,
/// Cron / heartbeat check-ins.
Heartbeat,
/// Release registrations.
Release,
}
impl DataCategory {
/// Stable string key for this category.
pub fn as_str(&self) -> &'static str {
match self {
DataCategory::Error => "error",
DataCategory::Transaction => "transaction",
DataCategory::Session => "session",
DataCategory::Log => "log",
DataCategory::HttpRequest => "http_request",
DataCategory::Db => "db",
DataCategory::Heartbeat => "heartbeat",
DataCategory::Release => "release",
}
}
}
/// A ready-to-send ingest item: endpoint path, category and JSON body.
#[derive(Debug, Clone)]
pub struct Envelope {
/// Path relative to the configured host, e.g. `/ingest/v1/errors`.
pub path: &'static str,
/// Rate-limit category for this item.
pub category: DataCategory,
/// The JSON body to POST.
pub body: Value,
}
impl Envelope {
/// Build an envelope from a serializable payload, panicking only on a
/// programming error (a payload that cannot be represented as JSON).
pub fn new<T: serde::Serialize>(
path: &'static str,
category: DataCategory,
payload: &T,
) -> Self {
let body = serde_json::to_value(payload).unwrap_or(Value::Null);
Envelope {
path,
category,
body,
}
}
}