#![doc(html_root_url = "https://docs.rs/slog-retry/0.1.1/slog-retry/")]
#![warn(missing_docs)]
extern crate failure;
extern crate slog;
use std::cell::{Cell, RefCell, RefMut};
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
use std::iter;
use std::thread;
use std::time::Duration;
use failure::Fail;
use slog::{Drain, OwnedKVList, Record};
#[derive(Debug)]
pub struct Error<FactoryError: Fail + Debug, SlaveError: Fail + Debug> {
pub factory: Option<FactoryError>,
pub slave: Option<SlaveError>,
}
impl<FactoryError, SlaveError> Fail for Error<FactoryError, SlaveError>
where
FactoryError: Fail + Debug,
SlaveError: Fail + Debug,
{
fn cause(&self) -> Option<&Fail> {
if let Some(ref slave) = self.slave {
return Some(slave);
}
if let Some(ref fact) = self.factory {
return Some(fact);
}
None
}
}
impl<FactoryError, SlaveError> Display for Error<FactoryError, SlaveError>
where
FactoryError: Fail + Debug,
SlaveError: Fail + Debug,
{
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
let factory = self.factory
.as_ref()
.map(|f| format!("{}", f))
.unwrap_or_else(|| "()".to_owned());
let slave = self.slave
.as_ref()
.map(|s| format!("{}", s))
.unwrap_or_else(|| "()".to_owned());
write!(
fmt,
"Failed to reconnect the logging drain: {}/{}",
factory, slave
)
}
}
pub type Strategy = Box<Iterator<Item = Duration>>;
pub type NewStrategy = Box<Fn() -> Strategy + Send>;
pub struct Retry<Slave, Factory> {
slave: RefCell<Option<Slave>>,
factory: Factory,
strategy: NewStrategy,
initialized: Cell<bool>,
}
impl<Slave, FactoryError, Factory> Retry<Slave, Factory>
where
Slave: Drain,
FactoryError: Fail + Debug,
Slave::Err: Fail + Debug,
Factory: Fn() -> Result<Slave, FactoryError>,
{
pub fn new(
factory: Factory,
strategy: Option<NewStrategy>,
connect_now: bool,
) -> Result<Self, Error<FactoryError, Slave::Err>> {
let result = Self {
slave: RefCell::new(None),
factory,
strategy: strategy.unwrap_or_else(|| Box::new(|| default_new_strategy())),
initialized: Cell::new(false),
};
if connect_now {
result
.init(&mut result.slave.borrow_mut(), &mut (result.strategy)())
.map_err(|factory| {
Error {
factory,
slave: None,
}
})?;
}
Ok(result)
}
fn init(
&self,
slave: &mut RefMut<Option<Slave>>,
strategy: &mut Strategy,
) -> Result<(), Option<FactoryError>> {
let prefix: Strategy = if self.initialized.get() {
Box::new(iter::empty())
} else {
self.initialized.set(true);
Box::new(iter::once(Duration::from_secs(0)))
};
let mut last_err = None;
for sleep in prefix.chain(strategy) {
thread::sleep(sleep);
match (self.factory)() {
Ok(ok) => {
**slave = Some(ok);
return Ok(());
},
Err(err) => last_err = Some(err),
}
}
Err(last_err)
}
}
impl<Slave, FactoryError, Factory> Drain for Retry<Slave, Factory>
where
Slave: Drain,
FactoryError: Fail + Debug,
Slave::Err: Fail + Debug,
Factory: Fn() -> Result<Slave, FactoryError>,
{
type Ok = Slave::Ok;
type Err = Error<FactoryError, Slave::Err>;
fn log(&self, record: &Record, values: &OwnedKVList) -> Result<Self::Ok, Self::Err> {
let mut borrowed = self.slave.borrow_mut();
let mut slave_err = None;
if let Some(ref slave) = *borrowed {
match slave.log(record, values) {
Ok(ok) => return Ok(ok),
Err(err) => slave_err = Some(err),
}
}
borrowed.take();
let mut strategy = (self.strategy)();
loop {
match self.init(&mut borrowed, &mut strategy) {
Err(factory) =>
return Err(Error {
factory,
slave: slave_err,
}),
Ok(()) => match borrowed.as_ref().unwrap().log(record, values) {
Ok(ok) => return Ok(ok),
Err(err) => {
slave_err = Some(err);
borrowed.take();
},
},
}
}
}
}
fn default_new_strategy() -> Strategy {
let iterator = (1..5).map(Duration::from_secs);
Box::new(iterator)
}