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