relay_event_schema/protocol/trace_metric/container.rs
1use serde::{Deserialize, Serialize};
2
3use crate::protocol::utils;
4
5/// Metadata supplied with an item container containing trace metrics.
6///
7/// # Versions
8///
9/// ## Behaviour
10///
11/// Different versions influence how trace metrics are normalized and processed:
12///
13/// | Version | client ip | user agent |
14/// |---------|--------------------|------------|
15/// | none | {{auto}} | never |
16/// | 2 | {{auto}}, settings | settings |
17///
18/// For example, in version 2:
19/// - the client ip is inferred if either the client address attribute is
20/// set to `{{auto}}` or the ingest settings specify `infer_ip` as `auto`.
21/// - the user agent is only inferred if ingest settings specify `infer_user_agent` as `auto`.
22///
23/// ## Schema Changelog
24///
25/// | Version | Changes |
26/// |---------|---------------------------------|
27/// | 2 | Adds `ingest_settings` |
28#[derive(Debug, Clone, Deserialize, Serialize)]
29pub struct ContainerMetadata {
30 /// The metadata version.
31 ///
32 /// The version influences how trace metrics are processed during ingestion.
33 #[serde(skip_serializing_if = "Option::is_none")]
34 pub version: Option<u16>,
35 /// Settings controlling parts of the ingestion of individual trace metrics.
36 ///
37 /// Supported since version `2`.
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub ingest_settings: Option<IngestSettings>,
40}
41
42/// Trace metric ingestion settings.
43///
44/// See also: [`ContainerMetadata::ingest_settings`].
45#[derive(Debug, Clone, Deserialize, Serialize)]
46pub struct IngestSettings {
47 /// Controls how and if the client IP address should be inferred.
48 #[serde(
49 default,
50 skip_serializing_if = "Option::is_none",
51 deserialize_with = "utils::none_on_error"
52 )]
53 pub infer_ip: Option<InferIp>,
54 /// Controls how and if the user agent should be inferred.
55 #[serde(
56 default,
57 skip_serializing_if = "Option::is_none",
58 deserialize_with = "utils::none_on_error"
59 )]
60 pub infer_user_agent: Option<InferUserAgent>,
61}
62
63/// Settings controlling how client IP addresses should be inferred.
64///
65/// Specifically frontend SDKs may want to have the IP address automatically inferred.
66#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
67pub enum InferIp {
68 /// The IP address is inferred from the client connection.
69 #[serde(rename = "auto")]
70 Auto,
71 /// The client IP address is never inferred.
72 #[serde(rename = "never")]
73 Never,
74}
75
76impl InferIp {
77 /// Returns `true` if `self` is [`Self::Auto`].
78 pub fn is_auto(&self) -> bool {
79 matches!(self, Self::Auto)
80 }
81}
82
83/// Settings controlling how the user agent should be inferred.
84///
85/// Specifically frontend SDKs may want to have the user agent and browser information automatically inferred
86/// from the HTTP header provided user agent.
87#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
88pub enum InferUserAgent {
89 /// The user agent should be inferred from the http request submitting the trace metrics.
90 #[serde(rename = "auto")]
91 Auto,
92 /// The user agent should never be inferred.
93 #[serde(rename = "never")]
94 Never,
95}
96
97impl InferUserAgent {
98 /// Returns `true` if `self` is [`Self::Auto`].
99 pub fn is_auto(&self) -> bool {
100 matches!(self, Self::Auto)
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_valid_round_trip() {
110 let m: ContainerMetadata = serde_json::from_str(
111 r#"{
112 "version": 123,
113 "ingest_settings": {
114 "infer_ip": "auto",
115 "infer_user_agent": "never"
116 }
117 }"#,
118 )
119 .unwrap();
120
121 insta::assert_json_snapshot!(&m, @r#"
122 {
123 "version": 123,
124 "ingest_settings": {
125 "infer_ip": "auto",
126 "infer_user_agent": "never"
127 }
128 }
129 "#);
130 }
131
132 #[test]
133 fn test_ingest_settings_invalid() {
134 let m: ContainerMetadata = serde_json::from_str(
135 r#"{"ingest_settings": {
136 "infer_ip": "unknown",
137 "infer_user_agent": 123
138 }}"#,
139 )
140 .unwrap();
141
142 insta::assert_json_snapshot!(&m, @r#"
143 {
144 "ingest_settings": {}
145 }
146 "#);
147 }
148
149 #[test]
150 fn test_version_invalid_fails() {
151 let _ = serde_json::from_str::<ContainerMetadata>(r#"{"version": {}}"#).unwrap_err();
152 }
153}