1use crate::{
2 config::{LogFormat, SinkConfigBuild, SinkConfigTrait},
3 log_impl::{LogSink, LogSinkTrait},
4 time::Timer,
5};
6use log::*;
7use ring_file::*;
8
9use std::cell::UnsafeCell;
10use std::hash::{Hash, Hasher};
11use std::io::Write;
12use std::mem::transmute;
13use std::path::{Path, PathBuf};
14use std::sync::atomic::{AtomicBool, Ordering};
15
16#[derive(Hash)]
63pub struct LogRingFile {
64 pub file_path: Box<Path>,
65 pub level: Level,
66 pub format: LogFormat,
67 pub buf_size: i32,
69}
70
71impl LogRingFile {
72 pub fn new<P: Into<PathBuf>>(
73 file_path: P, buf_size: i32, max_level: Level, format: LogFormat,
74 ) -> Self {
75 assert!(buf_size > 0);
76 Self { buf_size, file_path: file_path.into().into_boxed_path(), level: max_level, format }
77 }
78}
79
80impl SinkConfigBuild for LogRingFile {
81 fn build(&self) -> LogSink {
82 LogSink::RingFile(LogSinkRingFile::new(self))
83 }
84}
85
86impl SinkConfigTrait for LogRingFile {
87 fn get_level(&self) -> Level {
88 self.level
89 }
90
91 fn get_file_path(&self) -> Option<Box<Path>> {
92 Some(self.file_path.clone())
93 }
94
95 fn write_hash(&self, hasher: &mut Box<dyn Hasher>) {
96 self.hash(hasher);
97 hasher.write(b"LogRingFile");
98 }
99}
100
101pub(crate) struct LogSinkRingFile {
102 max_level: Level,
103 inner: UnsafeCell<RingFile>,
104 formatter: LogFormat,
105 locked: AtomicBool,
107}
108
109unsafe impl Send for LogSinkRingFile {}
110unsafe impl Sync for LogSinkRingFile {}
111
112impl LogSinkRingFile {
113 fn new(config: &LogRingFile) -> Self {
114 Self {
115 max_level: config.level,
116 inner: UnsafeCell::new(RingFile::new(config.buf_size, config.file_path.to_path_buf())),
117 formatter: config.format.clone(),
118 locked: AtomicBool::new(false),
119 }
120 }
121
122 #[inline(always)]
123 fn get_inner(&self) -> &RingFile {
124 unsafe { transmute(self.inner.get()) }
125 }
126
127 #[inline(always)]
128 fn get_inner_mut(&self) -> &mut RingFile {
129 unsafe { transmute(self.inner.get()) }
130 }
131}
132
133impl LogSinkTrait for LogSinkRingFile {
134 fn open(&self) -> std::io::Result<()> {
135 Ok(())
136 }
137
138 fn reopen(&self) -> std::io::Result<()> {
139 println!("RingFile: start dumping");
140 if let Err(e) = self.get_inner().dump() {
141 println!("RingFile: dump error {:?}", e);
142 Err(e)
143 } else {
144 println!("RingFile: dump complete");
145 Ok(())
146 }
147 }
148
149 #[inline(always)]
150 fn log(&self, now: &Timer, r: &Record) {
151 if r.level() <= self.max_level {
152 let buf = self.formatter.process(now, r);
153 while self
154 .locked
155 .compare_exchange_weak(false, true, Ordering::SeqCst, Ordering::Relaxed)
156 .is_err()
157 {
158 std::hint::spin_loop();
159 }
160 let _ = self.get_inner_mut().write_all(buf.as_bytes());
161 self.locked.store(false, Ordering::Release);
162 }
163 }
164
165 #[inline(always)]
166 fn flush(&self) {}
167}