systemprompt_logging/models/
log_entry.rs1use 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}