1use std::{
2 cell::{Cell, RefCell},
3 cmp, env,
4 fmt::Arguments,
5 fs::{File, OpenOptions},
6 io::{Error as IoError, ErrorKind as IoErrorKind, Stdout, Write, stdout},
7 net::{SocketAddr, TcpStream, UdpSocket},
8 ops::{Deref, DerefMut},
9 path::Path,
10 str::FromStr,
11};
12
13use mio::net::UnixDatagram;
14use prost::{Message, encoding::encoded_len_varint};
15
16use crate::{
17 AsString,
18 config::{Config, DEFAULT_LOG_TARGET},
19 logging::{LogDuration, LogError, LogMessage, RequestRecord},
20 proto::command::ProtobufAccessLogFormat,
21 writer::MultiLineWriter,
22};
23
24thread_local! {
25 pub static LOGGER: RefCell<Logger> = RefCell::new(Logger::new());
26 static LOGGER_COLORED: Cell<bool> = const { Cell::new(false) };
31}
32
33pub fn is_logger_colored() -> bool {
43 LOGGER_COLORED.with(|c| c.get())
44}
45
46pub fn ansi_palette() -> (
64 &'static str,
65 &'static str,
66 &'static str,
67 &'static str,
68 &'static str,
69) {
70 if is_logger_colored() {
71 ("\x1b[1;97m", "\x1b[0m", "\x1b[37m", "\x1b[90m", "\x1b[97m")
72 } else {
73 ("", "", "", "", "")
74 }
75}
76
77pub static COMPAT_LOGGER: CompatLogger = CompatLogger;
81
82#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
83#[serde(deny_unknown_fields, rename_all = "lowercase")]
84pub enum AccessLogFormat {
85 Ascii,
86 Protobuf,
87}
88
89impl From<&ProtobufAccessLogFormat> for AccessLogFormat {
90 fn from(value: &ProtobufAccessLogFormat) -> Self {
91 match value {
92 ProtobufAccessLogFormat::Ascii => Self::Ascii,
93 ProtobufAccessLogFormat::Protobuf => Self::Protobuf,
94 }
95 }
96}
97
98impl From<&Option<AccessLogFormat>> for ProtobufAccessLogFormat {
99 fn from(value: &Option<AccessLogFormat>) -> Self {
100 match value {
101 Some(AccessLogFormat::Ascii) | None => Self::Ascii,
102 Some(AccessLogFormat::Protobuf) => Self::Protobuf,
103 }
104 }
105}
106
107pub struct InnerLogger {
108 directives: Vec<LogDirective>,
109 backend: LoggerBackend,
110 log_target: String,
112 pub colored: bool,
113 access_backend: Option<LoggerBackend>,
115 access_logs_target: Option<String>,
117 access_format: AccessLogFormat,
119 access_colored: bool,
120 buffer: LoggerBuffer,
121}
122
123pub struct Logger {
124 inner: InnerLogger,
125 tag: String,
127 pid: i32,
129 initialized: bool,
130}
131
132impl std::ops::Deref for Logger {
133 type Target = InnerLogger;
134 fn deref(&self) -> &Self::Target {
135 &self.inner
136 }
137}
138impl std::ops::DerefMut for Logger {
139 fn deref_mut(&mut self) -> &mut Self::Target {
140 &mut self.inner
141 }
142}
143
144impl Default for Logger {
145 fn default() -> Self {
146 Self {
147 inner: InnerLogger {
148 directives: vec![LogDirective {
149 name: None,
150 level: LogLevelFilter::Error,
151 }],
152 backend: LoggerBackend::Stdout(stdout()),
153 log_target: DEFAULT_LOG_TARGET.to_string(),
154 colored: false,
155 access_backend: None,
156 access_logs_target: None,
157 access_format: AccessLogFormat::Ascii,
158 access_colored: false,
159 buffer: LoggerBuffer(Vec::with_capacity(4096)),
160 },
161 tag: "UNINITIALIZED".to_string(),
162 pid: 0,
163 initialized: false,
164 }
165 }
166}
167
168impl Logger {
169 pub fn new() -> Self {
170 Self::default()
171 }
172
173 pub fn init(
174 tag: String,
175 spec: &str,
176 log_target: &str,
177 colored: bool,
178 access_logs_target: Option<&str>,
179 access_format: Option<AccessLogFormat>,
180 access_colored: Option<bool>,
181 ) -> Result<(), LogError> {
182 println!("Logs will be sent to {log_target}");
183 let backend = target_or_default(log_target);
184
185 println!("Access logs will be sent to {access_logs_target:?}");
186 let access_backend = access_logs_target.map(target_to_backend).transpose()?;
187
188 let (directives, _errors) = parse_logging_spec(spec);
189 LOGGER.with(|logger| {
190 let mut logger = logger.borrow_mut();
191 if !logger.initialized {
192 logger.set_directives(directives);
193 logger.colored = match backend {
194 LoggerBackend::Stdout(_) => colored,
195 _ => false,
196 };
197 LOGGER_COLORED.with(|c| c.set(logger.is_colored()));
206 logger.access_colored = match (&access_backend, &backend) {
207 (Some(LoggerBackend::Stdout(_)), _) | (None, LoggerBackend::Stdout(_)) => {
208 access_colored.unwrap_or(colored)
209 }
210 _ => false,
211 };
212 logger.backend = backend;
213 logger.log_target = log_target.to_owned();
214 logger.access_backend = access_backend;
215 logger.access_logs_target = access_logs_target.map(ToOwned::to_owned);
216 logger.access_format = access_format.unwrap_or(AccessLogFormat::Ascii);
217 logger.tag = tag;
218 logger.pid = unsafe { libc::getpid() };
222 logger.initialized = true;
223
224 let _ = log::set_logger(&COMPAT_LOGGER)
225 .map_err(|e| println!("could not register compat logger: {e:?}"));
226
227 log::set_max_level(log::LevelFilter::Info);
228 }
229 });
230 Ok(())
231 }
232
233 pub fn set_directives(&mut self, directives: Vec<LogDirective>) {
234 self.directives = directives;
235 }
236
237 pub fn split(&mut self) -> (i32, &str, &mut InnerLogger) {
238 (self.pid, &self.tag, &mut self.inner)
239 }
240
241 pub fn is_colored(&self) -> bool {
243 self.inner.colored
244 }
245}
246
247struct LoggerBuffer(Vec<u8>);
248
249impl Deref for LoggerBuffer {
250 type Target = Vec<u8>;
251 fn deref(&self) -> &Self::Target {
252 &self.0
253 }
254}
255impl DerefMut for LoggerBuffer {
256 fn deref_mut(&mut self) -> &mut Self::Target {
257 &mut self.0
258 }
259}
260
261impl LoggerBuffer {
262 fn fmt<F: FnOnce(&[u8]) -> Result<usize, IoError>>(
263 &mut self,
264 args: Arguments,
265 flush: F,
266 ) -> Result<(), IoError> {
267 self.clear();
268 self.write_fmt(args)?;
269 flush(self.as_slice())?;
270 Ok(())
271 }
272}
273
274fn log_arguments(
275 args: Arguments,
276 backend: &mut LoggerBackend,
277 buffer: &mut LoggerBuffer,
278) -> Result<(), IoError> {
279 match backend {
280 LoggerBackend::Stdout(stdout) => {
281 let _ = stdout.write_fmt(args);
282 Ok(())
283 }
284 LoggerBackend::Tcp(socket) => socket.write_fmt(args),
285 LoggerBackend::File(file) => file.write_fmt(args),
286 LoggerBackend::Unix(socket) => buffer.fmt(args, |bytes| socket.send(bytes)),
287 LoggerBackend::Udp(sock, addr) => buffer.fmt(args, |b| sock.send_to(b, *addr)),
288 }
289}
290
291impl InnerLogger {
292 pub fn log(&mut self, args: Arguments) {
293 if let Err(e) = log_arguments(args, &mut self.backend, &mut self.buffer) {
294 println!("Could not write log to {}: {e:?}", self.backend.as_ref());
295 }
296 }
297
298 pub fn log_access(&mut self, log: RequestRecord) -> bool {
302 let backend = self.access_backend.as_mut().unwrap_or(&mut self.backend);
303
304 let io_result = match self.access_format {
305 AccessLogFormat::Protobuf => {
306 let binary_log = log.into_binary_access_log();
307 let log_length = binary_log.encoded_len();
308 let total_length = log_length + encoded_len_varint(log_length as u64);
309 self.buffer.clear();
310 let current_capacity = self.buffer.capacity();
311 if current_capacity < total_length {
312 self.buffer.reserve(total_length - current_capacity);
313 }
314
315 if let Err(e) = binary_log.encode_length_delimited(&mut self.buffer.0) {
316 Err(IoError::new(IoErrorKind::InvalidData, e))
317 } else {
318 self.buffer.extend_from_slice(&[0, 0]); let bytes = &self.buffer;
320 match backend {
321 LoggerBackend::Stdout(stdout) => {
322 let _ = stdout.write(bytes);
323 return true;
324 }
325 LoggerBackend::Tcp(socket) => socket.write(bytes),
326 LoggerBackend::File(file) => file.write(bytes),
327 LoggerBackend::Unix(socket) => socket.send(bytes),
328 LoggerBackend::Udp(socket, address) => socket.send_to(bytes, *address),
329 }
330 .map(|_| ())
331 }
332 }
333 AccessLogFormat::Ascii => crate::_prompt_log! {
334 logger: |args| log_arguments(args, backend, &mut self.buffer),
335 is_access: true,
336 condition: self.access_colored,
337 prompt: [
338 log.now,
339 log.precise_time,
340 log.pid,
341 log.level,
342 log.tag,
343 ],
344 standard: {
345 formats: ["{} {} {} {}/{}/{}/{}/{} {} {} [{}] {:?} {} {}{}\n"],
346 args: [
347 log.context,
348 log.session_address.as_string_or("-"),
349 log.backend_address.as_string_or("-"),
350 LogDuration(Some(log.request_time)),
351 LogDuration(Some(log.service_time)),
352 LogDuration(log.response_time),
353 LogDuration(log.client_rtt),
354 LogDuration(log.server_rtt),
355 log.bytes_in,
356 log.bytes_out,
357 log.full_tags(),
358 log.otel,
359 log.protocol,
360 log.endpoint,
361 LogMessage(log.message),
362 ]
363 },
364 colored: {
365 formats: ["\x1b[;1m{}\x1b[m {} {} {}/{}/{}/{}/{} {} {} \x1b[2m[{}] {:?} \x1b[;1m{} {:#}\x1b[m{}\n"],
366 args: @,
367 }
368 },
369 };
370
371 if let Err(e) = io_result {
372 println!("Could not write access log to {}: {e:?}", backend.as_ref());
373 println!(
374 "Trying to revive the backend of access logs to {:?}, or defaulting to {}",
375 self.access_logs_target, self.log_target
376 );
377 let log_target = self.access_logs_target.as_ref().unwrap_or(&self.log_target);
378 if let Err(err) = backend.revive(log_target) {
379 eprintln!("could not revive logger backend: {err}");
380 }
381 false
382 } else {
383 true
384 }
385 }
386
387 pub fn enabled(&self, meta: Metadata) -> bool {
388 for directive in self.directives.iter().rev() {
390 match &directive.name {
391 Some(name) if !meta.target.starts_with(name) => {}
392 Some(_) | None => return meta.level <= directive.level,
393 }
394 }
395 false
396 }
397
398 fn compat_enabled(&self, meta: &log::Metadata) -> bool {
399 for directive in self.directives.iter().rev() {
401 match &directive.name {
402 Some(name) if !meta.target().starts_with(name) => {}
403 Some(_) | None => return LogLevel::from(meta.level()) <= directive.level,
404 }
405 }
406 false
407 }
408}
409
410pub enum LoggerBackend {
411 Stdout(Stdout),
412 Unix(UnixDatagram),
413 Udp(UdpSocket, SocketAddr),
414 Tcp(TcpStream),
415 File(crate::writer::MultiLineWriter<File>),
416}
417
418impl LoggerBackend {
419 fn revive(&mut self, log_target: &str) -> Result<(), LogError> {
420 *self = target_to_backend(log_target)?;
421 Ok(())
422 }
423}
424
425#[repr(usize)]
426#[derive(Clone, Copy, Eq, Debug)]
427pub enum LogLevel {
428 Error = 1, Warn,
436 Info,
440 Debug,
444 Trace,
448}
449
450static LOG_LEVEL_NAMES: [&str; 6] = ["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"];
451
452impl PartialEq for LogLevel {
453 #[inline]
454 fn eq(&self, other: &LogLevel) -> bool {
455 *self as usize == *other as usize
456 }
457}
458
459impl PartialEq<LogLevelFilter> for LogLevel {
460 #[inline]
461 fn eq(&self, other: &LogLevelFilter) -> bool {
462 *self as usize == *other as usize
463 }
464}
465
466impl PartialOrd for LogLevel {
467 #[inline]
468 fn partial_cmp(&self, other: &LogLevel) -> Option<cmp::Ordering> {
469 Some(self.cmp(other))
470 }
471}
472
473impl PartialOrd<LogLevelFilter> for LogLevel {
474 #[inline]
475 fn partial_cmp(&self, other: &LogLevelFilter) -> Option<cmp::Ordering> {
476 Some((*self as usize).cmp(&(*other as usize)))
477 }
478}
479
480impl Ord for LogLevel {
481 #[inline]
482 fn cmp(&self, other: &LogLevel) -> cmp::Ordering {
483 (*self as usize).cmp(&(*other as usize))
484 }
485}
486
487impl LogLevel {
488 fn from_usize(u: usize) -> Option<LogLevel> {
489 match u {
490 1 => Some(LogLevel::Error),
491 2 => Some(LogLevel::Warn),
492 3 => Some(LogLevel::Info),
493 4 => Some(LogLevel::Debug),
494 5 => Some(LogLevel::Trace),
495 _ => None,
496 }
497 }
498
499 #[inline]
501 pub fn max() -> LogLevel {
502 LogLevel::Trace
503 }
504
505 #[inline]
507 pub fn to_log_level_filter(self) -> LogLevelFilter {
508 LogLevelFilter::from_usize(self as usize).unwrap()
509 }
510}
511
512#[repr(usize)]
513#[derive(Clone, Copy, Eq, Debug)]
514pub enum LogLevelFilter {
515 Off,
516 Error,
517 Warn,
518 Info,
519 Debug,
520 Trace,
521}
522
523impl PartialEq for LogLevelFilter {
524 #[inline]
525 fn eq(&self, other: &LogLevelFilter) -> bool {
526 *self as usize == *other as usize
527 }
528}
529
530impl PartialEq<LogLevel> for LogLevelFilter {
531 #[inline]
532 fn eq(&self, other: &LogLevel) -> bool {
533 other.eq(self)
534 }
535}
536
537impl PartialOrd for LogLevelFilter {
538 #[inline]
539 fn partial_cmp(&self, other: &LogLevelFilter) -> Option<cmp::Ordering> {
540 Some(self.cmp(other))
541 }
542}
543
544impl PartialOrd<LogLevel> for LogLevelFilter {
545 #[inline]
546 fn partial_cmp(&self, other: &LogLevel) -> Option<cmp::Ordering> {
547 other.partial_cmp(self).map(|x| x.reverse())
548 }
549}
550
551impl Ord for LogLevelFilter {
552 #[inline]
553 fn cmp(&self, other: &LogLevelFilter) -> cmp::Ordering {
554 (*self as usize).cmp(&(*other as usize))
555 }
556}
557
558impl FromStr for LogLevelFilter {
559 type Err = ();
560 fn from_str(level: &str) -> Result<LogLevelFilter, ()> {
561 LOG_LEVEL_NAMES
562 .iter()
563 .position(|&name| name.eq_ignore_ascii_case(level))
564 .map(|p| LogLevelFilter::from_usize(p).unwrap())
565 .ok_or(())
566 }
567}
568
569impl LogLevelFilter {
570 fn from_usize(u: usize) -> Option<LogLevelFilter> {
571 match u {
572 0 => Some(LogLevelFilter::Off),
573 1 => Some(LogLevelFilter::Error),
574 2 => Some(LogLevelFilter::Warn),
575 3 => Some(LogLevelFilter::Info),
576 4 => Some(LogLevelFilter::Debug),
577 5 => Some(LogLevelFilter::Trace),
578 _ => None,
579 }
580 }
581 #[inline]
583 pub fn max() -> LogLevelFilter {
584 LogLevelFilter::Trace
585 }
586
587 #[inline]
591 pub fn to_log_level(self) -> Option<LogLevel> {
592 LogLevel::from_usize(self as usize)
593 }
594}
595
596#[derive(Debug)]
598pub struct Metadata {
599 pub level: LogLevel,
600 pub target: &'static str,
601}
602
603#[derive(Debug)]
604pub struct LogDirective {
605 name: Option<String>,
606 level: LogLevelFilter,
607}
608
609#[derive(thiserror::Error, Debug)]
610pub enum LogSpecParseError {
611 #[error("Too many '/'s: {0}")]
612 TooManySlashes(String),
613 #[error("Too many '='s: {0}")]
614 TooManyEquals(String),
615 #[error("Invalid log level: {0}")]
616 InvalidLogLevel(String),
617}
618
619pub fn parse_logging_spec(spec: &str) -> (Vec<LogDirective>, Vec<LogSpecParseError>) {
620 let mut dirs = Vec::new();
621 let mut errors = Vec::new();
622
623 let mut parts = spec.split('/');
624 let mods = parts.next();
625 let _ = parts.next();
626 if parts.next().is_some() {
627 errors.push(LogSpecParseError::TooManySlashes(spec.to_string()));
628 }
629 if let Some(m) = mods {
630 for s in m.split(',') {
631 if s.is_empty() {
632 continue;
633 }
634 let mut parts = s.split('=');
635 let (log_level, name) =
636 match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
637 (Some(part0), None, None) => {
638 match part0.parse() {
641 Ok(num) => (num, None),
642 Err(_) => {
643 errors.push(LogSpecParseError::InvalidLogLevel(s.to_string()));
644 (LogLevelFilter::max(), None)
645 }
646 }
647 }
648 (Some(part0), Some(""), None) => (LogLevelFilter::max(), Some(part0)),
649 (Some(part0), Some(part1), None) => match part1.parse() {
650 Ok(num) => (num, Some(part0)),
651 Err(_) => {
652 errors.push(LogSpecParseError::InvalidLogLevel(s.to_string()));
653 continue;
654 }
655 },
656 _ => {
657 errors.push(LogSpecParseError::TooManyEquals(s.to_string()));
658 continue;
659 }
660 };
661 dirs.push(LogDirective {
662 name: name.map(|s| s.to_string()),
663 level: log_level,
664 });
665 }
666 }
667
668 for error in &errors {
669 println!("{error:?}");
670 }
671 (dirs, errors)
672}
673
674pub fn setup_default_logging(
676 log_colored: bool,
677 log_level: &str,
678 tag: &str,
679) -> Result<(), LogError> {
680 setup_logging("stdout", log_colored, None, None, None, log_level, tag)
681}
682
683pub fn setup_logging_with_config(config: &Config, tag: &str) -> Result<(), LogError> {
685 setup_logging(
686 &config.log_target,
687 config.log_colored,
688 config.access_logs_target.as_deref(),
689 config.access_logs_format.clone(),
690 config.access_logs_colored,
691 &config.log_level,
692 tag,
693 )
694}
695
696pub fn setup_logging(
701 log_target: &str,
702 log_colored: bool,
703 access_logs_target: Option<&str>,
704 access_logs_format: Option<AccessLogFormat>,
705 access_logs_colored: Option<bool>,
706 log_level: &str,
707 tag: &str,
708) -> Result<(), LogError> {
709 Logger::init(
710 tag.to_string(),
711 env::var("RUST_LOG").as_deref().unwrap_or(log_level),
712 log_target,
713 log_colored,
714 access_logs_target,
715 access_logs_format,
716 access_logs_colored,
717 )
718}
719
720fn target_or_default(target: &str) -> LoggerBackend {
722 match target_to_backend(target) {
723 Ok(backend) => backend,
724 Err(target_error) => {
725 eprintln!("{target_error}, defaulting to stdout");
726 LoggerBackend::Stdout(stdout())
727 }
728 }
729}
730
731pub fn target_to_backend(target: &str) -> Result<LoggerBackend, LogError> {
732 if target == "stdout" {
733 return Ok(LoggerBackend::Stdout(stdout()));
734 }
735
736 if let Some(addr) = target.strip_prefix("udp://") {
737 let address = addr
738 .parse::<SocketAddr>()
739 .map_err(|e| LogError::InvalidSocketAddress(target.to_owned(), e))?;
740
741 let socket = UdpSocket::bind(("0.0.0.0", 0)).map_err(LogError::UdpBind)?;
742
743 return Ok(LoggerBackend::Udp(socket, address));
744 }
745
746 if let Some(addr) = target.strip_prefix("tcp://") {
747 let tcp_stream =
748 TcpStream::connect(addr).map_err(|e| LogError::TcpConnect(target.to_owned(), e))?;
749
750 return Ok(LoggerBackend::Tcp(tcp_stream));
751 }
752
753 if let Some(addr) = target.strip_prefix("unix://") {
754 let socket = UnixDatagram::unbound().map_err(LogError::CreateUnixSocket)?;
755
756 socket
757 .connect(addr)
758 .map_err(|e| LogError::ConnectToUnixSocket(target.to_owned(), e))?;
759
760 return Ok(LoggerBackend::Unix(socket));
761 }
762
763 if let Some(addr) = target.strip_prefix("file://") {
764 let path = Path::new(addr);
765 let file = OpenOptions::new()
766 .create(true)
767 .append(true)
768 .open(path)
769 .map_err(|e| LogError::OpenFile(target.to_owned(), e))?;
770
771 return Ok(LoggerBackend::File(MultiLineWriter::new(file)));
772 }
773
774 Err(LogError::InvalidLogTarget(
775 target.to_owned(),
776 "Log target is not parseable",
777 ))
778}
779
780#[macro_export]
781macro_rules! _prompt_log {
782 {
783 logger: $logger:expr,
784 is_access: $access:expr,
785 condition: $cond:expr,
786 prompt: [$($p:tt)*],
787 standard: {$($std:tt)*}$(,)?
788 } => {
789 $crate::_prompt_log!{
790 logger: $logger,
791 is_access: $access,
792 condition: $cond,
793 prompt: [$($p)*],
794 standard: {$($std)*},
795 colored: {$($std)*},
796 }
797 };
798 {
799 logger: $logger:expr,
800 is_access: $access:expr,
801 condition: $cond:expr,
802 prompt: [$($p:tt)*],
803 standard: {
804 formats: [$($std_fmt:tt)*],
805 args: [$($std_args:expr),*$(,)?]$(,)?
806 },
807 colored: {
808 formats: [$($col_fmt:tt)*],
809 args: @$(,)?
810 }$(,)?
811 } => {
812 $crate::_prompt_log!{
813 logger: $logger,
814 is_access: $access,
815 condition: $cond,
816 prompt: [$($p)*],
817 standard: {
818 formats: [$($std_fmt)*],
819 args: [$($std_args),*],
820 },
821 colored: {
822 formats: [$($col_fmt)*],
823 args: [$($std_args),*],
824 },
825 }
826 };
827 {
828 logger: $logger:expr,
829 is_access: $access:expr,
830 condition: $cond:expr,
831 prompt: [$now:expr, $precise_time:expr, $pid:expr, $lvl:expr, $tag:expr$(,)?],
832 standard: {
833 formats: [$($std_fmt:tt)*],
834 args: [$($std_args:expr),*$(,)?]$(,)?
835 },
836 colored: {
837 formats: [$($col_fmt:tt)*],
838 args: [$($col_args:expr),*$(,)?]$(,)?
839 }$(,)?
840 } => {
841 if $cond {
842 $crate::_prompt_log!(@bind [$logger, concat!("{} \x1b[2m{} \x1b[;2;1m{} {} \x1b[0;1m{}\x1b[m\t", $($col_fmt)*)] [$now, $precise_time, $pid, $lvl.as_str($access, true), $tag] $($col_args),*)
843 } else {
844 $crate::_prompt_log!(@bind [$logger, concat!("{} {} {} {} {}\t", $($std_fmt)*)] [$now, $precise_time, $pid, $lvl.as_str($access, false), $tag] $($std_args),*)
845 }
846 };
847 (@bind [$logger:expr, $fmt:expr] [$($bindings:expr),*] $arg:expr $(, $args:expr)*) => {{
848 let binding = &$arg;
849 $crate::_prompt_log!(@bind [$logger, $fmt] [$($bindings),* , binding] $($args),*)
850 }};
851 (@bind [$logger:expr, $fmt:expr] [$($bindings:expr),*]) => {
852 $logger(format_args!($fmt, $($bindings),*))
853 };
854}
855
856#[derive(Clone, Copy, Debug)]
857pub struct LogLineCachedState(u8);
858const LOG_LINE_ENABLED: u8 = 1 << 7;
859
860impl Default for LogLineCachedState {
861 fn default() -> Self {
862 Self::new()
863 }
864}
865
866impl LogLineCachedState {
867 pub const fn new() -> Self {
868 Self(0)
869 }
870 #[inline(always)]
871 pub fn version(&self) -> u8 {
872 self.0 & !LOG_LINE_ENABLED
873 }
874 #[inline(always)]
875 pub fn enabled(&self) -> bool {
876 self.0 & LOG_LINE_ENABLED != 0
877 }
878 #[inline(always)]
879 pub fn set(&mut self, version: u8, enabled: bool) {
880 self.0 = version;
881 if enabled {
882 self.0 |= LOG_LINE_ENABLED
883 }
884 }
885}
886
887#[macro_export]
888macro_rules! _log_enabled {
889 ($logger:expr, $lvl:expr) => {{
890 let logger = $logger.borrow_mut();
891 if !logger.enabled($crate::logging::Metadata {
892 level: $lvl,
893 target: module_path!(),
894 }) {
895 return;
896 }
897 logger
898 }};
899}
900
901#[macro_export]
902macro_rules! _log {
903 ($lvl:expr, $format:expr $(, $args:expr)*) => {{
904 $crate::logging::LOGGER.with(|logger| {
905 let mut logger = $crate::_log_enabled!(logger, $lvl);
906 let (pid, tag, inner) = logger.split();
907 let (now, precise_time) = $crate::logging::now();
908
909 $crate::_prompt_log!{
910 logger: |args| inner.log(args),
911 is_access: false,
912 condition: inner.colored,
913 prompt: [now, precise_time, pid, $lvl, tag],
914 standard: {
915 formats: [$format, '\n'],
916 args: [$($args),*]
917 }
918 };
919 })
920 }};
921}
922
923#[macro_export]
924macro_rules! _log_access {
925 ($lvl:expr, $on_failure:block, $($request_record_fields:tt)*) => {{
926 $crate::logging::LOGGER.with(|logger| {
927 let success = {
928 let mut logger = $crate::_log_enabled!(logger, $lvl);
929 let (pid, tag, inner) = logger.split();
930 let (now, precise_time) = $crate::logging::now();
931
932 inner.log_access(
933 $crate::_structured_access_log!(
934 [$crate::logging::RequestRecord]
935 pid, tag, now, precise_time, level: $lvl, $($request_record_fields)*
936 )
937 )
938 }; if !success {
941 $on_failure
944 }
945 });
946 }};
947}
948
949#[macro_export]
950macro_rules! _structured_access_log {
951 ([$($struct_name:tt)+] $($fields:tt)*) => {{
952 $($struct_name)+ {$(
953 $fields
954 )*}
955 }};
956}
957
958#[macro_export]
959macro_rules! log_access {
961 ($error:expr, on_failure: $on_failure:block, $($request_record_fields:tt)*) => {
962 let lvl = if $error {
963 $crate::logging::LogLevel::Error
964 } else {
965 $crate::logging::LogLevel::Info
966 };
967 _log_access!(lvl, $on_failure, $($request_record_fields)*);
968 };
969}
970
971#[macro_export]
973macro_rules! error_access {
974 (on_failure: $on_failure:block, $($request_record_fields:tt)*) => {
975 $crate::_log_access!($crate::logging::LogLevel::Error, $on_failure, $($request_record_fields)*);
976 };
977}
978
979#[macro_export]
981macro_rules! info_access {
982 (on_failure: $on_failure:block, $($request_record_fields:tt)*) => {
983 $crate::_log_access!($crate::logging::LogLevel::Info, $on_failure, $($request_record_fields)*);
984 };
985}
986
987#[macro_export]
989macro_rules! error {
990 ($format:expr $(, $args:expr)* $(,)?) => {
991 $crate::_log!($crate::logging::LogLevel::Error, $format $(, $args)*)
992 };
993}
994
995#[macro_export]
997macro_rules! warn {
998 ($format:expr $(, $args:expr)* $(,)?) => {
999 $crate::_log!($crate::logging::LogLevel::Warn, $format $(, $args)*)
1000 };
1001}
1002
1003#[macro_export]
1005macro_rules! info {
1006 ($format:expr $(, $args:expr)* $(,)?) => {
1007 $crate::_log!($crate::logging::LogLevel::Info, $format $(, $args)*)
1008 };
1009}
1010
1011#[macro_export]
1013macro_rules! debug {
1014 ($format:expr $(, $args:expr)* $(,)?) => {{
1015 #[cfg(any(debug_assertions, feature = "logs-debug", feature = "logs-trace"))]
1016 $crate::_log!($crate::logging::LogLevel::Debug, concat!("{}\t", $format), module_path!() $(, $args)*);
1017 #[cfg(not(any(debug_assertions, feature = "logs-debug", feature = "logs-trace")))]
1024 if false {$( let _ = $args; )*}
1025 }};
1026}
1027
1028#[macro_export]
1030macro_rules! trace {
1031 ($format:expr $(, $args:expr)* $(,)?) => {{
1032 #[cfg(any(debug_assertions, feature = "logs-trace"))]
1033 $crate::_log!($crate::logging::LogLevel::Trace, concat!("{}\t", $format), module_path!() $(, $args)*);
1034 #[cfg(not(any(debug_assertions, feature = "logs-trace")))]
1035 if false {$( let _ = $args; )*}
1036 }};
1037}
1038
1039#[macro_export]
1041macro_rules! fixme {
1042 ($(, $args:expr)* $(,)?) => {
1043 $crate::_log!($crate::logging::LogLevel::Info, "FIXME: {}:{} in {}: {}", file!(), line!(), module_path!() $(, $args)*)
1044 };
1045}
1046
1047pub struct CompatLogger;
1048
1049impl From<log::Level> for LogLevel {
1050 fn from(lvl: log::Level) -> Self {
1051 match lvl {
1052 log::Level::Error => LogLevel::Error,
1053 log::Level::Warn => LogLevel::Warn,
1054 log::Level::Info => LogLevel::Info,
1055 log::Level::Debug => LogLevel::Debug,
1056 log::Level::Trace => LogLevel::Trace,
1057 }
1058 }
1059}
1060
1061impl log::Log for CompatLogger {
1062 fn enabled(&self, _: &log::Metadata) -> bool {
1063 true
1064 }
1065
1066 fn log(&self, record: &log::Record) {
1067 LOGGER.with(|logger| {
1068 let mut logger = logger.borrow_mut();
1069 if !logger.compat_enabled(record.metadata()) {
1070 return;
1071 }
1072 let (pid, tag, inner) = logger.split();
1073 let (now, precise_time) = now();
1074 crate::_prompt_log! {
1075 logger: |args| inner.log(args),
1076 is_access: false,
1077 condition: inner.colored,
1078 prompt: [
1079 now, precise_time, pid, LogLevel::from(record.level()), tag
1080 ],
1081 standard: {
1082 formats: ["{}\n"],
1083 args: [record.args()]
1084 }
1085 };
1086 })
1087 }
1088
1089 fn flush(&self) {}
1090}
1091
1092#[macro_export]
1094macro_rules! setup_test_logger {
1095 () => {
1096 let _ = $crate::logging::Logger::init(
1097 module_path!().to_string(),
1098 "error",
1099 sozu_command_lib::config::DEFAULT_LOG_TARGET,
1100 false,
1101 None,
1102 None,
1103 None,
1104 );
1105 };
1106}
1107
1108pub struct Rfc3339Time {
1109 pub inner: ::time::OffsetDateTime,
1110}
1111
1112pub fn now() -> (Rfc3339Time, i128) {
1114 let t = time::OffsetDateTime::now_utc();
1115 (
1116 Rfc3339Time { inner: t },
1117 (t - time::OffsetDateTime::UNIX_EPOCH).whole_nanoseconds(),
1118 )
1119}