use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LogRecord {
pub timestamp: i64,
pub observed_timestamp: Option<i64>,
pub severity: SeverityLevel,
pub severity_text: Option<String>,
pub body: String,
pub attributes: HashMap<String, String>,
pub trace_id: Option<String>,
pub span_id: Option<String>,
pub resource: Option<super::Resource>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[repr(u8)]
pub enum SeverityLevel {
Trace = 1,
Debug = 5,
Info = 9,
Warn = 13,
Error = 17,
Fatal = 21,
}
impl LogRecord {
pub fn new(severity: SeverityLevel, body: impl Into<String>, timestamp: i64) -> Self {
Self {
timestamp,
observed_timestamp: None,
severity,
severity_text: None,
body: body.into(),
attributes: HashMap::new(),
trace_id: None,
span_id: None,
resource: None,
}
}
pub fn with_attribute(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.attributes.insert(key.into(), value.into());
self
}
pub fn with_trace_context(mut self, trace_id: String, span_id: String) -> Self {
self.trace_id = Some(trace_id);
self.span_id = Some(span_id);
self
}
pub fn with_resource(mut self, resource: super::Resource) -> Self {
self.resource = Some(resource);
self
}
}
impl SeverityLevel {
pub fn as_str(&self) -> &'static str {
match self {
Self::Trace => "TRACE",
Self::Debug => "DEBUG",
Self::Info => "INFO",
Self::Warn => "WARN",
Self::Error => "ERROR",
Self::Fatal => "FATAL",
}
}
pub fn from_i32(value: i32) -> Option<Self> {
match value {
1 => Some(Self::Trace),
5 => Some(Self::Debug),
9 => Some(Self::Info),
13 => Some(Self::Warn),
17 => Some(Self::Error),
21 => Some(Self::Fatal),
_ => None,
}
}
pub fn to_i32(self) -> i32 {
self as i32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_log_record_creation() {
let log = LogRecord::new(SeverityLevel::Info, "Application started", 1234567890);
assert_eq!(log.severity, SeverityLevel::Info);
assert_eq!(log.body, "Application started");
assert_eq!(log.timestamp, 1234567890);
}
#[test]
fn test_log_with_attributes() {
let log = LogRecord::new(SeverityLevel::Error, "Connection failed", 1234567890)
.with_attribute("error.type", "NetworkError")
.with_attribute("retry.count", "3");
assert_eq!(log.attributes.len(), 2);
assert_eq!(
log.attributes.get("error.type"),
Some(&"NetworkError".to_string())
);
}
#[test]
fn test_log_with_trace_context() {
let log = LogRecord::new(SeverityLevel::Debug, "Processing request", 1234567890)
.with_trace_context("trace123".to_string(), "span456".to_string());
assert_eq!(log.trace_id, Some("trace123".to_string()));
assert_eq!(log.span_id, Some("span456".to_string()));
}
#[test]
fn test_severity_ordering() {
assert!(SeverityLevel::Trace < SeverityLevel::Debug);
assert!(SeverityLevel::Debug < SeverityLevel::Info);
assert!(SeverityLevel::Info < SeverityLevel::Warn);
assert!(SeverityLevel::Warn < SeverityLevel::Error);
assert!(SeverityLevel::Error < SeverityLevel::Fatal);
}
#[test]
fn test_severity_as_str() {
assert_eq!(SeverityLevel::Info.as_str(), "INFO");
assert_eq!(SeverityLevel::Error.as_str(), "ERROR");
}
}