use std::sync::RwLock;
use lazy_static::lazy_static;
use serde::Serialize;
use tracing::{debug, error, info, warn};
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub enum PluginLogSource {
Stderr,
LogRpc,
}
#[derive(Debug, Clone, Serialize)]
pub struct PluginLogEntry {
pub plugin_name: String,
pub plugin_instance_id: String,
pub test_run_id: Option<String>,
pub level: String,
pub message: String,
pub target: Option<String>,
pub timestamp_ms: i64,
pub source: PluginLogSource,
}
pub trait PluginLogSink: Send + Sync {
fn log(&self, entry: &PluginLogEntry);
}
struct DefaultPluginLogSink;
fn is_transport_target(target: &str) -> bool {
target.starts_with("h2::") || target.starts_with("tower::") ||
target.starts_with("tonic::") || target.starts_with("hyper_util::") ||
target.starts_with("hyper::")
}
impl PluginLogSink for DefaultPluginLogSink {
fn log(&self, entry: &PluginLogEntry) {
if entry.source != PluginLogSource::LogRpc {
return;
}
if entry.level.to_uppercase() == "TRACE" {
return;
}
if entry.target.as_deref().map(is_transport_target).unwrap_or(false) {
return;
}
let plugin = &entry.plugin_name;
let instance = &entry.plugin_instance_id;
let msg = &entry.message;
let run_id = entry.test_run_id.as_deref().unwrap_or("");
match entry.level.to_uppercase().as_str() {
"DEBUG" => debug!(plugin_name = %plugin, plugin_instance = %instance, test_run_id = %run_id, "{}", msg),
"INFO" => info!(plugin_name = %plugin, plugin_instance = %instance, test_run_id = %run_id, "{}", msg),
"WARN" => warn!(plugin_name = %plugin, plugin_instance = %instance, test_run_id = %run_id, "{}", msg),
_ => error!(plugin_name = %plugin, plugin_instance = %instance, test_run_id = %run_id, "{}", msg),
}
}
}
lazy_static! {
static ref PLUGIN_LOG_SINK: RwLock<Box<dyn PluginLogSink>> =
RwLock::new(Box::new(DefaultPluginLogSink));
}
pub fn set_plugin_log_sink(sink: Box<dyn PluginLogSink>) {
*PLUGIN_LOG_SINK.write().unwrap() = sink;
}
pub(crate) fn emit_plugin_log(entry: &PluginLogEntry) {
PLUGIN_LOG_SINK.read().unwrap().log(entry);
}