1use serde::{Deserialize, Serialize};
4use std::fmt;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
8#[serde(rename_all = "lowercase")]
9pub enum Level {
10 Trace,
11 Debug,
12 Info,
13 Warn,
14 Error,
15}
16
17impl Level {
18 pub fn to_tracing_level(&self) -> tracing::Level {
20 match self {
21 Level::Trace => tracing::Level::TRACE,
22 Level::Debug => tracing::Level::DEBUG,
23 Level::Info => tracing::Level::INFO,
24 Level::Warn => tracing::Level::WARN,
25 Level::Error => tracing::Level::ERROR,
26 }
27 }
28}
29
30impl fmt::Display for Level {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 match self {
33 Level::Trace => write!(f, "trace"),
34 Level::Debug => write!(f, "debug"),
35 Level::Info => write!(f, "info"),
36 Level::Warn => write!(f, "warn"),
37 Level::Error => write!(f, "error"),
38 }
39 }
40}
41
42pub struct StandardFields;
44
45impl StandardFields {
46 pub const SERVICE: &'static str = "service";
48 pub const MODULE: &'static str = "module";
50 pub const LEVEL: &'static str = "level";
52 pub const MESSAGE: &'static str = "message";
54 pub const TIMESTAMP: &'static str = "timestamp";
56 pub const ERROR: &'static str = "error";
58 pub const ERROR_TYPE: &'static str = "error_type";
60 pub const ERROR_MESSAGE: &'static str = "error_message";
62 pub const ERROR_STACK: &'static str = "error_stack";
64 pub const TRACE_ID: &'static str = "trace_id";
66 pub const SPAN_ID: &'static str = "span_id";
68 pub const REQUEST_ID: &'static str = "request_id";
70 pub const USER_ID: &'static str = "user_id";
72 pub const TENANT_ID: &'static str = "tenant_id";
74 pub const HTTP_METHOD: &'static str = "http.method";
76 pub const HTTP_PATH: &'static str = "http.path";
78 pub const HTTP_STATUS: &'static str = "http.status";
80 pub const HTTP_DURATION_MS: &'static str = "http.duration_ms";
82 pub const CLIENT_IP: &'static str = "client.ip";
84 pub const USER_AGENT: &'static str = "user_agent";
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct Event {
91 #[serde(rename = "service")]
93 pub service: String,
94 #[serde(rename = "module")]
96 pub module: Option<String>,
97 #[serde(rename = "level")]
99 pub level: Level,
100 #[serde(rename = "message")]
102 pub message: String,
103 #[serde(rename = "timestamp")]
105 pub timestamp: String,
106 #[serde(flatten)]
108 pub fields: serde_json::Value,
109}
110
111impl Event {
112 pub fn new(
114 service: impl Into<String>,
115 module: Option<String>,
116 level: Level,
117 message: impl Into<String>,
118 ) -> Self {
119 Self {
120 service: service.into(),
121 module,
122 level,
123 message: message.into(),
124 timestamp: chrono::Utc::now().to_rfc3339(),
125 fields: serde_json::Value::Object(serde_json::Map::new()),
126 }
127 }
128
129 pub fn format_error(error: &dyn std::error::Error) -> serde_json::Value {
131 let mut error_obj = serde_json::Map::new();
132 error_obj.insert(
133 StandardFields::ERROR_TYPE.to_string(),
134 serde_json::Value::String(std::any::type_name_of_val(error).to_string()),
135 );
136 error_obj.insert(
137 StandardFields::ERROR_MESSAGE.to_string(),
138 serde_json::Value::String(error.to_string()),
139 );
140
141 let mut source_chain = Vec::new();
143 let mut current: Option<&dyn std::error::Error> = Some(error);
144 while let Some(err) = current {
145 source_chain.push(err.to_string());
146 current = err.source();
147 }
148
149 if source_chain.len() > 1 {
150 error_obj.insert(
151 StandardFields::ERROR_STACK.to_string(),
152 serde_json::Value::Array(
153 source_chain
154 .into_iter()
155 .map(serde_json::Value::String)
156 .collect(),
157 ),
158 );
159 }
160
161 serde_json::Value::Object(error_obj)
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn test_level_display() {
171 assert_eq!(Level::Info.to_string(), "info");
172 assert_eq!(Level::Error.to_string(), "error");
173 }
174
175 #[test]
176 fn test_event_creation() {
177 let event = Event::new("my-service", Some("my-module".to_string()), Level::Info, "test");
178 assert_eq!(event.service, "my-service");
179 assert_eq!(event.module, Some("my-module".to_string()));
180 assert_eq!(event.level, Level::Info);
181 assert_eq!(event.message, "test");
182 }
183}
184