Skip to main content

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}