mod cursor;
mod parser;
mod stream;
mod types;
pub use cursor::{LogCursor, LogCursorParseError};
pub use stream::{LogStreamOptions, LogStreamStart};
pub use types::{LogEntry, LogOptions, LogSource};
use std::path::PathBuf;
use futures::Stream;
use stream::{LogEngine, LogFileConfig, LogFileFormat};
use crate::{MicrosandboxError, MicrosandboxResult};
const LOG_FILES: &[LogFileConfig] = &[
LogFileConfig {
filename: "exec.log",
format: LogFileFormat::Jsonl,
max_rotation_index: 4,
produces: &[
LogSource::Stdout,
LogSource::Stderr,
LogSource::Output,
LogSource::System,
],
},
LogFileConfig {
filename: "runtime.log",
format: LogFileFormat::Text,
max_rotation_index: 0,
produces: &[LogSource::System],
},
LogFileConfig {
filename: "kernel.log",
format: LogFileFormat::Text,
max_rotation_index: 0,
produces: &[LogSource::System],
},
];
#[derive(Debug, Clone)]
pub struct LogSnapshot {
pub entries: Vec<LogEntry>,
pub cursor: LogCursor,
}
pub fn log_dir_for(name: &str) -> PathBuf {
crate::backend::default_backend()
.as_local()
.map(|local| local.config().sandboxes_dir().join(name).join("logs"))
.unwrap_or_else(|| {
microsandbox_utils::resolve_home()
.join(microsandbox_utils::SANDBOXES_SUBDIR)
.join(name)
.join("logs")
})
}
pub async fn read_logs(name: &str, opts: &LogOptions) -> MicrosandboxResult<Vec<LogEntry>> {
Ok(read_logs_snapshot(name, opts).await?.entries)
}
pub async fn read_logs_snapshot(name: &str, opts: &LogOptions) -> MicrosandboxResult<LogSnapshot> {
crate::sandbox::validate_sandbox_name(name)?;
let stream_opts = LogStreamOptions {
sources: opts.sources.clone(),
start: opts.since.map(LogStreamStart::Since).unwrap_or_default(),
until: None,
follow: false,
};
let log_dir = log_dir_for(name);
if !tokio::fs::try_exists(&log_dir).await.unwrap_or(false) {
return Err(MicrosandboxError::SandboxNotFound(name.to_string()));
}
let sources = LogSource::effective(&stream_opts.sources);
let engine = LogEngine::new(
log_dir,
LOG_FILES,
sources,
&stream_opts.start,
stream_opts.until,
stream_opts.follow,
)
.await?;
let (mut entries, cursor) = engine.drain_sorted_snapshot().await?;
opts.apply_to(&mut entries);
Ok(LogSnapshot { entries, cursor })
}
pub async fn log_stream(
name: &str,
opts: &LogStreamOptions,
) -> MicrosandboxResult<impl Stream<Item = MicrosandboxResult<LogEntry>> + Send + 'static + use<>> {
crate::sandbox::validate_sandbox_name(name)?;
let log_dir = log_dir_for(name);
if !tokio::fs::try_exists(&log_dir).await.unwrap_or(false) {
return Err(MicrosandboxError::SandboxNotFound(name.to_string()));
}
let sources = LogSource::effective(&opts.sources);
let engine = LogEngine::new(
log_dir,
LOG_FILES,
sources,
&opts.start,
opts.until,
opts.follow,
)
.await?;
Ok(engine.into_stream())
}