use std::cell::Cell;
use std::collections::{BTreeMap, HashMap};
use std::sync::Arc;
use parking_lot::Mutex;
use tracing::Subscriber;
use tracing::field::{Field, Visit};
use tracing_subscriber::Layer;
use tracing_subscriber::layer::{Context, SubscriberExt};
use tracing_subscriber::registry::LookupSpan;
use crate::chaos::{SIM_FAULT_EVENT_NAME, SimFaultEvent};
use super::event::{FieldValue, TraceEvent};
use super::invariant::Invariant;
use super::query::TraceQuery;
struct EventStore {
seq_counter: u64,
by_name: HashMap<String, Vec<TraceEvent>>,
last_sim_time_ms: u64,
}
impl EventStore {
fn new() -> Self {
Self {
seq_counter: 0,
by_name: HashMap::new(),
last_sim_time_ms: 0,
}
}
fn push(
&mut self,
time_ms: u64,
source: String,
target: String,
level: tracing::Level,
name: String,
fields: BTreeMap<String, FieldValue>,
) {
let seq = self.seq_counter;
self.seq_counter += 1;
let event = TraceEvent {
seq,
time_ms,
source,
target,
level,
name: name.clone(),
fields,
};
self.by_name.entry(name).or_default().push(event);
}
}
pub struct SimulationLayer {
events: Arc<Mutex<EventStore>>,
invariants: Arc<Mutex<Vec<Box<dyn Invariant + Send>>>>,
}
impl SimulationLayer {
#[must_use]
pub fn new() -> Self {
Self {
events: Arc::new(Mutex::new(EventStore::new())),
invariants: Arc::new(Mutex::new(Vec::new())),
}
}
#[must_use]
pub fn handle(&self) -> SimulationLayerHandle {
SimulationLayerHandle {
events: self.events.clone(),
invariants: self.invariants.clone(),
}
}
#[must_use]
pub fn install(self) -> (SimulationLayerHandle, InstallGuard) {
let handle = self.handle();
let interest_anchor = tracing::Dispatch::new(tracing_subscriber::registry());
let subscriber = tracing_subscriber::registry().with(self);
let guard = tracing::subscriber::set_default(subscriber);
tracing::callsite::rebuild_interest_cache();
(
handle,
InstallGuard {
_guard: guard,
_interest_anchor: interest_anchor,
},
)
}
}
impl Default for SimulationLayer {
fn default() -> Self {
Self::new()
}
}
pub struct InstallGuard {
_guard: tracing::subscriber::DefaultGuard,
_interest_anchor: tracing::Dispatch,
}
#[derive(Clone)]
pub struct SimulationLayerHandle {
events: Arc<Mutex<EventStore>>,
invariants: Arc<Mutex<Vec<Box<dyn Invariant + Send>>>>,
}
impl SimulationLayerHandle {
pub fn register(&self, inv: Box<dyn Invariant + Send>) {
self.invariants.lock().push(inv);
}
pub fn reset_for_seed(&self) {
{
let mut store = self.events.lock();
store.by_name.clear();
store.seq_counter = 0;
store.last_sim_time_ms = 0;
}
let mut invs = self.invariants.lock();
for inv in invs.iter_mut() {
inv.reset();
}
}
#[must_use]
pub fn current_sim_time_ms(&self) -> u64 {
self.events.lock().last_sim_time_ms
}
pub fn set_sim_time_ms(&self, ms: u64) {
self.events.lock().last_sim_time_ms = ms;
}
pub fn record_sim_fault(&self, time_ms: u64, fault: &SimFaultEvent) {
let mut fields = fault.to_fields();
fields.insert("kind".to_owned(), FieldValue::Str(fault.kind().to_owned()));
self.events.lock().push(
time_ms,
"sim".to_owned(),
"moonpool_sim::fault".to_owned(),
tracing::Level::INFO,
SIM_FAULT_EVENT_NAME.to_owned(),
fields,
);
}
pub fn run_invariants(&self) {
let sim_time_ms = self.current_sim_time_ms();
let invariants = self.invariants.lock();
for inv in invariants.iter() {
inv.observe(self, sim_time_ms);
}
}
}
impl TraceQuery for SimulationLayerHandle {
fn len(&self, name: &str) -> usize {
self.events
.lock()
.by_name
.get(name)
.map_or(0, std::vec::Vec::len)
}
fn since(&self, name: &str, cursor: &Cell<usize>) -> Vec<TraceEvent> {
let store = self.events.lock();
let Some(entries) = store.by_name.get(name) else {
return Vec::new();
};
let len = entries.len();
let from = cursor.get();
if from >= len {
return Vec::new();
}
let result: Vec<TraceEvent> = entries[from..].to_vec();
cursor.set(len);
result
}
fn snapshot(&self, name: &str) -> Vec<TraceEvent> {
self.events
.lock()
.by_name
.get(name)
.cloned()
.unwrap_or_default()
}
}
struct SourceIp(String);
struct SpanIpVisitor {
ip: Option<String>,
}
impl Visit for SpanIpVisitor {
fn record_str(&mut self, field: &Field, value: &str) {
if field.name() == "ip" {
self.ip = Some(value.to_owned());
}
}
fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
if field.name() == "ip" {
self.ip = Some(format!("{value:?}"));
}
}
}
struct EventVisitor {
name: Option<String>,
fields: BTreeMap<String, FieldValue>,
}
impl EventVisitor {
fn new() -> Self {
Self {
name: None,
fields: BTreeMap::new(),
}
}
}
impl Visit for EventVisitor {
fn record_bool(&mut self, field: &Field, value: bool) {
self.fields
.insert(field.name().to_owned(), FieldValue::Bool(value));
}
fn record_i64(&mut self, field: &Field, value: i64) {
self.fields
.insert(field.name().to_owned(), FieldValue::I64(value));
}
fn record_u64(&mut self, field: &Field, value: u64) {
self.fields
.insert(field.name().to_owned(), FieldValue::U64(value));
}
fn record_f64(&mut self, field: &Field, value: f64) {
self.fields
.insert(field.name().to_owned(), FieldValue::F64(value));
}
fn record_str(&mut self, field: &Field, value: &str) {
self.fields
.insert(field.name().to_owned(), FieldValue::Str(value.to_owned()));
}
fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
let formatted = format!("{value:?}");
if field.name() == "message" {
self.name = Some(formatted);
} else {
self.fields
.insert(field.name().to_owned(), FieldValue::Str(formatted));
}
}
}
fn nearest_source<S>(ctx: &Context<'_, S>, event: &tracing::Event<'_>) -> Option<String>
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
let scope = ctx.event_scope(event)?;
for span in scope {
if let Some(ip) = span.extensions().get::<SourceIp>() {
return Some(ip.0.clone());
}
}
None
}
impl<S> Layer<S> for SimulationLayer
where
S: Subscriber + for<'a> LookupSpan<'a>,
{
fn register_callsite(
&self,
_metadata: &'static tracing::Metadata<'static>,
) -> tracing::subscriber::Interest {
tracing::subscriber::Interest::sometimes()
}
fn on_new_span(
&self,
attrs: &tracing::span::Attributes<'_>,
id: &tracing::span::Id,
ctx: Context<'_, S>,
) {
let mut visitor = SpanIpVisitor { ip: None };
attrs.record(&mut visitor);
if let Some(ip) = visitor.ip
&& let Some(span) = ctx.span(id)
{
span.extensions_mut().insert(SourceIp(ip));
}
}
fn on_event(&self, event: &tracing::Event<'_>, ctx: Context<'_, S>) {
if *event.metadata().level() > tracing::Level::INFO {
return;
}
let Some(source) = nearest_source(&ctx, event) else {
return;
};
let mut visitor = EventVisitor::new();
event.record(&mut visitor);
let Some(name) = visitor.name.filter(|n| !n.is_empty()) else {
return;
};
let mut store = self.events.lock();
let time_ms = store.last_sim_time_ms;
store.push(
time_ms,
source,
event.metadata().target().to_owned(),
*event.metadata().level(),
name,
visitor.fields,
);
}
}