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//!
13//! Copyright (c) systemprompt.io — Business Source License 1.1.
14//! See <https://systemprompt.io> for licensing details.
15
16use std::sync::OnceLock;
17use systemprompt_identifiers::UserId;
18use systemprompt_models::services::SystemAdmin;
19use thiserror::Error;
20
21static PLATFORM_OWNER: OnceLock<SystemAdmin> = OnceLock::new();
22
23/// On a repeat call the argument is dropped; the first-installed value is
24/// returned.
25pub fn install_log_attribution(admin: SystemAdmin) -> &'static SystemAdmin {
26 PLATFORM_OWNER.get_or_init(|| admin)
27}
28
29pub fn platform_attribution() -> Result<&'static SystemAdmin, LogAttributionUnset> {
30 PLATFORM_OWNER.get().ok_or(LogAttributionUnset)
31}
32
33pub(crate) fn platform_owner_id() -> Result<&'static UserId, LogAttributionUnset> {
34 platform_attribution().map(SystemAdmin::id)
35}
36
37#[derive(Debug, Clone, Copy, Error)]
38#[error("log attribution not installed: AppContext bootstrap must run before platform log events")]
39pub struct LogAttributionUnset;