use std::{fs::OpenOptions, future::IntoFuture, io::Write, path::Path, sync::atomic::Ordering};
use linera_chain::types::CertificateValue;
use tokio::select;
use crate::{config::DestinationId, storage::ExporterStorage};
pub(crate) struct LoggingExporter {
id: DestinationId,
file: std::fs::File,
}
impl LoggingExporter {
pub fn new(id: DestinationId) -> Self {
let log_file = Path::new(id.address());
let file = OpenOptions::new()
.write(true)
.create(true)
.truncate(false)
.open(log_file)
.expect("Failed to create log file");
LoggingExporter { id, file }
}
pub(crate) async fn run_with_shutdown<S, F: IntoFuture<Output = ()>>(
self,
shutdown_signal: F,
storage: ExporterStorage<S>,
) -> anyhow::Result<()>
where
S: linera_storage::Storage + Clone + Send + Sync + 'static,
{
let id = self.id.clone();
let shutdown_signal_future = shutdown_signal.into_future();
let mut pinned_shutdown_signal = Box::pin(shutdown_signal_future);
select! {
_ = &mut pinned_shutdown_signal => {
tracing::info!(?id, "logging exporter shutdown signal received, exiting.");
}
_ = self.start_logger(storage) => {
}
}
Ok(())
}
async fn start_logger<S>(mut self, storage: ExporterStorage<S>) -> anyhow::Result<()>
where
S: linera_storage::Storage + Clone + Send + Sync + 'static,
{
let destination_state = storage.load_destination_state(&self.id);
let mut destination_height = destination_state.load(Ordering::Acquire) as usize;
tracing::info!("starting logging exporter at height {}", destination_height);
loop {
if let Ok((block, blobs)) = storage.get_block_with_blobs(destination_height).await {
let inner = block.inner();
writeln!(
self.file,
"Block ID: {}, Chain: {}, Height: {}, State Hash: {}, Authenticated Signer: {}",
inner.hash(),
inner.chain_id(),
inner.height(),
inner.block().header.state_hash,
inner
.block()
.header
.authenticated_signer
.map_or_else(|| "N/A".into(), |signer| signer.to_string()),
)?;
for blob in blobs {
writeln!(self.file, "\tBlob ID: {}", blob.id(),)?;
}
destination_state.fetch_add(1, Ordering::Release);
destination_height += 1;
} else {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
}
}
}
}