#[macro_use]
extern crate slog;
use slog::Logger;
use std::time;
use std::collections::HashMap;
pub struct TimeReporter {
times: HashMap<&'static str, time::Duration>,
cur_state_time: Option<(&'static str, time::Instant)>,
log: Logger,
level: slog::Level,
}
impl TimeReporter {
pub fn new<S : Into<String>>(name: S, log: Logger) -> TimeReporter {
let name = name.into();
let log = log.new(o!("name" => name));
TimeReporter {
times: HashMap::new(),
log: log,
cur_state_time: None,
level: slog::Level::Info,
}
}
pub fn new_with_level<S : Into<String>>(name: S, log: Logger, level: slog::Level) -> TimeReporter {
let name = name.into();
let log = log.new(o!("name" => name));
TimeReporter {
times: HashMap::new(),
log: log,
cur_state_time: None,
level: level,
}
}
pub fn start(&mut self, key: &'static str) {
let now = time::Instant::now();
self.save_current(now);
trace!(self.log, "start"; "phase" => key);
self.cur_state_time = Some((key, now))
}
pub fn start_with<F, R>(&mut self, key :&'static str, f: F) -> R
where F: FnOnce() -> R
{
self.start(key);
f()
}
fn save_current(&mut self, now: time::Instant) {
if let Some((key, prev)) = self.cur_state_time.take() {
*self.times
.entry(key)
.or_insert(time::Duration::new(0, 0)) += now - prev;
trace!(self.log, "end"; "phase" => key);
}
}
pub fn stop(&mut self) {
let now = time::Instant::now();
self.save_current(now);
}
pub fn finish(self) {}
}
struct TimeReporterDroper<'a> {
reporter: &'a TimeReporter,
}
impl<'a> slog::KV for TimeReporterDroper<'a> {
fn serialize(&self,
_record: &slog::Record,
ser: &mut slog::Serializer)
-> slog::Result {
let mut stats: Vec<(&'static str, time::Duration)> = self.reporter
.times
.iter()
.map(|(&k, &v)| (k, v))
.collect();
stats.sort_by_key(|s| s.1);
for &(state, dur) in stats.iter().rev() {
ser.emit_arguments(
state,
&format_args!("{}",
dur.as_secs() as f64
+ dur.subsec_nanos() as f64 / 1000000000f64)
)?;
}
Ok(())
}
}
impl Drop for TimeReporter {
fn drop(&mut self) {
self.stop();
debug_assert!(self.cur_state_time.is_none());
let lrd = TimeReporterDroper { reporter: &self };
static LOC : slog::RecordLocation = slog::RecordLocation {
file: file!(),
line: line!(),
column: column!(),
function: "",
module: module_path!(),
};
let record_static = slog::RecordStatic {
location : &LOC,
level: self.level,
tag: "slog_perf",
};
let args = format_args!("time report");
self.log.log(&slog::Record::new(
&record_static,
&args,
slog_b!(lrd),
));
}
}