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