systemprompt-logging 0.63.1

Tracing and audit infrastructure for systemprompt.io AI governance. Structured events, five-point audit traces, and SIEM-ready JSON output — part of the MCP governance pipeline.
Documentation
//! Log persistence repository.
//!
//! [`LoggingRepository`] persists entries to the `logs` table and serves
//! paginated reads, lookups, and age-based cleanup;
//! [`AnalyticsRepository`] records analytics events. Read and write pools are
//! held separately so reads never contend with the write path.
//!
//! Copyright (c) systemprompt.io — Business Source License 1.1.
//! See <https://systemprompt.io> for licensing details.

use std::sync::Arc;

use chrono::{DateTime, Utc};
use sqlx::PgPool;
use systemprompt_database::DbPool;
use systemprompt_identifiers::{LogId, UserId};

use crate::models::{LogEntry, LogFilter, LoggingError};

pub mod analytics;
mod operations;
mod ownership;

pub use analytics::{AnalyticsEvent, AnalyticsRepository};
pub use ownership::LoggingOwnerReassignment;

#[derive(Clone, Debug)]
pub struct LoggingRepository {
    pool: Arc<PgPool>,
    write_pool: Arc<PgPool>,
}

impl LoggingRepository {
    pub fn new(db: &DbPool) -> Self {
        let pool = db.pool();
        let write_pool = db.write_pool();
        Self { pool, write_pool }
    }

    pub async fn log(&self, entry: LogEntry) -> Result<(), LoggingError> {
        entry.validate()?;
        operations::create_log(&self.write_pool, &entry).await
    }

    pub async fn insert_batch(&self, entries: &[LogEntry]) -> Result<(), LoggingError> {
        operations::insert_log_batch(&self.write_pool, entries).await
    }

    pub async fn get_recent_logs(&self, limit: i64) -> Result<Vec<LogEntry>, LoggingError> {
        operations::list_logs(&self.pool, limit).await
    }

    pub async fn get_logs_by_module_patterns(
        &self,
        patterns: &[String],
        limit: i64,
    ) -> Result<Vec<LogEntry>, LoggingError> {
        operations::list_logs_by_module_patterns(&self.pool, patterns, limit).await
    }

    pub async fn cleanup_old_logs(&self, older_than: DateTime<Utc>) -> Result<u64, LoggingError> {
        operations::cleanup_logs_before(&self.write_pool, older_than).await
    }

    pub async fn count_logs_before(&self, cutoff: DateTime<Utc>) -> Result<u64, LoggingError> {
        operations::count_logs_before(&self.pool, cutoff).await
    }

    pub async fn distinct_log_user_ids(&self) -> Result<Vec<UserId>, LoggingError> {
        operations::distinct_log_user_ids(&self.pool).await
    }

    pub async fn delete_logs_for_users(&self, user_ids: &[UserId]) -> Result<u64, LoggingError> {
        operations::delete_logs_for_users(&self.write_pool, user_ids).await
    }

    pub async fn count_logs_for_users(&self, user_ids: &[UserId]) -> Result<u64, LoggingError> {
        operations::count_logs_for_users(&self.pool, user_ids).await
    }

    pub async fn clear_all_logs(&self) -> Result<u64, LoggingError> {
        operations::clear_all_logs(&self.write_pool).await
    }

    pub async fn get_logs_paginated(
        &self,
        filter: &LogFilter,
    ) -> Result<(Vec<LogEntry>, i64), LoggingError> {
        operations::list_logs_paginated(&self.pool, filter).await
    }

    pub async fn get_by_id(&self, id: &LogId) -> Result<Option<LogEntry>, LoggingError> {
        operations::get_log(&self.pool, id).await
    }

    pub async fn update_log_entry(
        &self,
        id: &LogId,
        entry: &LogEntry,
    ) -> Result<bool, LoggingError> {
        operations::update_log(&self.write_pool, id, entry).await
    }

    pub async fn delete_log_entry(&self, id: &LogId) -> Result<bool, LoggingError> {
        operations::delete_log(&self.write_pool, id).await
    }

    pub async fn delete_log_entries(&self, ids: &[LogId]) -> Result<u64, LoggingError> {
        operations::delete_logs_multiple(&self.write_pool, ids).await
    }
}