otelite_core/telemetry/
log.rs1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
8pub struct LogRecord {
9 pub timestamp: i64,
11
12 pub observed_timestamp: Option<i64>,
14
15 pub severity: SeverityLevel,
17
18 pub severity_text: Option<String>,
20
21 pub body: String,
23
24 pub attributes: HashMap<String, String>,
26
27 pub trace_id: Option<String>,
29
30 pub span_id: Option<String>,
32
33 pub resource: Option<super::Resource>,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
39#[repr(u8)]
40pub enum SeverityLevel {
41 Trace = 1,
43
44 Debug = 5,
46
47 Info = 9,
49
50 Warn = 13,
52
53 Error = 17,
55
56 Fatal = 21,
58}
59
60impl LogRecord {
61 pub fn new(severity: SeverityLevel, body: impl Into<String>, timestamp: i64) -> Self {
63 Self {
64 timestamp,
65 observed_timestamp: None,
66 severity,
67 severity_text: None,
68 body: body.into(),
69 attributes: HashMap::new(),
70 trace_id: None,
71 span_id: None,
72 resource: None,
73 }
74 }
75
76 pub fn with_attribute(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
78 self.attributes.insert(key.into(), value.into());
79 self
80 }
81
82 pub fn with_trace_context(mut self, trace_id: String, span_id: String) -> Self {
84 self.trace_id = Some(trace_id);
85 self.span_id = Some(span_id);
86 self
87 }
88
89 pub fn with_resource(mut self, resource: super::Resource) -> Self {
91 self.resource = Some(resource);
92 self
93 }
94}
95
96impl SeverityLevel {
97 pub fn as_str(&self) -> &'static str {
99 match self {
100 Self::Trace => "TRACE",
101 Self::Debug => "DEBUG",
102 Self::Info => "INFO",
103 Self::Warn => "WARN",
104 Self::Error => "ERROR",
105 Self::Fatal => "FATAL",
106 }
107 }
108
109 pub fn from_i32(value: i32) -> Option<Self> {
111 match value {
112 1 => Some(Self::Trace),
113 5 => Some(Self::Debug),
114 9 => Some(Self::Info),
115 13 => Some(Self::Warn),
116 17 => Some(Self::Error),
117 21 => Some(Self::Fatal),
118 _ => None,
119 }
120 }
121
122 pub fn to_i32(self) -> i32 {
124 self as i32
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn test_log_record_creation() {
134 let log = LogRecord::new(SeverityLevel::Info, "Application started", 1234567890);
135 assert_eq!(log.severity, SeverityLevel::Info);
136 assert_eq!(log.body, "Application started");
137 assert_eq!(log.timestamp, 1234567890);
138 }
139
140 #[test]
141 fn test_log_with_attributes() {
142 let log = LogRecord::new(SeverityLevel::Error, "Connection failed", 1234567890)
143 .with_attribute("error.type", "NetworkError")
144 .with_attribute("retry.count", "3");
145
146 assert_eq!(log.attributes.len(), 2);
147 assert_eq!(
148 log.attributes.get("error.type"),
149 Some(&"NetworkError".to_string())
150 );
151 }
152
153 #[test]
154 fn test_log_with_trace_context() {
155 let log = LogRecord::new(SeverityLevel::Debug, "Processing request", 1234567890)
156 .with_trace_context("trace123".to_string(), "span456".to_string());
157
158 assert_eq!(log.trace_id, Some("trace123".to_string()));
159 assert_eq!(log.span_id, Some("span456".to_string()));
160 }
161
162 #[test]
163 fn test_severity_ordering() {
164 assert!(SeverityLevel::Trace < SeverityLevel::Debug);
165 assert!(SeverityLevel::Debug < SeverityLevel::Info);
166 assert!(SeverityLevel::Info < SeverityLevel::Warn);
167 assert!(SeverityLevel::Warn < SeverityLevel::Error);
168 assert!(SeverityLevel::Error < SeverityLevel::Fatal);
169 }
170
171 #[test]
172 fn test_severity_as_str() {
173 assert_eq!(SeverityLevel::Info.as_str(), "INFO");
174 assert_eq!(SeverityLevel::Error.as_str(), "ERROR");
175 }
176}