Skip to main content

systemprompt_logging/
attribution.rs

1//! Logging-side attribution cell.
2//!
3//! `tracing` macros fire from contexts where no `AppContext` handle is in
4//! scope (gateway access logs, OTLP ingest, panic hooks). Threading a
5//! resolved owner through every `info!()` call site is impractical, so the
6//! platform parks the resolved [`SystemAdmin`] in a logging-private
7//! `OnceLock` during runtime bootstrap. Only [`platform_attribution`] reads it.
8//!
9//! This is the *only* legitimate global owned by the logging crate. Other
10//! subsystems (MCP registry, scheduler) thread their owner explicitly through
11//! `AppContext` instead of consulting a process-wide cell.
12
13use std::sync::OnceLock;
14use systemprompt_identifiers::UserId;
15use systemprompt_models::services::SystemAdmin;
16use thiserror::Error;
17
18static PLATFORM_OWNER: OnceLock<SystemAdmin> = OnceLock::new();
19
20/// On a repeat call the argument is dropped; the first-installed value is
21/// returned.
22pub fn install_log_attribution(admin: SystemAdmin) -> &'static SystemAdmin {
23    PLATFORM_OWNER.get_or_init(|| admin)
24}
25
26pub fn platform_attribution() -> Result<&'static SystemAdmin, LogAttributionUnset> {
27    PLATFORM_OWNER.get().ok_or(LogAttributionUnset)
28}
29
30pub(crate) fn platform_owner_id() -> Result<&'static UserId, LogAttributionUnset> {
31    platform_attribution().map(SystemAdmin::id)
32}
33
34#[derive(Debug, Clone, Copy, Error)]
35#[error("log attribution not installed: AppContext bootstrap must run before platform log events")]
36pub struct LogAttributionUnset;