use crate::cli;
use crate::collection::collect;
use crate::collection::collect::files::ProcFileHandles;
use crate::collection::collect::read::StatFileLayout;
use crate::collection::flush::{FlushLog, FlushLogger};
use crate::collection::perf_table::TableMetadata;
use crate::collection::system_info::SystemInfo;
use crate::shared::CollectionTarget;
use crate::util::{self, CgroupDriver, CgroupPath};
use std::fs::{self, File, OpenOptions};
use std::io::{self, ErrorKind, Write};
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use csv::{Writer, WriterBuilder};
use failure::Error;
use serde::Serialize;
pub struct Collector {
pub writer: Writer<FlushLogger<File>>,
pub file_handles: ProcFileHandles,
pub active: bool,
pub memory_layout: StatFileLayout,
pub target: CollectionTarget,
}
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(rename_all = "PascalCase")]
struct LogFileHeader<'a> {
version: &'static str,
provider: &'static str,
metadata: &'a Option<serde_yaml::Value>,
perf_table: &'a TableMetadata,
system: SystemInfo,
cgroup: &'a PathBuf,
cgroup_driver: &'a CgroupDriver,
polled_at: u128,
initialized_at: u128,
}
impl Collector {
pub fn create(
logs_location: &Path,
target: CollectionTarget,
cgroup: &CgroupPath,
buffer_capacity: usize,
perf_table: &Arc<TableMetadata>,
event_log: Option<Arc<Mutex<FlushLog>>>,
) -> Result<Self, Error> {
fs::create_dir_all(logs_location)?;
let path = construct_log_path(&target.id, logs_location)?;
let file = OpenOptions::new()
.write(true)
.create(true)
.append(true)
.open(path)?;
let collector = Self::new(file, target, cgroup, buffer_capacity, perf_table, event_log)?;
Ok(collector)
}
pub fn collect(
&mut self,
working_buffers: &mut collect::WorkingBuffers,
) -> Result<(), csv::Error> {
collect::run(self, working_buffers)
}
fn new(
file: File,
target: CollectionTarget,
cgroup: &CgroupPath,
buffer_capacity: usize,
perf_table: &Arc<TableMetadata>,
event_log: Option<Arc<Mutex<FlushLog>>>,
) -> Result<Self, Error> {
let header = LogFileHeader {
version: cli::VERSION.unwrap_or("unknown"),
provider: target.provider,
metadata: &target.metadata,
system: SystemInfo::get(),
cgroup: &cgroup.path,
cgroup_driver: &cgroup.driver,
polled_at: target.poll_time,
initialized_at: util::nano_ts(),
perf_table,
};
let header_str = serde_yaml::to_string(&header)?;
writeln!(&file, "{}", header_str)?;
writeln!(&file, "---")?;
let mut writer = WriterBuilder::new()
.buffer_capacity(buffer_capacity)
.from_writer(FlushLogger::new(file, target.id.clone(), event_log));
writer.write_byte_record(collect::get_header())?;
let file_handles = ProcFileHandles::new(&cgroup.path);
let memory_layout = collect::examine_memory(&file_handles);
Ok(Self {
writer,
active: true,
file_handles,
memory_layout,
target,
})
}
}
fn construct_log_path(id: &str, logs_location: &Path) -> Result<String, io::Error> {
let filename = format!("{}_{}.log", id.to_string(), util::second_ts().to_string());
let base = Path::new(logs_location);
let filename_path = Path::new(&filename);
match base.join(filename_path).into_os_string().into_string() {
Ok(path) => Ok(path),
Err(_) => Err(io::Error::new(
ErrorKind::InvalidInput,
format!("could not create log path in {:?}", logs_location),
)),
}
}