systemprompt_logging/
lib.rs1pub mod attribution;
26pub mod extension;
27pub mod layer;
28pub mod models;
29pub mod repository;
30pub mod services;
31pub mod trace;
32
33pub use attribution::{LogAttributionUnset, install_log_attribution, platform_attribution};
34pub use extension::LoggingExtension;
35
36pub use layer::DatabaseLayer;
37pub use models::{LogActor, LogEntry, LogFilter, LogLevel};
38pub use repository::{AnalyticsEvent, AnalyticsRepository, LoggingRepository};
39#[cfg(feature = "cli")]
40pub use services::CliService;
41pub use services::{
42 DatabaseLogService, FilterSystemFields, LoggingMaintenanceService, RequestSpan,
43 RequestSpanBuilder, SystemSpan, is_startup_mode, publish_log, set_log_publisher,
44 set_startup_mode,
45};
46pub use trace::{
47 AiRequestDetail, AiRequestFilter, AiRequestInfo, AiRequestListItem, AiRequestStats,
48 AiRequestSummary, AiTraceService, AuditLookupResult, AuditToolCallRow, ConversationMessage,
49 ExecutionStep, ExecutionStepSummary, LevelCount, LinkedMcpCall, LogSearchFilter, LogSearchItem,
50 LogTimeRange, McpExecutionSummary, McpToolExecution, ModelStatsRow, ModuleCount,
51 ProviderStatsRow, TaskArtifact, TaskInfo, ToolExecutionFilter, ToolExecutionItem, ToolLogEntry,
52 TraceEvent, TraceListFilter, TraceListItem, TraceQueryService,
53};
54
55use std::sync::OnceLock;
56
57use layer::ProxyDatabaseLayer;
58use systemprompt_database::DbPool;
59use tracing_subscriber::layer::SubscriberExt;
60use tracing_subscriber::util::SubscriberInitExt;
61use tracing_subscriber::{EnvFilter, Layer};
62
63static SUBSCRIBER_INITIALIZED: OnceLock<()> = OnceLock::new();
64static DB_PROXY: OnceLock<ProxyDatabaseLayer> = OnceLock::new();
65
66const NOISE_FILTERS: &[&str] = &[
67 "tokio_cron_scheduler=warn",
68 "sqlx::postgres::notice=warn",
69 "sqlx::query=warn",
70 "handlebars=warn",
71 "systemprompt_database::lifecycle=info",
72 "systemprompt_templates=info",
73 "systemprompt_extension::registry=info",
74 "systemprompt_api::services::middleware::session=info",
75 "rmcp=warn",
76 "rmcp::transport=warn",
77];
78
79fn build_filter(base: &str) -> EnvFilter {
80 let filter_str = std::iter::once(base.to_owned())
81 .chain(NOISE_FILTERS.iter().map(ToString::to_string))
82 .collect::<Vec<_>>()
83 .join(",");
84 EnvFilter::new(filter_str)
85}
86
87fn ensure_subscriber(level_override: Option<&str>) {
88 if SUBSCRIBER_INITIALIZED.set(()).is_err() {
89 return;
90 }
91
92 let console_filter = level_override.map_or_else(
93 || {
94 if is_startup_mode() {
95 EnvFilter::new("warn")
96 } else {
97 EnvFilter::try_from_default_env().unwrap_or_else(|_| build_filter("info"))
98 }
99 },
100 |level| EnvFilter::try_from_default_env().unwrap_or_else(|_| build_filter(level)),
101 );
102
103 let fmt_layer = tracing_subscriber::fmt::layer()
104 .fmt_fields(FilterSystemFields::new())
105 .with_target(true)
106 .with_writer(std::io::stderr)
107 .with_filter(console_filter);
108
109 let proxy = DB_PROXY.get_or_init(ProxyDatabaseLayer::new).clone();
110 let db_layer = proxy.with_filter(build_filter("info"));
111
112 tracing_subscriber::registry()
113 .with(fmt_layer)
114 .with(db_layer)
115 .init();
116}
117
118pub fn init_logging(db_pool: DbPool) {
119 ensure_subscriber(None);
120
121 let proxy = DB_PROXY.get_or_init(ProxyDatabaseLayer::new);
122 proxy.attach(db_pool);
123}
124
125pub fn init_console_logging() {
126 init_console_logging_with_level(None);
127}
128
129pub fn init_console_logging_with_level(level: Option<&str>) {
130 ensure_subscriber(level);
131}