use log;
use std::fmt;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Instant;
pub struct LoggingTimer<'name> {
level: log::Level,
file: &'static str,
module_path: &'static str,
line: u32,
finished: AtomicBool,
start_time: Instant,
name: &'name str,
extra_info: Option<String>,
}
impl<'name> LoggingTimer<'name> {
pub fn new(
file: &'static str,
module_path: &'static str,
line: u32,
name: &'name str,
extra_info: Option<String>,
level: log::Level,
) -> Option<Self> {
if log::log_enabled!(level) {
Some(LoggingTimer {
level: level,
start_time: Instant::now(),
file: file,
module_path: module_path,
line: line,
name: name,
finished: AtomicBool::new(false),
extra_info: extra_info
})
} else {
None
}
}
pub fn with_start_message(
file: &'static str,
module_path: &'static str,
line: u32,
name: &'name str,
extra_info: Option<String>,
level: log::Level,
) -> Option<Self> {
if log::log_enabled!(level) {
let tmr = Self::new(file, module_path, line, name, extra_info, level).unwrap();
tmr.log_impl(TimerTarget::Starting, None);
Some(tmr)
} else {
None
}
}
pub fn elapsed(&self) -> std::time::Duration {
self.start_time.elapsed()
}
#[deprecated(since = "0.3", note = "Please use the first parameter to the `timer` or `stimer` macro instead")]
pub fn level(mut self, level: log::Level) -> Self {
self.level = level;
self
}
pub fn executing(&self, args: Option<fmt::Arguments>) {
self.log_impl(TimerTarget::Executing, args);
}
pub fn finish(&self, args: Option<fmt::Arguments>) {
if !self.finished.load(Ordering::SeqCst) {
self.finished.store(true, Ordering::SeqCst);
self.log_impl(TimerTarget::Finished, args);
}
}
fn log_impl(&self, target: TimerTarget, args: Option<fmt::Arguments>) {
if !log::log_enabled!(self.level) {
return;
}
match (target, self.extra_info.as_ref(), args) {
(TimerTarget::Starting, Some(info), Some(args)) => {
self.log_record(target, format_args!("{}, {}, {}", self.name, info, args))
}
(TimerTarget::Starting, Some(info), None) => {
self.log_record(target, format_args!("{}, {}", self.name, info))
}
(TimerTarget::Starting, None, Some(args)) => {
self.log_record(target, format_args!("{}, {}", self.name, args))
}
(TimerTarget::Starting, None, None) => self.log_record(target, format_args!("{}", self.name)),
(_, Some(info), Some(args)) => {
self.log_record(target, format_args!("{}, Elapsed={:?}, {}, {}", self.name, self.elapsed(), info, args))
}
(_, Some(info), None) => {
self.log_record(target, format_args!("{}, Elapsed={:?}, {}", self.name, self.elapsed(), info))
}
(_, None, Some(args)) => {
self.log_record(target, format_args!("{}, Elapsed={:?}, {}", self.name, self.elapsed(), args))
}
(_, None, None) => self.log_record(target, format_args!("{}, Elapsed={:?}", self.name, self.elapsed())),
};
}
fn log_record(&self, target: TimerTarget, args: fmt::Arguments) {
log::logger().log(
&log::RecordBuilder::new()
.level(self.level)
.target(match target {
TimerTarget::Starting => "TimerStarting",
TimerTarget::Executing => "TimerExecuting",
TimerTarget::Finished => "TimerFinished",
})
.file(Some(self.file))
.module_path(Some(self.module_path))
.line(Some(self.line))
.args(args)
.build(),
);
}
}
impl<'a> Drop for LoggingTimer<'a> {
fn drop(&mut self) {
self.finish(None);
}
}
#[derive(Debug, Copy, Clone)]
enum TimerTarget {
Starting,
Executing,
Finished,
}
#[macro_export]
macro_rules! timer {
($name:expr) => {
{
$crate::LoggingTimer::new(
file!(),
module_path!(),
line!(),
$name,
None,
Level::Debug,
)
}
};
($level:expr; $name:expr) => {
{
$crate::LoggingTimer::new(
file!(),
module_path!(),
line!(),
$name,
None,
$level,
)
}
};
($name:expr, $format:tt) => {
{
$crate::LoggingTimer::new(
file!(),
module_path!(),
line!(),
$name,
Some(format!($format)),
Level::Debug,
)
}
};
($level:expr; $name:expr, $format:tt) => {
{
$crate::LoggingTimer::new(
file!(),
module_path!(),
line!(),
$name,
Some(format!($format)),
$level,
)
}
};
($name:expr, $format:tt, $($arg:expr),*) => {
{
$crate::LoggingTimer::new(
file!(),
module_path!(),
line!(),
$name,
Some(format!($format, $($arg), *)),
Level::Debug,
)
}
};
($level:expr; $name:expr, $format:tt, $($arg:expr),*) => {
{
$crate::LoggingTimer::new(
file!(),
module_path!(),
line!(),
$name,
Some(format!($format, $($arg), *)),
$level,
)
}
};
}
#[macro_export]
macro_rules! stimer {
($name:expr) => {
{
$crate::LoggingTimer::with_start_message(
file!(),
module_path!(),
line!(),
$name,
None,
Level::Debug,
)
}
};
($level:expr; $name:expr) => {
{
$crate::LoggingTimer::with_start_message(
file!(),
module_path!(),
line!(),
$name,
None,
$level,
)
}
};
($level:expr; $name:expr, $format:tt) => {
{
$crate::LoggingTimer::with_start_message(
file!(),
module_path!(),
line!(),
$name,
Some(format!($format)),
$level,
)
}
};
($name:expr, $format:tt) => {
{
$crate::LoggingTimer::with_start_message(
file!(),
module_path!(),
line!(),
$name,
Some(format!($format)),
Level::Debug,
)
}
};
($name:expr, $format:tt, $($arg:expr),*) => {
{
$crate::LoggingTimer::with_start_message(
file!(),
module_path!(),
line!(),
$name,
Some(format!($format, $($arg), *)),
Level::Debug,
)
}
};
($level:expr; $name:expr, $format:tt, $($arg:expr),*) => {
{
$crate::LoggingTimer::with_start_message(
file!(),
module_path!(),
line!(),
$name,
Some(format!($format, $($arg), *)),
$level,
)
}
};
}
#[macro_export]
macro_rules! executing {
($timer:expr) => ({
if let Some(ref tmr) = $timer {
tmr.executing(None);
}
});
($timer:expr, $format:tt) => ({
if let Some(ref tmr) = $timer {
tmr.executing(Some(format_args!($format)))
}
});
($timer:expr, $format:tt, $($arg:expr),*) => ({
if let Some(ref tmr) = $timer {
tmr.executing(Some(format_args!($format, $($arg), *)))
}
})
}
#[macro_export]
macro_rules! finish {
($timer:expr) => ({
if let Some(ref tmr) = $timer {
tmr.finish(None)
}
});
($timer:expr, $format:tt) => ({
if let Some(ref tmr) = $timer {
tmr.finish(Some(format_args!($format)))
}
});
($timer:expr, $format:tt, $($arg:expr),*) => ({
if let Some(ref tmr) = $timer {
tmr.finish(Some(format_args!($format, $($arg), *)))
}
})
}