1use crate::error::{LogError, LogResult};
6use std::collections::VecDeque;
7use std::fs::File;
8use std::io::{LineWriter, Write};
9use std::str::FromStr;
10use std::sync::Mutex;
11
12pub const CACHE_STR_INIT_SIZE: usize = 256;
14
15pub enum AsyncLogType {
17 Message(Vec<u8>),
18 Flush,
19 QuickwitLog(crate::quickwit::QuickwitLogEntry),
20}
21
22pub struct SkipAnsiColorIter<'a> {
24 data: &'a [u8],
25 pos: usize,
26 find_len: usize,
27}
28
29impl<'a> SkipAnsiColorIter<'a> {
30 pub fn new(data: &'a [u8]) -> Self {
31 let find_len = if data.len() > 3 {
32 data.len() - 3
33 } else {
34 0
35 };
36
37 SkipAnsiColorIter {
38 data,
39 pos: 0,
40 find_len,
41 }
42 }
43}
44
45impl<'a> Iterator for SkipAnsiColorIter<'a> {
46 type Item = &'a [u8];
47
48 #[inline]
49 fn next(&mut self) -> Option<Self::Item> {
50 let (mut pos, find_len, data) = (self.pos, self.find_len, self.data);
52 while pos < find_len {
53 unsafe {
54 if *data.get_unchecked(pos) != 0x1b || *data.get_unchecked(pos + 1) != b'[' {
55 pos += 1;
56 continue;
57 }
58
59 let n = if *data.get_unchecked(pos + 3) == b'm' { 4 } else { 5 };
61 let p = self.pos;
62 self.pos = pos + n;
63 return Some(&data[p..pos]);
64 }
65 }
66
67 let dl = data.len();
69 if pos < dl {
70 let p = self.pos;
71 self.pos = dl;
72 return Some(&data[p..dl]);
73 }
74
75 None
76 }
77}
78
79pub fn parse_level(level: &str) -> LogResult<log::LevelFilter> {
81 match log::LevelFilter::from_str(level) {
82 Ok(num) => Ok(num),
83 Err(_) => Err(format!("can't parse log level: {level}").into()),
84 }
85}
86
87pub fn parse_size(size: &str) -> LogResult<u32> {
89 match size.parse() {
90 Ok(n) => Ok(n),
91 Err(_) => match size[..size.len() - 1].parse() {
92 Ok(n) => {
93 let s = size.as_bytes();
94 match s[s.len() - 1] {
95 b'b' | b'B' => Ok(n),
96 b'k' | b'K' => Ok(n * 1024),
97 b'm' | b'M' => Ok(n * 1024 * 1024),
98 b'g' | b'G' => Ok(n * 1024 * 1024 * 1024),
99 _ => Err(format!("parse size error, unit is unknown: {size}").into()),
100 }
101 },
102 Err(e) => Err(e.into()),
103 }
104 }
105}
106
107pub fn write_text(w: &mut LineWriter<File>, msg: &[u8]) -> std::io::Result<usize> {
109 let mut written = 0;
110 for chunk in SkipAnsiColorIter::new(msg) {
111 w.write_all(chunk)?;
112 written += chunk.len();
113 }
114 Ok(written)
115}
116
117#[inline]
119pub fn safe_flush<W: Write>(writer: &mut W) -> LogResult<()> {
120 writer.flush().map_err(LogError::from)
121}
122
123pub fn open_log_file_sync(file_path: &str) -> LogResult<(LineWriter<File>, u32)> {
125 use std::fs::OpenOptions;
126 use std::io::Seek;
127
128 let mut file = OpenOptions::new()
129 .create(true)
130 .append(true)
131 .open(file_path)
132 .map_err(|e| LogError::file_operation(
133 file_path.to_string(),
134 format!("Failed to open file: {}", e)
135 ))?;
136
137 let size = file.seek(std::io::SeekFrom::End(0))
138 .map_err(|e| LogError::file_operation(
139 file_path.to_string(),
140 format!("Failed to seek file: {}", e)
141 ))? as u32;
142
143 let writer = LineWriter::new(file);
144 Ok((writer, size))
145}
146
147pub fn level_color(level: log::Level) -> &'static str {
149 match level {
150 log::Level::Error => "\x1b[31m", log::Level::Warn => "\x1b[33m", log::Level::Info => "\x1b[32m", log::Level::Debug => "\x1b[36m", log::Level::Trace => "\x1b[35m", }
156}
157
158pub fn get_msg_from_cache() -> Vec<u8> {
160 static FMT_CACHE: Mutex<VecDeque<Vec<u8>>> = Mutex::new(VecDeque::new());
161
162 if let Ok(mut cache) = FMT_CACHE.lock() {
163 cache.pop_front().unwrap_or_else(|| Vec::with_capacity(CACHE_STR_INIT_SIZE))
164 } else {
165 Vec::with_capacity(CACHE_STR_INIT_SIZE)
166 }
167}
168
169pub fn put_msg_to_cache(mut value: Vec<u8>) {
171 static FMT_CACHE: Mutex<VecDeque<Vec<u8>>> = Mutex::new(VecDeque::new());
172
173 value.clear();
174 if let Ok(mut cache) = FMT_CACHE.lock() {
175 if cache.len() < 16 {
176 cache.push_back(value);
177 }
178 }
179}
180
181pub fn debug_check_init() {
183 #[cfg(debug_assertions)]
184 {
185 println!("log-full debug mode enabled");
186 }
187}
188
189pub fn version() -> &'static str {
191 env!("CARGO_PKG_VERSION")
192}
193
194pub fn build_info() -> (&'static str, &'static str) {
196 (env!("CARGO_PKG_VERSION"), env!("CARGO_PKG_NAME"))
197}