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}