log4rs_routing_appender/route/
mod.rs

1//! Routers.
2//!
3//! A router determines the appender to which a log event should be sent.
4use linked_hash_map::LinkedHashMap;
5use log::Record;
6use log4rs::append::Append;
7use std::error::Error;
8use std::fmt;
9use std::sync::Arc;
10use std::time::{Duration, Instant};
11
12#[cfg(feature = "file")]
13use log4rs::file::Deserializable;
14
15use {AppenderInner, CacheInner};
16
17#[cfg(feature = "pattern-router")]
18pub mod pattern;
19
20struct TrackedAppender {
21    appender: Appender,
22    used: Instant,
23}
24
25/// A cache of appenders.
26///
27/// It stores appenders identified by arbitrary strings. It is up to the router to decide how those
28/// strings are formatted.
29pub struct Cache {
30    map: LinkedHashMap<String, TrackedAppender>,
31    ttl: Duration,
32}
33
34impl CacheInner for Cache {
35    fn new(ttl: Duration) -> Cache {
36        Cache {
37            map: LinkedHashMap::new(),
38            ttl: ttl,
39        }
40    }
41}
42
43impl Cache {
44    /// Looks up the entry corresponding to the specified key.
45    pub fn entry<'a>(&'a mut self, key: String) -> Entry<'a> {
46        let now = Instant::now();
47        self.purge(now);
48
49        let entry = match self.map.get_refresh(&key) {
50            Some(entry) => {
51                entry.used = now;
52                Some(Appender(entry.appender.0.clone()))
53            }
54            None => None,
55        };
56
57        match entry {
58            Some(appender) => Entry::Occupied(OccupiedEntry(self, appender)),
59            None => Entry::Vacant(VacantEntry {
60                cache: self,
61                key: key,
62                time: now,
63            }),
64        }
65    }
66
67    fn purge(&mut self, now: Instant) {
68        let timeout = now - self.ttl;
69        loop {
70            match self.map.front() {
71                Some((_, v)) if v.used <= timeout => {}
72                _ => break,
73            }
74            self.map.pop_front();
75        }
76    }
77}
78
79/// A (possibly vacant) entry of a `Cache`.
80pub enum Entry<'a> {
81    /// An entry which is present in the `Cache`.
82    Occupied(OccupiedEntry<'a>),
83    /// An entry which is not present in the `Cache`.
84    Vacant(VacantEntry<'a>),
85}
86
87impl<'a> Entry<'a> {
88    /// Returns the value of the entry, using the provided closure to create and insert it if the
89    /// entry is not present in the cache.
90    pub fn or_insert_with<F>(self, f: F) -> Appender
91    where
92        F: FnOnce() -> Box<Append>,
93    {
94        match self {
95            Entry::Occupied(e) => e.into_value(),
96            Entry::Vacant(e) => e.insert(f()),
97        }
98    }
99}
100
101/// An entry which exists in the cache.
102pub struct OccupiedEntry<'a>(&'a mut Cache, Appender);
103
104impl<'a> OccupiedEntry<'a> {
105    /// Consumes the entry, returning the associated appender.
106    pub fn into_value(self) -> Appender {
107        self.1
108    }
109}
110
111/// An entry which does not exist in the cache.
112pub struct VacantEntry<'a> {
113    cache: &'a mut Cache,
114    key: String,
115    time: Instant,
116}
117
118impl<'a> VacantEntry<'a> {
119    /// Inserts an appender into the cache, returning the wrapped version of it.
120    pub fn insert(self, value: Box<Append>) -> Appender {
121        let appender = Arc::new(value);
122        let tracked = TrackedAppender {
123            appender: Appender(appender.clone()),
124            used: self.time,
125        };
126        self.cache.map.insert(self.key, tracked);
127        Appender(appender)
128    }
129}
130
131/// An opaque, wrapped appender stored by the `Cache`.
132pub struct Appender(Arc<Box<Append>>);
133
134impl AppenderInner for Appender {
135    fn appender(&self) -> &Append {
136        &**self.0
137    }
138}
139
140/// A trait implemented by types that can route log events to appenders.
141pub trait Route: fmt::Debug + 'static + Sync + Send {
142    /// Returns the appender to which the provided log event should be routed.
143    fn route(
144        &self,
145        record: &Record,
146        cache: &mut Cache,
147    ) -> Result<Appender, Box<Error + Sync + Send>>;
148}
149
150#[cfg(feature = "file")]
151impl Deserializable for Route {
152    fn name() -> &'static str {
153        "router"
154    }
155}