1use crate::Sensitivity;
14use serde::{Deserialize, Serialize};
15
16pub const MAX_LOG_TEXT_BYTES: usize = 4 * 1024;
18pub const MAX_LOG_FIELDS: usize = 32;
20pub const MAX_LOG_FIELD_KEY_BYTES: usize = 128;
22pub const MAX_LOG_FIELD_VALUE_BYTES: usize = 4 * 1024;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum LogEventError {
28 TextTooLong,
30 TooManyFields,
32 FieldKeyTooLong,
34 FieldValueTooLong,
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
40pub enum Severity {
41 Trace,
43 Debug,
45 Info,
47 Warn,
49 Error,
51 Critical,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
57pub struct Verbosity(u8);
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub struct VerbosityError;
62
63impl Verbosity {
64 pub const V1: Self = Self(1);
66 pub const V2: Self = Self(2);
68 pub const V3: Self = Self(3);
70 pub const V4: Self = Self(4);
72 pub const V5: Self = Self(5);
74 pub const V6: Self = Self(6);
76 pub const V7: Self = Self(7);
78 pub const V8: Self = Self(8);
80 pub const V9: Self = Self(9);
82
83 pub const fn new(value: u8) -> Option<Self> {
85 if value >= 1 && value <= 9 {
86 Some(Self(value))
87 } else {
88 None
89 }
90 }
91 pub const fn value(self) -> u8 {
93 self.0
94 }
95}
96
97#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
99pub struct LogField {
100 pub key: String,
102 pub value: String,
104 pub path: bool,
106 pub sensitive: bool,
108}
109
110#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
112pub struct LogEvent {
113 pub timestamp_ms: u64,
115 pub severity: Severity,
117 pub verbosity: Verbosity,
119 pub message: String,
121 pub component: String,
123 pub module: Option<String>,
125 pub operation: Option<String>,
127 pub sensitivity: Sensitivity,
129 pub application_id: Option<String>,
131 pub node_id: Option<String>,
133 pub tenant_id: Option<String>,
135 pub trace_id: Option<String>,
137 pub request_id: Option<String>,
139 pub fields: Vec<LogField>,
141}
142
143impl LogEvent {
144 pub fn new(
146 timestamp_ms: u64,
147 severity: Severity,
148 verbosity: Verbosity,
149 component: impl Into<String>,
150 message: impl Into<String>,
151 ) -> Self {
152 Self {
153 timestamp_ms,
154 severity,
155 verbosity,
156 message: message.into(),
157 component: component.into(),
158 module: None,
159 operation: None,
160 sensitivity: Sensitivity::Safe,
161 application_id: None,
162 node_id: None,
163 tenant_id: None,
164 trace_id: None,
165 request_id: None,
166 fields: Vec::new(),
167 }
168 }
169
170 #[must_use]
172 pub fn field(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
173 self.fields.push(LogField {
174 key: key.into(),
175 value: value.into(),
176 path: false,
177 sensitive: false,
178 });
179 self
180 }
181
182 #[must_use]
184 pub fn path(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
185 self.fields.push(LogField {
186 key: key.into(),
187 value: value.into(),
188 path: true,
189 sensitive: false,
190 });
191 self
192 }
193
194 #[must_use]
196 pub fn secret(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
197 self.fields.push(LogField {
198 key: key.into(),
199 value: value.into(),
200 path: false,
201 sensitive: true,
202 });
203 self
204 }
205
206 #[must_use]
208 pub fn module(mut self, module: impl Into<String>) -> Self {
209 self.module = Some(module.into());
210 self
211 }
212
213 #[must_use]
215 pub fn operation(mut self, operation: impl Into<String>) -> Self {
216 self.operation = Some(operation.into());
217 self
218 }
219
220 #[must_use]
222 pub fn sensitivity(mut self, sensitivity: Sensitivity) -> Self {
223 self.sensitivity = sensitivity;
224 self
225 }
226
227 pub fn validate(&self) -> Result<(), LogEventError> {
229 for value in [
230 &self.message,
231 &self.component,
232 self.module.as_deref().unwrap_or_default(),
233 self.operation.as_deref().unwrap_or_default(),
234 self.application_id.as_deref().unwrap_or_default(),
235 self.node_id.as_deref().unwrap_or_default(),
236 self.tenant_id.as_deref().unwrap_or_default(),
237 self.trace_id.as_deref().unwrap_or_default(),
238 self.request_id.as_deref().unwrap_or_default(),
239 ] {
240 if value.len() > MAX_LOG_TEXT_BYTES {
241 return Err(LogEventError::TextTooLong);
242 }
243 }
244 if self.fields.len() > MAX_LOG_FIELDS {
245 return Err(LogEventError::TooManyFields);
246 }
247 for field in &self.fields {
248 if field.key.len() > MAX_LOG_FIELD_KEY_BYTES {
249 return Err(LogEventError::FieldKeyTooLong);
250 }
251 if field.value.len() > MAX_LOG_FIELD_VALUE_BYTES {
252 return Err(LogEventError::FieldValueTooLong);
253 }
254 }
255 Ok(())
256 }
257
258 pub fn retained_bytes(&self) -> usize {
260 let optional = [
261 &self.module,
262 &self.operation,
263 &self.application_id,
264 &self.node_id,
265 &self.tenant_id,
266 &self.trace_id,
267 &self.request_id,
268 ]
269 .into_iter()
270 .flatten()
271 .map(String::capacity)
272 .sum::<usize>();
273 let fields = self
274 .fields
275 .iter()
276 .map(|field| field.key.capacity().saturating_add(field.value.capacity()))
277 .sum::<usize>();
278 std::mem::size_of::<Self>()
279 .saturating_add(self.message.capacity())
280 .saturating_add(self.component.capacity())
281 .saturating_add(
282 self.fields
283 .capacity()
284 .saturating_mul(std::mem::size_of::<LogField>()),
285 )
286 .saturating_add(optional)
287 .saturating_add(fields)
288 }
289}