use std::env;
use std::fmt;
use tracing::Level;
use tracing::field::{Field, Visit};
use tracing_subscriber::EnvFilter;
use tracing_subscriber::layer::{Context, Filter, Layer, SubscriberExt};
use tracing_subscriber::util::SubscriberInitExt;
struct RmcpResumeNoiseFilter;
const RMCP_NOISE_TARGET: &str = "rmcp::transport::common::server_side_http";
struct MessageCapture(String);
impl Visit for MessageCapture {
fn record_str(&mut self, field: &Field, value: &str) {
if field.name() == "message" {
self.0.push_str(value);
}
}
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
if field.name() == "message" {
use std::fmt::Write;
let _ = write!(&mut self.0, "{value:?}");
}
}
}
impl<S: tracing::Subscriber> Filter<S> for RmcpResumeNoiseFilter {
fn enabled(&self, _metadata: &tracing::Metadata<'_>, _ctx: &Context<'_, S>) -> bool {
true
}
fn event_enabled(&self, event: &tracing::Event<'_>, _ctx: &Context<'_, S>) -> bool {
if event.metadata().target() != RMCP_NOISE_TARGET {
return true;
}
let mut capture = MessageCapture(String::new());
event.record(&mut capture);
let msg = capture.0;
let noisy = msg.contains("resume session") && msg.contains("Channel closed");
!noisy
}
}
pub fn init() -> Result<(), TracingInitError> {
let filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new(format!("{}", Level::INFO)));
let format = env::var("OTEL_LOG_FORMAT").unwrap_or_else(|_| "compact".to_string());
let fmt_layer = match format.as_str() {
"json" => tracing_subscriber::fmt::layer()
.json()
.with_target(true)
.with_filter(RmcpResumeNoiseFilter)
.boxed(),
"pretty" => tracing_subscriber::fmt::layer()
.pretty()
.with_target(false)
.with_filter(RmcpResumeNoiseFilter)
.boxed(),
_ => tracing_subscriber::fmt::layer()
.compact()
.with_target(false)
.with_filter(RmcpResumeNoiseFilter)
.boxed(),
};
tracing_subscriber::registry()
.with(filter)
.with(fmt_layer)
.try_init()?;
#[cfg(feature = "otel")]
{
match otel::try_install_global()? {
Some(()) => tracing::info!("observability: OTLP exporter active"),
None => tracing::info!("observability: fmt-only (OTEL_EXPORTER_OTLP_ENDPOINT not set)"),
}
}
#[cfg(not(feature = "otel"))]
tracing::info!("observability: fmt-only (otel feature disabled)");
Ok(())
}
pub fn shutdown() {
#[cfg(feature = "otel")]
otel::shutdown();
}
#[derive(Debug, thiserror::Error)]
pub enum TracingInitError {
#[error("tracing subscriber already set: {0}")]
SubscriberSet(#[from] tracing::dispatcher::SetGlobalDefaultError),
#[error("tracing init failed: {0}")]
Init(String),
#[error("otlp exporter setup failed: {0}")]
Exporter(String),
}
impl From<tracing_subscriber::util::TryInitError> for TracingInitError {
fn from(err: tracing_subscriber::util::TryInitError) -> Self {
Self::Init(err.to_string())
}
}
#[cfg(feature = "otel")]
mod otel {
use std::env;
use super::TracingInitError;
pub(super) fn try_install_global() -> Result<Option<()>, TracingInitError> {
match env::var("OTEL_EXPORTER_OTLP_ENDPOINT") {
Ok(v) if !v.is_empty() => Ok(Some(())),
_ => Ok(None),
}
}
pub(super) fn shutdown() {
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{Arc, Mutex};
use tracing::{Level, error, info};
use tracing_subscriber::layer::SubscriberExt;
#[test]
fn init_is_idempotent_per_process() {
let result = init();
match result {
Ok(()) | Err(TracingInitError::SubscriberSet(_)) | Err(TracingInitError::Init(_)) => {}
Err(other) => panic!("unexpected error: {other}"),
}
}
#[derive(Default, Clone)]
struct CaptureLayer(Arc<Mutex<Vec<String>>>);
impl<S: tracing::Subscriber> Layer<S> for CaptureLayer {
fn on_event(&self, event: &tracing::Event<'_>, _ctx: Context<'_, S>) {
let mut capture = MessageCapture(String::new());
event.record(&mut capture);
self.0.lock().unwrap().push(format!(
"{}:{}:{}",
event.metadata().level(),
event.metadata().target(),
capture.0
));
}
}
#[test]
fn noise_filter_drops_resume_session_channel_closed() {
let capture = CaptureLayer::default();
let subscriber = tracing_subscriber::registry()
.with(EnvFilter::new("trace"))
.with(capture.clone().with_filter(RmcpResumeNoiseFilter));
tracing::subscriber::with_default(subscriber, || {
error!(
target: RMCP_NOISE_TARGET,
"Internal server error when resume session: Session error: Channel closed: Some(6)"
);
error!(
target: RMCP_NOISE_TARGET,
"Internal server error when create session: timeout"
);
info!(target: "post_cortex_daemon::test", "unrelated info");
});
let captured = capture.0.lock().unwrap();
let joined = captured.join("\n");
assert!(
!joined.contains("Channel closed"),
"resume-session noise should be dropped, got: {joined}"
);
assert!(
joined.contains("create session: timeout"),
"other rmcp errors must still surface, got: {joined}"
);
assert!(
joined.contains("unrelated info"),
"non-rmcp events must pass, got: {joined}"
);
}
#[test]
fn noise_filter_respects_target() {
let capture = CaptureLayer::default();
let subscriber = tracing_subscriber::registry()
.with(EnvFilter::new("trace"))
.with(capture.clone().with_filter(RmcpResumeNoiseFilter));
tracing::subscriber::with_default(subscriber, || {
error!(
target: "post_cortex::session_manager",
"resume session aborted: Channel closed by client"
);
});
let captured = capture.0.lock().unwrap();
assert_eq!(
captured.len(),
1,
"non-rmcp targets must not be filtered, got: {captured:?}"
);
}
#[test]
fn noise_filter_is_level_agnostic() {
let capture = CaptureLayer::default();
let subscriber = tracing_subscriber::registry()
.with(EnvFilter::new("trace"))
.with(capture.clone().with_filter(RmcpResumeNoiseFilter));
tracing::subscriber::with_default(subscriber, || {
tracing::warn!(
target: RMCP_NOISE_TARGET,
"Internal server error when resume session: Session error: Channel closed: Some(9)"
);
});
let captured = capture.0.lock().unwrap();
assert!(
captured.is_empty(),
"level-agnostic drop expected, got: {captured:?}"
);
let _ = Level::WARN;
}
}