Skip to main content

systemprompt_logging/models/
log_entry.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use systemprompt_identifiers::LogId;
4
5use super::{LogLevel, LoggingError};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct LogEntry {
9    pub id: LogId,
10    pub timestamp: DateTime<Utc>,
11    pub level: LogLevel,
12    pub module: String,
13    pub message: String,
14    pub metadata: Option<serde_json::Value>,
15    pub user_id: systemprompt_identifiers::UserId,
16    pub session_id: systemprompt_identifiers::SessionId,
17    pub task_id: Option<systemprompt_identifiers::TaskId>,
18    pub trace_id: systemprompt_identifiers::TraceId,
19    pub context_id: Option<systemprompt_identifiers::ContextId>,
20    pub client_id: Option<systemprompt_identifiers::ClientId>,
21}
22
23impl LogEntry {
24    pub fn new(level: LogLevel, module: impl Into<String>, message: impl Into<String>) -> Self {
25        Self {
26            id: LogId::generate(),
27            timestamp: Utc::now(),
28            level,
29            module: module.into(),
30            message: message.into(),
31            metadata: None,
32            user_id: systemprompt_identifiers::UserId::system(),
33            session_id: systemprompt_identifiers::SessionId::system(),
34            task_id: None,
35            trace_id: systemprompt_identifiers::TraceId::system(),
36            context_id: None,
37            client_id: None,
38        }
39    }
40
41    #[must_use]
42    pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
43        self.metadata = Some(metadata);
44        self
45    }
46
47    #[must_use]
48    pub fn with_user_id(mut self, user_id: systemprompt_identifiers::UserId) -> Self {
49        self.user_id = user_id;
50        self
51    }
52
53    #[must_use]
54    pub fn with_session_id(mut self, session_id: systemprompt_identifiers::SessionId) -> Self {
55        self.session_id = session_id;
56        self
57    }
58
59    #[must_use]
60    pub fn with_task_id(mut self, task_id: systemprompt_identifiers::TaskId) -> Self {
61        self.task_id = Some(task_id);
62        self
63    }
64
65    #[must_use]
66    pub fn with_trace_id(mut self, trace_id: systemprompt_identifiers::TraceId) -> Self {
67        self.trace_id = trace_id;
68        self
69    }
70
71    #[must_use]
72    pub fn with_context_id(mut self, context_id: systemprompt_identifiers::ContextId) -> Self {
73        self.context_id = Some(context_id);
74        self
75    }
76
77    #[must_use]
78    pub fn with_client_id(mut self, client_id: systemprompt_identifiers::ClientId) -> Self {
79        self.client_id = Some(client_id);
80        self
81    }
82
83    pub fn validate(&self) -> Result<(), LoggingError> {
84        if self.module.is_empty() {
85            return Err(LoggingError::EmptyModuleName);
86        }
87        if self.message.is_empty() {
88            return Err(LoggingError::EmptyMessage);
89        }
90        if let Some(metadata) = &self.metadata {
91            if !metadata.is_object()
92                && !metadata.is_array()
93                && !metadata.is_string()
94                && !metadata.is_null()
95            {
96                return Err(LoggingError::InvalidMetadata);
97            }
98        }
99        Ok(())
100    }
101}
102
103impl std::fmt::Display for LogEntry {
104    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        let level_str = match self.level {
106            LogLevel::Error => "ERROR",
107            LogLevel::Warn => "WARN ",
108            LogLevel::Info => "INFO ",
109            LogLevel::Debug => "DEBUG",
110            LogLevel::Trace => "TRACE",
111        };
112
113        let timestamp_str = self.timestamp.format("%H:%M:%S");
114
115        if let Some(metadata) = &self.metadata {
116            write!(
117                f,
118                "{} [{}] {}: {} {}",
119                timestamp_str,
120                level_str,
121                self.module,
122                self.message,
123                serde_json::to_string(metadata).unwrap_or_default()
124            )
125        } else {
126            write!(
127                f,
128                "{} [{}] {}: {}",
129                timestamp_str, level_str, self.module, self.message
130            )
131        }
132    }
133}