Skip to main content

relay_event_schema/protocol/ourlog/
container.rs

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