Skip to main content

systemprompt_logging/
runtime.rs

1//! Owned handles to the logging pipeline.
2//!
3//! [`LogSink`] is what [`crate::init_logging`] and the console initialisers
4//! return: a handle onto the database layer inside the installed `tracing`
5//! subscriber. It enqueues entries persisted outside a `tracing` call, reports
6//! the drop count, and flushes the writer on shutdown. [`LoggingRuntime`]
7//! pairs a sink with the resolved platform owner, so originator-less telemetry
8//! is attributed without a process-wide cell. The runtime builds it once at
9//! bootstrap and stores it on `AppContext`; code with no context in scope is
10//! handed the value through its constructor.
11//!
12//! Copyright (c) systemprompt.io — Business Source License 1.1.
13//! See <https://systemprompt.io> for licensing details.
14
15use std::sync::Arc;
16
17use systemprompt_database::DbPool;
18use systemprompt_identifiers::{InstanceId, TraceId};
19use systemprompt_manifest::services::SystemAdmin;
20
21use crate::layer::{LogWriterShutdownError, ProxyDatabaseLayer};
22use crate::models::{LogActor, LogEntry};
23
24/// Handle onto the database sink of the installed subscriber; clones share it.
25#[derive(Debug, Clone, Default)]
26pub struct LogSink {
27    proxy: ProxyDatabaseLayer,
28}
29
30impl LogSink {
31    pub(crate) const fn from_proxy(proxy: ProxyDatabaseLayer) -> Self {
32        Self { proxy }
33    }
34
35    pub(crate) fn attach(&self, db_pool: &DbPool, instance_id: Option<InstanceId>) {
36        self.proxy.attach_owned(db_pool, instance_id);
37    }
38
39    pub fn enqueue(&self, entry: LogEntry) {
40        self.proxy.enqueue(entry);
41    }
42
43    #[must_use]
44    pub fn dropped_entries(&self) -> u64 {
45        self.proxy.shared().dropped()
46    }
47
48    #[must_use]
49    pub fn instance_id(&self) -> Option<&InstanceId> {
50        self.proxy.shared().instance.get()
51    }
52
53    pub async fn shutdown(&self) -> Result<(), LogWriterShutdownError> {
54        self.proxy.shutdown().await
55    }
56}
57
58/// The process's logging handle: the sink plus the platform owner.
59#[derive(Debug, Clone)]
60pub struct LoggingRuntime {
61    sink: LogSink,
62    platform_owner: Arc<SystemAdmin>,
63}
64
65impl LoggingRuntime {
66    #[must_use]
67    pub const fn new(sink: LogSink, platform_owner: Arc<SystemAdmin>) -> Self {
68        Self {
69            sink,
70            platform_owner,
71        }
72    }
73
74    #[must_use]
75    pub const fn sink(&self) -> &LogSink {
76        &self.sink
77    }
78
79    #[must_use]
80    pub fn platform_owner(&self) -> &SystemAdmin {
81        &self.platform_owner
82    }
83
84    #[must_use]
85    pub fn platform_actor(&self, trace_id: TraceId) -> LogActor {
86        LogActor::platform(&self.platform_owner, trace_id)
87    }
88
89    #[must_use]
90    pub fn instance_id(&self) -> Option<&InstanceId> {
91        self.sink.instance_id()
92    }
93
94    pub fn enqueue(&self, entry: LogEntry) {
95        self.sink.enqueue(entry);
96    }
97}