pub mod emit;
pub mod layer;
pub mod record;
pub mod render;
pub use emit::{
emit_cap_decision, emit_ceiling_class, emit_credential_issue, finish_tool_call,
instantiation_span, tool_call_span,
};
pub use layer::{AuditLayer, Detail};
pub use record::{
CapDecisionRecord, CeilingClassRecord, CredentialIssueRecord, Decision4, Outcome,
ToolCallStart, Transport, sha256_hex,
};
pub const TARGET_AUDIT: &str = "act::audit";
pub const TARGET_GUEST: &str = "act::guest";
pub fn fmt_filter<S>(
env_filter: tracing_subscriber::EnvFilter,
) -> impl tracing_subscriber::layer::Filter<S> {
use tracing_subscriber::filter::{FilterExt, filter_fn};
env_filter.and(filter_fn(|meta: &tracing::Metadata<'_>| {
meta.target() != crate::audit::TARGET_AUDIT && meta.target() != crate::audit::TARGET_GUEST
}))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fmt_filter_excludes_audit_and_guest_targets() {
use std::sync::{Arc, Mutex};
use tracing_subscriber::layer::{Context, Layer};
use tracing_subscriber::prelude::*;
use tracing_subscriber::registry::LookupSpan;
#[derive(Clone, Default)]
struct Capture(Arc<Mutex<Vec<String>>>);
impl<S> Layer<S> for Capture
where
S: tracing::Subscriber + for<'a> LookupSpan<'a>,
{
fn on_event(&self, event: &tracing::Event<'_>, _ctx: Context<'_, S>) {
self.0
.lock()
.unwrap()
.push(event.metadata().target().to_string());
}
}
let cap = Capture::default();
let sink = cap.clone();
let env_filter: tracing_subscriber::EnvFilter = "act=info".parse().unwrap();
let sub = tracing_subscriber::registry().with(cap.with_filter(fmt_filter(env_filter)));
tracing::subscriber::with_default(sub, || {
tracing::info!(target: TARGET_AUDIT, "audit event");
tracing::info!(target: TARGET_GUEST, "guest event");
tracing::info!(target: "act::runtime", "ordinary event");
});
let got = sink.0.lock().unwrap().clone();
assert_eq!(got, vec!["act::runtime".to_string()]);
}
}