Skip to main content

systemprompt_logging/repository/
mod.rs

1#![allow(clippy::print_stdout)]
2
3use std::sync::Arc;
4
5use chrono::{DateTime, Utc};
6use sqlx::PgPool;
7use systemprompt_database::DbPool;
8use systemprompt_identifiers::LogId;
9
10use crate::models::{LogEntry, LogFilter, LogLevel, LoggingError};
11
12pub mod analytics;
13mod operations;
14
15pub use analytics::{AnalyticsEvent, AnalyticsRepository};
16
17#[derive(Clone, Debug)]
18pub struct LoggingRepository {
19    pool: Arc<PgPool>,
20    write_pool: Arc<PgPool>,
21    terminal_output: bool,
22    db_output: bool,
23}
24
25impl LoggingRepository {
26    pub fn new(db: &DbPool) -> anyhow::Result<Self> {
27        let pool = db.pool_arc()?;
28        let write_pool = db.write_pool_arc()?;
29        Ok(Self {
30            pool,
31            write_pool,
32            terminal_output: true,
33            db_output: false,
34        })
35    }
36
37    #[must_use]
38    pub const fn with_terminal(mut self, enabled: bool) -> Self {
39        self.terminal_output = enabled;
40        self
41    }
42
43    #[must_use]
44    pub const fn with_database(mut self, enabled: bool) -> Self {
45        self.db_output = enabled;
46        self
47    }
48
49    pub async fn log(&self, entry: LogEntry) -> Result<(), LoggingError> {
50        entry.validate()?;
51
52        if self.terminal_output {
53            println!("{entry}");
54        }
55
56        if self.db_output {
57            operations::create_log(&self.write_pool, &entry).await?;
58        }
59
60        Ok(())
61    }
62
63    pub async fn log_message(
64        &self,
65        level: LogLevel,
66        module: &str,
67        message: &str,
68    ) -> Result<(), LoggingError> {
69        let entry = LogEntry::new(level, module, message);
70        self.log(entry).await
71    }
72
73    pub async fn log_message_with_metadata(
74        &self,
75        level: LogLevel,
76        module: &str,
77        message: &str,
78        metadata: serde_json::Value,
79    ) -> Result<(), LoggingError> {
80        let entry = LogEntry::new(level, module, message).with_metadata(metadata);
81        self.log(entry).await
82    }
83
84    pub async fn error(&self, module: &str, message: &str) -> Result<(), LoggingError> {
85        self.log_message(LogLevel::Error, module, message).await
86    }
87
88    pub async fn warn(&self, module: &str, message: &str) -> Result<(), LoggingError> {
89        self.log_message(LogLevel::Warn, module, message).await
90    }
91
92    pub async fn info(&self, module: &str, message: &str) -> Result<(), LoggingError> {
93        self.log_message(LogLevel::Info, module, message).await
94    }
95
96    pub async fn debug(&self, module: &str, message: &str) -> Result<(), LoggingError> {
97        self.log_message(LogLevel::Debug, module, message).await
98    }
99
100    pub async fn trace(&self, module: &str, message: &str) -> Result<(), LoggingError> {
101        self.log_message(LogLevel::Trace, module, message).await
102    }
103
104    pub async fn get_recent_logs(&self, limit: i64) -> Result<Vec<LogEntry>, LoggingError> {
105        operations::list_logs(&self.pool, limit).await
106    }
107
108    pub async fn get_logs_by_module_patterns(
109        &self,
110        patterns: &[String],
111        limit: i64,
112    ) -> Result<Vec<LogEntry>, LoggingError> {
113        operations::list_logs_by_module_patterns(&self.pool, patterns, limit).await
114    }
115
116    pub async fn cleanup_old_logs(&self, older_than: DateTime<Utc>) -> Result<u64, LoggingError> {
117        operations::cleanup_logs_before(&self.write_pool, older_than).await
118    }
119
120    pub async fn count_logs_before(&self, cutoff: DateTime<Utc>) -> Result<u64, LoggingError> {
121        operations::count_logs_before(&self.pool, cutoff).await
122    }
123
124    pub async fn clear_all_logs(&self) -> Result<u64, LoggingError> {
125        operations::clear_all_logs(&self.write_pool).await
126    }
127
128    pub async fn get_logs_paginated(
129        &self,
130        filter: &LogFilter,
131    ) -> Result<(Vec<LogEntry>, i64), LoggingError> {
132        operations::list_logs_paginated(&self.pool, filter).await
133    }
134
135    pub async fn get_by_id(&self, id: &LogId) -> Result<Option<LogEntry>, LoggingError> {
136        operations::get_log(&self.pool, id).await
137    }
138
139    pub async fn update_log_entry(
140        &self,
141        id: &LogId,
142        entry: &LogEntry,
143    ) -> Result<bool, LoggingError> {
144        operations::update_log(&self.write_pool, id, entry).await
145    }
146
147    pub async fn delete_log_entry(&self, id: &LogId) -> Result<bool, LoggingError> {
148        operations::delete_log(&self.write_pool, id).await
149    }
150
151    pub async fn delete_log_entries(&self, ids: &[LogId]) -> Result<u64, LoggingError> {
152        operations::delete_logs_multiple(&self.write_pool, ids).await
153    }
154}