Skip to main content

systemprompt_logging/repository/
mod.rs

1use std::io::Write;
2use std::sync::Arc;
3
4use chrono::{DateTime, Utc};
5use sqlx::PgPool;
6use systemprompt_database::DbPool;
7use systemprompt_identifiers::LogId;
8
9use crate::models::{LogEntry, LogFilter, LoggingError};
10
11pub mod analytics;
12mod operations;
13
14pub use analytics::{AnalyticsEvent, AnalyticsRepository};
15
16#[derive(Clone, Debug)]
17pub struct LoggingRepository {
18    pool: Arc<PgPool>,
19    write_pool: Arc<PgPool>,
20    terminal_output: bool,
21    db_output: bool,
22}
23
24impl LoggingRepository {
25    pub fn new(db: &DbPool) -> Result<Self, LoggingError> {
26        let pool = db.pool_arc()?;
27        let write_pool = db.write_pool_arc()?;
28        Ok(Self {
29            pool,
30            write_pool,
31            terminal_output: true,
32            db_output: false,
33        })
34    }
35
36    #[must_use]
37    pub const fn with_terminal(mut self, enabled: bool) -> Self {
38        self.terminal_output = enabled;
39        self
40    }
41
42    #[must_use]
43    pub const fn with_database(mut self, enabled: bool) -> Self {
44        self.db_output = enabled;
45        self
46    }
47
48    pub async fn log(&self, entry: LogEntry) -> Result<(), LoggingError> {
49        entry.validate()?;
50
51        if self.terminal_output {
52            let mut stdout = std::io::stdout();
53            writeln!(stdout, "{entry}").ok();
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 get_recent_logs(&self, limit: i64) -> Result<Vec<LogEntry>, LoggingError> {
64        operations::list_logs(&self.pool, limit).await
65    }
66
67    pub async fn get_logs_by_module_patterns(
68        &self,
69        patterns: &[String],
70        limit: i64,
71    ) -> Result<Vec<LogEntry>, LoggingError> {
72        operations::list_logs_by_module_patterns(&self.pool, patterns, limit).await
73    }
74
75    pub async fn cleanup_old_logs(&self, older_than: DateTime<Utc>) -> Result<u64, LoggingError> {
76        operations::cleanup_logs_before(&self.write_pool, older_than).await
77    }
78
79    pub async fn count_logs_before(&self, cutoff: DateTime<Utc>) -> Result<u64, LoggingError> {
80        operations::count_logs_before(&self.pool, cutoff).await
81    }
82
83    pub async fn clear_all_logs(&self) -> Result<u64, LoggingError> {
84        operations::clear_all_logs(&self.write_pool).await
85    }
86
87    pub async fn get_logs_paginated(
88        &self,
89        filter: &LogFilter,
90    ) -> Result<(Vec<LogEntry>, i64), LoggingError> {
91        operations::list_logs_paginated(&self.pool, filter).await
92    }
93
94    pub async fn get_by_id(&self, id: &LogId) -> Result<Option<LogEntry>, LoggingError> {
95        operations::get_log(&self.pool, id).await
96    }
97
98    pub async fn update_log_entry(
99        &self,
100        id: &LogId,
101        entry: &LogEntry,
102    ) -> Result<bool, LoggingError> {
103        operations::update_log(&self.write_pool, id, entry).await
104    }
105
106    pub async fn delete_log_entry(&self, id: &LogId) -> Result<bool, LoggingError> {
107        operations::delete_log(&self.write_pool, id).await
108    }
109
110    pub async fn delete_log_entries(&self, ids: &[LogId]) -> Result<u64, LoggingError> {
111        operations::delete_logs_multiple(&self.write_pool, ids).await
112    }
113}