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