pact_plugin_driver/
plugin_log_sink.rs1use std::sync::RwLock;
4
5use lazy_static::lazy_static;
6use serde::Serialize;
7use tracing::{debug, error, info, warn};
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
11pub enum PluginLogSource {
12 Stderr,
14 LogRpc,
16}
17
18#[derive(Debug, Clone, Serialize)]
20pub struct PluginLogEntry {
21 pub plugin_name: String,
23 pub plugin_instance_id: String,
25 pub test_run_id: Option<String>,
27 pub level: String,
29 pub message: String,
31 pub target: Option<String>,
33 pub timestamp_ms: i64,
35 pub source: PluginLogSource,
37}
38
39pub trait PluginLogSink: Send + Sync {
46 fn log(&self, entry: &PluginLogEntry);
47}
48
49struct DefaultPluginLogSink;
50
51fn is_transport_target(target: &str) -> bool {
52 target.starts_with("h2::") || target.starts_with("tower::") ||
53 target.starts_with("tonic::") || target.starts_with("hyper_util::") ||
54 target.starts_with("hyper::")
55}
56
57impl PluginLogSink for DefaultPluginLogSink {
58 fn log(&self, entry: &PluginLogEntry) {
59 if entry.source != PluginLogSource::LogRpc {
60 return;
61 }
62 if entry.level.to_uppercase() == "TRACE" {
63 return;
64 }
65 if entry.target.as_deref().map(is_transport_target).unwrap_or(false) {
66 return;
67 }
68 let plugin = &entry.plugin_name;
69 let instance = &entry.plugin_instance_id;
70 let msg = &entry.message;
71 let run_id = entry.test_run_id.as_deref().unwrap_or("");
72 match entry.level.to_uppercase().as_str() {
73 "DEBUG" => debug!(plugin_name = %plugin, plugin_instance = %instance, test_run_id = %run_id, "{}", msg),
74 "INFO" => info!(plugin_name = %plugin, plugin_instance = %instance, test_run_id = %run_id, "{}", msg),
75 "WARN" => warn!(plugin_name = %plugin, plugin_instance = %instance, test_run_id = %run_id, "{}", msg),
76 _ => error!(plugin_name = %plugin, plugin_instance = %instance, test_run_id = %run_id, "{}", msg),
77 }
78 }
79}
80
81lazy_static! {
82 static ref PLUGIN_LOG_SINK: RwLock<Box<dyn PluginLogSink>> =
83 RwLock::new(Box::new(DefaultPluginLogSink));
84}
85
86pub fn set_plugin_log_sink(sink: Box<dyn PluginLogSink>) {
88 *PLUGIN_LOG_SINK.write().unwrap() = sink;
89}
90
91pub(crate) fn emit_plugin_log(entry: &PluginLogEntry) {
93 PLUGIN_LOG_SINK.read().unwrap().log(entry);
94}