fraiseql_server/observability/
logging.rs1use std::collections::HashMap;
6
7pub fn init_logging() -> Result<(), Box<dyn std::error::Error>> {
9 Ok(())
12}
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
16pub enum LogLevel {
17 Debug = 0,
19 Info = 1,
21 Warn = 2,
23 Error = 3,
25}
26
27impl LogLevel {
28 pub fn as_str(&self) -> &'static str {
30 match self {
31 Self::Debug => "DEBUG",
32 Self::Info => "INFO",
33 Self::Warn => "WARN",
34 Self::Error => "ERROR",
35 }
36 }
37}
38
39#[derive(Clone, Debug)]
41pub struct LogEntry {
42 pub timestamp: String,
44 pub level: LogLevel,
46 pub message: String,
48 pub trace_id: Option<String>,
50 pub span_id: Option<String>,
52 pub fields: HashMap<String, String>,
54}
55
56impl LogEntry {
57 pub fn new(level: LogLevel, message: impl Into<String>) -> Self {
59 Self {
60 timestamp: chrono::Utc::now().to_rfc3339(),
61 level,
62 message: message.into(),
63 trace_id: None,
64 span_id: None,
65 fields: HashMap::new(),
66 }
67 }
68
69 pub fn with_trace_id(mut self, trace_id: impl Into<String>) -> Self {
71 self.trace_id = Some(trace_id.into());
72 self
73 }
74
75 pub fn with_span_id(mut self, span_id: impl Into<String>) -> Self {
77 self.span_id = Some(span_id.into());
78 self
79 }
80
81 pub fn with_field(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
83 self.fields.insert(key.into(), value.into());
84 self
85 }
86
87 pub fn as_json(&self) -> Result<serde_json::Value, serde_json::Error> {
89 let mut json = serde_json::json!({
90 "timestamp": self.timestamp,
91 "level": self.level.as_str(),
92 "message": self.message,
93 });
94
95 if let Some(ref trace_id) = self.trace_id {
96 json["trace_id"] = serde_json::Value::String(trace_id.clone());
97 }
98
99 if let Some(ref span_id) = self.span_id {
100 json["span_id"] = serde_json::Value::String(span_id.clone());
101 }
102
103 for (key, value) in &self.fields {
104 json[key] = serde_json::Value::String(value.clone());
105 }
106
107 Ok(json)
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn test_log_entry() {
117 let entry = LogEntry::new(LogLevel::Info, "Test message")
118 .with_trace_id("trace-123")
119 .with_span_id("span-456")
120 .with_field("user_id", "user-123");
121
122 assert_eq!(entry.level, LogLevel::Info);
123 assert_eq!(entry.message, "Test message");
124 assert_eq!(entry.trace_id, Some("trace-123".to_string()));
125 assert_eq!(entry.fields.len(), 1);
126 }
127
128 #[test]
129 fn test_log_levels() {
130 assert!(LogLevel::Debug < LogLevel::Info);
131 assert!(LogLevel::Info < LogLevel::Warn);
132 assert!(LogLevel::Warn < LogLevel::Error);
133 }
134}