log4rs_routing_appender/route/
mod.rs1use 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
25pub 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 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
79pub enum Entry<'a> {
81 Occupied(OccupiedEntry<'a>),
83 Vacant(VacantEntry<'a>),
85}
86
87impl<'a> Entry<'a> {
88 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
101pub struct OccupiedEntry<'a>(&'a mut Cache, Appender);
103
104impl<'a> OccupiedEntry<'a> {
105 pub fn into_value(self) -> Appender {
107 self.1
108 }
109}
110
111pub struct VacantEntry<'a> {
113 cache: &'a mut Cache,
114 key: String,
115 time: Instant,
116}
117
118impl<'a> VacantEntry<'a> {
119 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
131pub struct Appender(Arc<Box<Append>>);
133
134impl AppenderInner for Appender {
135 fn appender(&self) -> &Append {
136 &**self.0
137 }
138}
139
140pub trait Route: fmt::Debug + 'static + Sync + Send {
142 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}