1use core::cmp;
2use core::fmt::{self, Write};
3use core::mem::MaybeUninit;
4
5use crate::ffi::{self, ngx_err_t, ngx_log_t, ngx_uint_t, NGX_MAX_ERROR_STR};
6
7pub const LOG_BUFFER_SIZE: usize = NGX_MAX_ERROR_STR as usize - b"1970/01/01 00:00:00 [info] 1#1: ".len();
12
13#[inline(always)]
15pub fn check_mask(mask: DebugMask, log_level: usize) -> bool {
16 let mask_bits: u32 = mask.into();
17 if log_level & mask_bits as usize == 0 {
18 return false;
19 }
20 true
21}
22
23#[inline]
27pub fn write_fmt<'a>(buf: &'a mut [MaybeUninit<u8>], args: fmt::Arguments<'_>) -> &'a [u8] {
28 if let Some(str) = args.as_str() {
29 str.as_bytes()
30 } else {
31 let mut buf = LogBuf::from(buf);
32 let _ = buf.write_fmt(args);
34 buf.filled()
35 }
36}
37
38#[inline]
43pub unsafe fn log_error(level: ngx_uint_t, log: *mut ngx_log_t, err: ngx_err_t, buf: &[u8]) {
44 unsafe {
45 #[cfg(ngx_feature = "have_variadic_macros")]
46 ffi::ngx_log_error_core(level, log, err, c"%*s".as_ptr(), buf.len(), buf.as_ptr());
47 #[cfg(not(ngx_feature = "have_variadic_macros"))]
48 ffi::ngx_log_error(level, log, err, c"%*s".as_ptr(), buf.len(), buf.as_ptr());
49 }
50}
51
52#[inline]
57pub unsafe fn log_debug(log: *mut ngx_log_t, err: ngx_err_t, buf: &[u8]) {
58 unsafe {
59 #[cfg(ngx_feature = "have_variadic_macros")]
60 ffi::ngx_log_error_core(
61 ffi::NGX_LOG_DEBUG as _,
62 log,
63 err,
64 c"%*s".as_ptr(),
65 buf.len(),
66 buf.as_ptr(),
67 );
68 #[cfg(not(ngx_feature = "have_variadic_macros"))]
69 ffi::ngx_log_debug_core(log, err, c"%*s".as_ptr(), buf.len(), buf.as_ptr());
70 }
71}
72
73#[macro_export]
78macro_rules! ngx_log_error {
79 ( $level:expr, $log:expr, $($arg:tt)+ ) => {
80 let log = $log;
81 let level = $level as $crate::ffi::ngx_uint_t;
82 if level < unsafe { (*log).log_level } {
83 let mut buf = [const { ::core::mem::MaybeUninit::<u8>::uninit() }; $crate::log::LOG_BUFFER_SIZE];
84 let message = $crate::log::write_fmt(&mut buf, format_args!($($arg)+));
85 unsafe { $crate::log::log_error(level, log, 0, message) };
86 }
87 }
88}
89
90#[macro_export]
92macro_rules! ngx_conf_log_error {
93 ( $level:expr, $cf:expr, $($arg:tt)+ ) => {
94 let cf: *mut $crate::ffi::ngx_conf_t = $cf;
95 let level = $level as $crate::ffi::ngx_uint_t;
96 if level < unsafe { (*(*cf).log).log_level } {
97 let mut buf = [const { ::core::mem::MaybeUninit::<u8>::uninit() }; $crate::log::LOG_BUFFER_SIZE];
98 let message = $crate::log::write_fmt(&mut buf, format_args!($($arg)+));
99 unsafe {
100 $crate::ffi::ngx_conf_log_error(level, cf, 0, c"%*s".as_ptr(), message.len(), message.as_ptr());
101 }
102 }
103 }
104}
105
106#[macro_export]
108macro_rules! ngx_log_debug {
109 ( mask: $mask:expr, $log:expr, $($arg:tt)+ ) => {
110 let log = $log;
111 if $crate::log::check_mask($mask, unsafe { (*log).log_level }) {
112 let mut buf = [const { ::core::mem::MaybeUninit::<u8>::uninit() }; $crate::log::LOG_BUFFER_SIZE];
113 let message = $crate::log::write_fmt(&mut buf, format_args!($($arg)+));
114 unsafe { $crate::log::log_debug(log, 0, message) };
115 }
116 };
117 ( $log:expr, $($arg:tt)+ ) => {
118 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::All, $log, $($arg)+);
119 }
120}
121
122#[macro_export]
126macro_rules! ngx_log_debug_http {
127 ( $request:expr, $($arg:tt)+ ) => {
128 let log = unsafe { (*$request.connection()).log };
129 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::Http, log, $($arg)+);
130 }
131}
132
133#[macro_export]
142macro_rules! ngx_log_debug_mask {
143 ( DebugMask::Core, $log:expr, $($arg:tt)+ ) => {
144 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::Core, $log, $($arg)+);
145 };
146 ( DebugMask::Alloc, $log:expr, $($arg:tt)+ ) => {
147 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::Alloc, $log, $($arg)+);
148 };
149 ( DebugMask::Mutex, $log:expr, $($arg:tt)+ ) => {
150 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::Mutex, $log, $($arg)+);
151 };
152 ( DebugMask::Event, $log:expr, $($arg:tt)+ ) => {
153 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::Event, $log, $($arg)+);
154 };
155 ( DebugMask::Http, $log:expr, $($arg:tt)+ ) => {
156 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::Http, $log, $($arg)+);
157 };
158 ( DebugMask::Mail, $log:expr, $($arg:tt)+ ) => {
159 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::Mail, $log, $($arg)+);
160 };
161 ( DebugMask::Stream, $log:expr, $($arg:tt)+ ) => {
162 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::Stream, $log, $($arg)+);
163 };
164}
165
166#[derive(Debug)]
169pub enum DebugMask {
170 Core,
172 Alloc,
174 Mutex,
176 Event,
178 Http,
180 Mail,
182 Stream,
184 All,
186}
187
188impl TryFrom<u32> for DebugMask {
189 type Error = u32;
190
191 fn try_from(value: u32) -> Result<Self, Self::Error> {
192 match value {
193 crate::ffi::NGX_LOG_DEBUG_CORE => Ok(DebugMask::Core),
194 crate::ffi::NGX_LOG_DEBUG_ALLOC => Ok(DebugMask::Alloc),
195 crate::ffi::NGX_LOG_DEBUG_MUTEX => Ok(DebugMask::Mutex),
196 crate::ffi::NGX_LOG_DEBUG_EVENT => Ok(DebugMask::Event),
197 crate::ffi::NGX_LOG_DEBUG_HTTP => Ok(DebugMask::Http),
198 crate::ffi::NGX_LOG_DEBUG_MAIL => Ok(DebugMask::Mail),
199 crate::ffi::NGX_LOG_DEBUG_STREAM => Ok(DebugMask::Stream),
200 crate::ffi::NGX_LOG_DEBUG_ALL => Ok(DebugMask::All),
201 _ => Err(0),
202 }
203 }
204}
205
206impl From<DebugMask> for u32 {
207 fn from(value: DebugMask) -> Self {
208 match value {
209 DebugMask::Core => crate::ffi::NGX_LOG_DEBUG_CORE,
210 DebugMask::Alloc => crate::ffi::NGX_LOG_DEBUG_ALLOC,
211 DebugMask::Mutex => crate::ffi::NGX_LOG_DEBUG_MUTEX,
212 DebugMask::Event => crate::ffi::NGX_LOG_DEBUG_EVENT,
213 DebugMask::Http => crate::ffi::NGX_LOG_DEBUG_HTTP,
214 DebugMask::Mail => crate::ffi::NGX_LOG_DEBUG_MAIL,
215 DebugMask::Stream => crate::ffi::NGX_LOG_DEBUG_STREAM,
216 DebugMask::All => crate::ffi::NGX_LOG_DEBUG_ALL,
217 }
218 }
219}
220
221struct LogBuf<'data> {
223 buf: &'data mut [MaybeUninit<u8>],
224 filled: usize,
225}
226
227impl<'data> LogBuf<'data> {
228 pub fn filled(&self) -> &'data [u8] {
229 unsafe {
231 let buf = self.buf.get_unchecked(..self.filled);
232 &*(buf as *const [MaybeUninit<u8>] as *const [u8])
234 }
235 }
236
237 pub fn append(&mut self, buf: &[u8]) -> &mut Self {
238 let n = cmp::min(self.buf.len() - self.filled, buf.len());
239 unsafe {
240 let src = buf.get_unchecked(..n);
242 let src: &[MaybeUninit<u8>] = core::mem::transmute(src);
244 self.buf
246 .get_unchecked_mut(self.filled..self.filled + n)
247 .copy_from_slice(src);
248 }
249 self.filled += n;
250 self
251 }
252}
253
254impl<'data> From<&'data mut [MaybeUninit<u8>]> for LogBuf<'data> {
255 fn from(buf: &'data mut [MaybeUninit<u8>]) -> Self {
256 Self { buf, filled: 0 }
257 }
258}
259
260impl fmt::Write for LogBuf<'_> {
261 fn write_str(&mut self, s: &str) -> fmt::Result {
262 self.append(s.as_bytes());
263 Ok(())
264 }
265}
266
267#[cfg(test)]
268mod tests {
269
270 use super::*;
271
272 #[test]
273 fn test_mask_lower_bound() {
274 assert!(<DebugMask as Into<u32>>::into(DebugMask::Core) == crate::ffi::NGX_LOG_DEBUG_FIRST);
275 }
276 #[test]
277 fn test_mask_upper_bound() {
278 assert!(<DebugMask as Into<u32>>::into(DebugMask::Stream) == crate::ffi::NGX_LOG_DEBUG_LAST);
279 }
280 #[test]
281 fn test_check_mask() {
282 struct MockLog {
283 log_level: usize,
284 }
285 let mock = MockLog { log_level: 16 };
286
287 let mut r = check_mask(DebugMask::Core, mock.log_level);
288 assert!(r);
289
290 r = check_mask(DebugMask::Alloc, mock.log_level);
291 assert!(!r);
292 }
293
294 #[test]
295 fn log_buffer() {
296 use core::str;
297
298 let mut buf = [const { MaybeUninit::<u8>::uninit() }; 32];
299 let mut buf = LogBuf::from(&mut buf[..]);
300 let words = ["Hello", "World"];
301
302 write!(&mut buf, "{} {}!", words[0], words[1]).unwrap();
304 assert_eq!(str::from_utf8(buf.filled()), Ok("Hello World!"));
305
306 write!(&mut buf, " This is a test, {}", usize::MAX).unwrap();
308 assert_eq!(str::from_utf8(buf.filled()), Ok("Hello World! This is a test, 184"));
309
310 write!(&mut buf, "test").unwrap();
312 assert_eq!(str::from_utf8(buf.filled()), Ok("Hello World! This is a test, 184"));
313 }
314}