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