1use std::io::Read;
2use std::path::PathBuf;
3use std::{
4 fs,
5 io,
6};
7use std::{
8 io::Write,
9 rc::Rc,
10};
11
12use byteorder::ReadBytesExt;
13use byteorder::{
14 BigEndian,
15 WriteBytesExt,
16};
17use integer_encoding::VarIntWriter;
18use time::OffsetDateTime;
19
20#[cfg(feature = "decode")]
21use crate::crypto::{
22 Aes128Gcm,
23 Aes256Gcm,
24};
25use crate::crypto::{
26 Aes128GcmSiv,
27 Aes256GcmSiv,
28};
29use crate::events::Event::{self,};
30use crate::{
31 appender::EZAppender,
32 compress::ZlibCodec,
33 errors::LogError,
34 CipherKind,
35 Compress,
36 CompressKind,
37 Cryptor,
38 EZLogConfig,
39 EZRecord,
40 RECORD_SIGNATURE_END,
41 RECORD_SIGNATURE_START,
42};
43use crate::{
44 errors,
45 event,
46 NonceGenFn,
47 V1_LOG_HEADER_SIZE,
48};
49use crate::{
50 Version,
51 V2_LOG_HEADER_SIZE,
52};
53
54type Result<T> = std::result::Result<T, LogError>;
55
56#[inline]
57pub(crate) fn create_size_chunk(size: usize) -> Result<Vec<u8>> {
58 let mut chunk: Vec<u8> = Vec::new();
59 chunk.write_varint(size)?;
60 Ok(chunk)
61}
62
63#[inline]
64pub(crate) fn encode_content(mut buf: Vec<u8>) -> Result<Vec<u8>> {
65 let mut chunk: Vec<u8> = Vec::new();
66 chunk.push(RECORD_SIGNATURE_START);
67 let size = buf.len();
68 let mut size_chunk = create_size_chunk(size)?;
69 chunk.append(&mut size_chunk);
70 chunk.append(&mut buf);
71 chunk.push(RECORD_SIGNATURE_END);
72 Ok(chunk)
73}
74
75#[allow(deprecated)]
76pub fn create_cryptor(config: &EZLogConfig) -> Result<Option<Box<dyn Cryptor>>> {
77 if let Some(key) = &config.cipher_key {
78 if let Some(nonce) = &config.cipher_nonce {
79 #[warn(unreachable_patterns)]
80 match config.cipher {
81 #[cfg(feature = "decode")]
82 CipherKind::AES128GCM => {
83 let encryptor = Aes128Gcm::new(key, nonce)?;
84 Ok(Some(Box::new(encryptor)))
85 }
86 #[cfg(feature = "decode")]
87 CipherKind::AES256GCM => {
88 let encryptor = Aes256Gcm::new(key, nonce)?;
89 Ok(Some(Box::new(encryptor)))
90 }
91 CipherKind::AES128GCMSIV => {
92 let encryptor = Aes128GcmSiv::new(key, nonce)?;
93 Ok(Some(Box::new(encryptor)))
94 }
95 CipherKind::AES256GCMSIV => {
96 let encryptor = Aes256GcmSiv::new(key, nonce)?;
97 Ok(Some(Box::new(encryptor)))
98 }
99 CipherKind::NONE => Ok(None),
100 unknown => Err(LogError::Crypto(format!("unknown cryption {}", unknown))),
101 }
102 } else {
103 Ok(None)
104 }
105 } else {
106 Ok(None)
107 }
108}
109
110pub fn create_compress(config: &EZLogConfig) -> Option<Box<dyn Compress>> {
111 match config.compress {
112 CompressKind::ZLIB => Some(Box::new(ZlibCodec::new(&config.compress_level))),
113 CompressKind::NONE => None,
114 CompressKind::UNKNOWN => None,
115 }
116}
117
118pub struct EZLogger {
119 pub(crate) config: Rc<EZLogConfig>,
120 pub(crate) appender: EZAppender,
121 pub(crate) compression: Option<Box<dyn Compress>>,
122 pub(crate) cryptor: Option<Box<dyn Cryptor>>,
123}
124
125impl EZLogger {
126 pub fn new(config: EZLogConfig) -> Result<Self> {
127 let rc_conf = Rc::new(config);
128 let mut appender = EZAppender::new(Rc::clone(&rc_conf))?;
129 appender.check_config_rolling(&rc_conf)?;
130 let compression = create_compress(&rc_conf);
131 let cryptor = create_cryptor(&rc_conf)?;
132
133 Ok(Self {
134 config: Rc::clone(&rc_conf),
135 appender,
136 compression,
137 cryptor,
138 })
139 }
140
141 pub(crate) fn append(&mut self, record: &EZRecord) -> Result<()> {
142 if record.content().len() > self.config.max_size as usize / 2 {
143 let mut e: Option<LogError> = None;
144
145 record.trunks(&self.config).iter().for_each(|record| {
146 match self
147 .encode_as_block(record)
148 .map(|buf| self.appender.write_all(&buf))
149 {
150 Ok(_) => {}
151 Err(err) => e = Some(err),
152 }
153 });
154 e.map_or(Ok(()), Err)
155 } else {
156 let buf = self.encode_as_block(record)?;
157 self.appender.write_all(&buf).map_err(|e| e.into())
158 }
159 }
160
161 #[inline]
162 fn encode(&mut self, record: &EZRecord) -> Result<Vec<u8>> {
163 let nonce_fn: NonceGenFn = self.gen_nonce();
164 let mut buf = self.format(record)?;
165 if buf.is_empty() {
166 return Ok(buf);
167 }
168 if self.config.version == Version::V1 {
169 if let Some(encryptor) = &self.cryptor {
170 event!(Event::Encrypt, &record.t_id());
171 buf = encryptor.encrypt(&buf, nonce_fn)?;
172 event!(Event::EncryptEnd, &record.t_id());
173 }
174 if let Some(compression) = &self.compression {
175 event!(Event::Compress, &record.t_id());
176 buf = compression.compress(&buf).map_err(LogError::Compress)?;
177 event!(Event::CompressEnd, &record.t_id());
178 }
179 } else {
180 let len = buf.len();
181 if let Some(compression) = &self.compression {
182 event!(Event::Compress, &record.t_id());
183 buf = compression.compress(&buf).map_err(LogError::Compress)?;
184 event!(
185 Event::CompressEnd,
186 &format!(
187 "{} compress ratio = {} ",
188 &record.t_id(),
189 buf.len() as f64 / len as f64
190 )
191 );
192 }
193 if let Some(encryptor) = &self.cryptor {
194 event!(Event::Encrypt, &record.t_id());
195 buf = encryptor.encrypt(&buf, nonce_fn)?;
196 event!(
197 Event::EncryptEnd,
198 &format!(
199 "{} process ratio = {} ",
200 &record.t_id(),
201 buf.len() as f64 / len as f64
202 )
203 );
204 }
205 }
206 Ok(buf)
207 }
208
209 fn gen_nonce(&mut self) -> NonceGenFn {
219 let timestamp = self.appender.inner.header().timestamp.unix_timestamp();
220 let position = self.appender.inner.header().recorder_position;
221 let combine = combine_time_position(timestamp, position.into());
222
223 Box::new(move |input| xor_slice(input, &combine))
225 }
226
227 #[inline]
229 pub fn encode_as_block(&mut self, record: &EZRecord) -> Result<Vec<u8>> {
230 let buf = self.encode(record)?;
231 encode_content(buf)
232 }
233
234 fn format(&self, record: &EZRecord) -> Result<Vec<u8>> {
235 crate::formatter().format(record)
236 }
237
238 pub(crate) fn flush(&mut self) -> std::result::Result<(), io::Error> {
239 self.appender.flush()
240 }
241
242 pub(crate) fn trim(&self) {
243 match fs::read_dir(&self.config.dir_path) {
244 Ok(dir) => {
245 for file in dir {
246 match file {
247 Ok(file) => {
248 if let Some(name) = file.file_name().to_str() {
249 match self.config.is_file_out_of_date(name) {
250 Ok(out_of_date) => {
251 if out_of_date {
252 fs::remove_file(file.path()).unwrap_or_else(|e| {
253 event!(
254 Event::TrimError,
255 "remove file err",
256 &e.into()
257 )
258 });
259 }
260 }
261 Err(e) => {
262 event!(Event::TrimError, "judge file out of date error", &e)
263 }
264 }
265 };
266 }
267 Err(e) => {
268 event!(Event::TrimError, "traversal file error", &e.into())
269 }
270 }
271 }
272 }
273 Err(e) => event!(Event::TrimError, "read dir error", &e.into()),
274 }
275 }
276
277 pub fn query_log_files_for_date(&self, date: OffsetDateTime) -> Vec<PathBuf> {
278 self.config.query_log_files_for_date(date)
279 }
280
281 pub(crate) fn rotate_if_not_empty(&mut self) -> Result<()> {
282 if self
283 .appender
284 .inner
285 .header()
286 .has_record_exclude_extra(&self.config)
287 {
288 self.appender.rotate()
289 } else {
290 Ok(())
291 }
292 }
293}
294
295pub(crate) fn combine_time_position(timestamp: i64, position: u64) -> Vec<u8> {
296 let position_bytes = position.to_be_bytes();
297 let time_bytes = timestamp.to_be_bytes();
298 let mut vec = time_bytes.to_vec();
299 vec.extend(position_bytes);
300 vec
301}
302
303pub(crate) fn xor_slice<'a>(slice: &'a [u8], vec: &'a [u8]) -> Vec<u8> {
304 let mut result = Vec::with_capacity(slice.len());
305 for (i, byte) in slice.iter().enumerate() {
306 if let Some(vec_byte) = vec.get(i) {
307 result.push(byte ^ vec_byte);
308 } else {
309 result.push(*byte);
310 }
311 }
312 result
313}
314
315use bitflags::bitflags;
316
317bitflags! {
318 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
319 pub(crate) struct Flags: u8 {
320 const NONE = 0b0000_0000;
321 const HAS_EXTRA = 0b0000_0001;
322 }
323}
324
325#[derive(Debug, Clone, PartialEq, Eq, Hash)]
330pub struct Header {
331 pub(crate) version: Version,
333 pub(crate) flag: Flags,
335 pub(crate) recorder_position: u32,
337 pub(crate) compress: CompressKind,
339 pub(crate) cipher: CipherKind,
341 pub(crate) cihper_hash: u32,
343 pub(crate) timestamp: OffsetDateTime,
345 pub(crate) rotate_time: Option<OffsetDateTime>,
347}
348
349impl Default for Header {
350 fn default() -> Self {
351 Self::new()
352 }
353}
354
355impl Header {
356 #[allow(deprecated)]
357 pub fn new() -> Self {
358 Header {
359 version: Version::V2,
360 flag: Flags::NONE,
361 recorder_position: 0,
362 compress: CompressKind::ZLIB,
363 cipher: CipherKind::AES128GCM,
364 cihper_hash: 0,
365 timestamp: OffsetDateTime::now_utc(),
366 rotate_time: None,
367 }
368 }
369
370 pub fn empty() -> Self {
371 Header {
372 version: Version::UNKNOWN,
373 flag: Flags::NONE,
374 recorder_position: 0,
375 compress: CompressKind::NONE,
376 cipher: CipherKind::NONE,
377 cihper_hash: 0,
378 timestamp: OffsetDateTime::UNIX_EPOCH,
379 rotate_time: None,
380 }
381 }
382
383 pub fn create(config: &EZLogConfig) -> Self {
384 let time = OffsetDateTime::now_utc();
385 let rotate_time = config.rotate_time(time);
386 let flag = if config.extra.is_some() {
387 Flags::HAS_EXTRA
388 } else {
389 Flags::NONE
390 };
391 Header {
392 version: config.version,
393 flag,
394 recorder_position: 0,
395 compress: config.compress,
396 cipher: config.cipher,
397 cihper_hash: config.cipher_hash(),
398 timestamp: OffsetDateTime::now_utc(),
399 rotate_time: Some(rotate_time),
400 }
401 }
402
403 pub fn max_length() -> usize {
404 V2_LOG_HEADER_SIZE
405 }
406
407 #[inline]
408 pub fn length_compat(version: &Version) -> usize {
409 match version {
410 Version::V1 => V1_LOG_HEADER_SIZE,
411 Version::V2 => V2_LOG_HEADER_SIZE,
412 Version::UNKNOWN => 0,
413 }
414 }
415
416 pub fn length(&self) -> usize {
417 Self::length_compat(&self.version)
418 }
419
420 pub fn encode(&self, writer: &mut dyn Write) -> std::result::Result<(), io::Error> {
421 match self.version {
422 Version::V1 => self.encode_v1(writer),
423 Version::V2 => self.encode_v2(writer),
424 Version::UNKNOWN => Err(io::Error::new(
425 io::ErrorKind::InvalidInput,
426 "unknown version",
427 )),
428 }
429 }
430
431 pub fn encode_v1(&self, writer: &mut dyn Write) -> std::result::Result<(), io::Error> {
432 writer.write_all(crate::FILE_SIGNATURE)?;
433 writer.write_u8(self.version.into())?;
434 writer.write_u8(self.flag.bits())?;
435 writer.write_u32::<BigEndian>(self.recorder_position)?;
436 writer.write_u8(self.compress.into())?;
437 writer.write_u8(self.cipher.into())
438 }
439
440 pub fn encode_v2(&self, writer: &mut dyn Write) -> std::result::Result<(), io::Error> {
441 writer.write_all(crate::FILE_SIGNATURE)?;
442 writer.write_u8(self.version.into())?;
443 writer.write_u8(self.flag.bits())?;
444 writer.write_i64::<BigEndian>(self.timestamp.unix_timestamp())?;
445 writer.write_u32::<BigEndian>(self.recorder_position)?;
446 writer.write_u8(self.compress.into())?;
447 writer.write_u8(self.cipher.into())?;
448 writer.write_u32::<BigEndian>(self.cihper_hash)
449 }
450
451 pub fn decode(reader: &mut dyn Read) -> std::result::Result<Self, errors::LogError> {
452 let mut signature = [0u8; 2];
453 reader.read_exact(&mut signature)?;
454 let version = Version::from(reader.read_u8()?);
455 let flag = Flags::from_bits(reader.read_u8()?).unwrap_or(Flags::NONE);
456 let mut timestamp = OffsetDateTime::now_utc().unix_timestamp();
457 if version == Version::V2 {
458 timestamp = reader.read_i64::<BigEndian>()?
459 }
460 let mut recorder_size = reader.read_u32::<BigEndian>()?;
461 if recorder_size < Header::length_compat(&version) as u32 {
462 recorder_size = Header::length_compat(&version) as u32;
463 }
464
465 let compress = reader.read_u8()?;
466 let cipher = reader.read_u8()?;
467 let mut hash: u32 = 0;
468
469 if version == Version::V2 {
470 hash = reader.read_u32::<BigEndian>()?;
471 }
472 Ok(Header {
473 version,
474 flag,
475 recorder_position: recorder_size,
476 compress: CompressKind::from(compress),
477 cipher: CipherKind::from(cipher),
478 cihper_hash: hash,
479 timestamp: OffsetDateTime::from_unix_timestamp(timestamp)
480 .unwrap_or_else(|_| OffsetDateTime::now_utc()),
481 rotate_time: None,
482 })
483 }
484
485 pub fn decode_and_config(
486 reader: &mut dyn Read,
487 config: &EZLogConfig,
488 ) -> std::result::Result<Self, errors::LogError> {
489 let mut decode = Self::decode(reader)?;
490 if !decode.is_config() {
491 decode = Self::create(config);
492 }
493 Ok(decode)
494 }
495
496 pub fn is_match(&self, config: &EZLogConfig) -> bool {
497 self.version == config.version
498 && self.compress == config.compress
499 && self.cipher == config.cipher
500 && self.cihper_hash == config.cipher_hash()
501 }
502
503 pub fn is_empty(&self) -> bool {
504 self.recorder_position <= self.length() as u32
505 }
506
507 pub fn is_config(&self) -> bool {
508 self.version != Version::UNKNOWN
509 }
510
511 pub fn has_record(&self) -> bool {
512 self.recorder_position > self.length() as u32
513 }
514
515 pub fn has_record_exclude_extra(&self, config: &EZLogConfig) -> bool {
516 let extra_len = match &config.extra {
517 Some(e) => {
518 let record = Vec::from(e.to_owned());
519 encode_content(record).map(|r| r.len()).unwrap_or(0)
520 }
521 None => 0,
522 };
523 self.recorder_position > (self.length() + extra_len) as u32
525 }
526
527 pub fn version(&self) -> &Version {
528 &self.version
529 }
530
531 pub(crate) fn init_record_poition(&mut self) {
532 self.recorder_position = Self::length_compat(&self.version) as u32;
533 }
534}