1use core::cmp;
2use core::fmt::{self, Write};
3use core::mem::MaybeUninit;
4use core::ptr::NonNull;
5
6use crate::ffi::{self, ngx_err_t, ngx_log_t, ngx_uint_t, NGX_MAX_ERROR_STR};
7
8pub const DEBUG: bool = cfg!(ngx_feature = "debug");
10
11pub const LOG_BUFFER_SIZE: usize =
16 NGX_MAX_ERROR_STR as usize - b"1970/01/01 00:00:00 [info] 1#1: ".len();
17
18#[inline(always)]
27pub fn ngx_cycle_log() -> NonNull<ngx_log_t> {
28 NonNull::new(unsafe { (*nginx_sys::ngx_cycle).log }).expect("global logger")
29}
30
31#[inline(always)]
33pub fn check_mask(mask: DebugMask, log_level: usize) -> bool {
34 let mask_bits: u32 = mask.into();
35 if log_level & mask_bits as usize == 0 {
36 return false;
37 }
38 true
39}
40
41#[inline]
45pub fn write_fmt<'a>(buf: &'a mut [MaybeUninit<u8>], args: fmt::Arguments<'_>) -> &'a [u8] {
46 if let Some(str) = args.as_str() {
47 str.as_bytes()
48 } else {
49 let mut buf = LogBuf::from(buf);
50 let _ = buf.write_fmt(args);
52 buf.filled()
53 }
54}
55
56#[inline]
61pub unsafe fn log_error(level: ngx_uint_t, log: *mut ngx_log_t, err: ngx_err_t, buf: &[u8]) {
62 unsafe {
63 #[cfg(ngx_feature = "have_variadic_macros")]
64 ffi::ngx_log_error_core(level, log, err, c"%*s".as_ptr(), buf.len(), buf.as_ptr());
65 #[cfg(not(ngx_feature = "have_variadic_macros"))]
66 ffi::ngx_log_error(level, log, err, c"%*s".as_ptr(), buf.len(), buf.as_ptr());
67 }
68}
69
70#[inline]
75pub unsafe fn log_debug(log: *mut ngx_log_t, err: ngx_err_t, buf: &[u8]) {
76 unsafe {
77 #[cfg(ngx_feature = "have_variadic_macros")]
78 ffi::ngx_log_error_core(
79 ffi::NGX_LOG_DEBUG as _,
80 log,
81 err,
82 c"%*s".as_ptr(),
83 buf.len(),
84 buf.as_ptr(),
85 );
86 #[cfg(not(ngx_feature = "have_variadic_macros"))]
87 ffi::ngx_log_debug_core(log, err, c"%*s".as_ptr(), buf.len(), buf.as_ptr());
88 }
89}
90
91#[macro_export]
96macro_rules! ngx_log_error {
97 ( $level:expr, $log:expr, $($arg:tt)+ ) => {
98 let log = $log;
99 let level = $level as $crate::ffi::ngx_uint_t;
100 if level < unsafe { (*log).log_level } {
101 let mut buf =
102 [const { ::core::mem::MaybeUninit::<u8>::uninit() }; $crate::log::LOG_BUFFER_SIZE];
103 let message = $crate::log::write_fmt(&mut buf, format_args!($($arg)+));
104 unsafe { $crate::log::log_error(level, log, 0, message) };
105 }
106 }
107}
108
109#[macro_export]
111macro_rules! ngx_conf_log_error {
112 ( $level:expr, $cf:expr, $($arg:tt)+ ) => {
113 let cf: *mut $crate::ffi::ngx_conf_t = $cf;
114 let level = $level as $crate::ffi::ngx_uint_t;
115 if level < unsafe { (*(*cf).log).log_level } {
116 let mut buf =
117 [const { ::core::mem::MaybeUninit::<u8>::uninit() }; $crate::log::LOG_BUFFER_SIZE];
118 let message = $crate::log::write_fmt(&mut buf, format_args!($($arg)+));
119 unsafe {
120 $crate::ffi::ngx_conf_log_error(
121 level,
122 cf,
123 0,
124 c"%*s".as_ptr(),
125 message.len(),
126 message.as_ptr()
127 );
128 }
129 }
130 }
131}
132
133#[macro_export]
135macro_rules! ngx_log_debug {
136 ( mask: $mask:expr, $log:expr, $($arg:tt)+ ) => {
137 let log = $log;
138 if $crate::log::DEBUG && $crate::log::check_mask($mask, unsafe { (*log).log_level }) {
139 let mut buf =
140 [const { ::core::mem::MaybeUninit::<u8>::uninit() }; $crate::log::LOG_BUFFER_SIZE];
141 let message = $crate::log::write_fmt(&mut buf, format_args!($($arg)+));
142 unsafe { $crate::log::log_debug(log, 0, message) };
143 }
144 };
145 ( $log:expr, $($arg:tt)+ ) => {
146 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::All, $log, $($arg)+);
147 }
148}
149
150#[macro_export]
154macro_rules! ngx_log_debug_http {
155 ( $request:expr, $($arg:tt)+ ) => {
156 let log = unsafe { (*$request.connection()).log };
157 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::Http, log, $($arg)+);
158 }
159}
160
161#[macro_export]
170macro_rules! ngx_log_debug_mask {
171 ( DebugMask::Core, $log:expr, $($arg:tt)+ ) => {
172 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::Core, $log, $($arg)+);
173 };
174 ( DebugMask::Alloc, $log:expr, $($arg:tt)+ ) => {
175 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::Alloc, $log, $($arg)+);
176 };
177 ( DebugMask::Mutex, $log:expr, $($arg:tt)+ ) => {
178 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::Mutex, $log, $($arg)+);
179 };
180 ( DebugMask::Event, $log:expr, $($arg:tt)+ ) => {
181 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::Event, $log, $($arg)+);
182 };
183 ( DebugMask::Http, $log:expr, $($arg:tt)+ ) => {
184 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::Http, $log, $($arg)+);
185 };
186 ( DebugMask::Mail, $log:expr, $($arg:tt)+ ) => {
187 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::Mail, $log, $($arg)+);
188 };
189 ( DebugMask::Stream, $log:expr, $($arg:tt)+ ) => {
190 $crate::ngx_log_debug!(mask: $crate::log::DebugMask::Stream, $log, $($arg)+);
191 };
192}
193
194#[derive(Debug)]
197pub enum DebugMask {
198 Core,
200 Alloc,
202 Mutex,
204 Event,
206 Http,
208 Mail,
210 Stream,
212 All,
214}
215
216impl TryFrom<u32> for DebugMask {
217 type Error = u32;
218
219 fn try_from(value: u32) -> Result<Self, Self::Error> {
220 match value {
221 crate::ffi::NGX_LOG_DEBUG_CORE => Ok(DebugMask::Core),
222 crate::ffi::NGX_LOG_DEBUG_ALLOC => Ok(DebugMask::Alloc),
223 crate::ffi::NGX_LOG_DEBUG_MUTEX => Ok(DebugMask::Mutex),
224 crate::ffi::NGX_LOG_DEBUG_EVENT => Ok(DebugMask::Event),
225 crate::ffi::NGX_LOG_DEBUG_HTTP => Ok(DebugMask::Http),
226 crate::ffi::NGX_LOG_DEBUG_MAIL => Ok(DebugMask::Mail),
227 crate::ffi::NGX_LOG_DEBUG_STREAM => Ok(DebugMask::Stream),
228 crate::ffi::NGX_LOG_DEBUG_ALL => Ok(DebugMask::All),
229 _ => Err(0),
230 }
231 }
232}
233
234impl From<DebugMask> for u32 {
235 fn from(value: DebugMask) -> Self {
236 match value {
237 DebugMask::Core => crate::ffi::NGX_LOG_DEBUG_CORE,
238 DebugMask::Alloc => crate::ffi::NGX_LOG_DEBUG_ALLOC,
239 DebugMask::Mutex => crate::ffi::NGX_LOG_DEBUG_MUTEX,
240 DebugMask::Event => crate::ffi::NGX_LOG_DEBUG_EVENT,
241 DebugMask::Http => crate::ffi::NGX_LOG_DEBUG_HTTP,
242 DebugMask::Mail => crate::ffi::NGX_LOG_DEBUG_MAIL,
243 DebugMask::Stream => crate::ffi::NGX_LOG_DEBUG_STREAM,
244 DebugMask::All => crate::ffi::NGX_LOG_DEBUG_ALL,
245 }
246 }
247}
248
249struct LogBuf<'data> {
251 buf: &'data mut [MaybeUninit<u8>],
252 filled: usize,
253}
254
255impl<'data> LogBuf<'data> {
256 pub fn filled(&self) -> &'data [u8] {
257 unsafe {
259 let buf = self.buf.get_unchecked(..self.filled);
260 &*(buf as *const [MaybeUninit<u8>] as *const [u8])
262 }
263 }
264
265 pub fn append(&mut self, buf: &[u8]) -> &mut Self {
266 let n = cmp::min(self.buf.len() - self.filled, buf.len());
267 unsafe {
268 let src = buf.get_unchecked(..n);
270 let src: &[MaybeUninit<u8>] = core::mem::transmute(src);
272 self.buf
274 .get_unchecked_mut(self.filled..self.filled + n)
275 .copy_from_slice(src);
276 }
277 self.filled += n;
278 self
279 }
280}
281
282impl<'data> From<&'data mut [MaybeUninit<u8>]> for LogBuf<'data> {
283 fn from(buf: &'data mut [MaybeUninit<u8>]) -> Self {
284 Self { buf, filled: 0 }
285 }
286}
287
288impl fmt::Write for LogBuf<'_> {
289 fn write_str(&mut self, s: &str) -> fmt::Result {
290 self.append(s.as_bytes());
291 Ok(())
292 }
293}
294
295#[cfg(test)]
296mod tests {
297
298 use super::*;
299
300 #[test]
301 fn test_mask_lower_bound() {
302 assert!(<DebugMask as Into<u32>>::into(DebugMask::Core) == crate::ffi::NGX_LOG_DEBUG_FIRST);
303 }
304 #[test]
305 fn test_mask_upper_bound() {
306 assert!(
307 <DebugMask as Into<u32>>::into(DebugMask::Stream) == crate::ffi::NGX_LOG_DEBUG_LAST
308 );
309 }
310 #[test]
311 fn test_check_mask() {
312 struct MockLog {
313 log_level: usize,
314 }
315 let mock = MockLog { log_level: 16 };
316
317 let mut r = check_mask(DebugMask::Core, mock.log_level);
318 assert!(r);
319
320 r = check_mask(DebugMask::Alloc, mock.log_level);
321 assert!(!r);
322 }
323
324 #[test]
325 fn log_buffer() {
326 use core::str;
327
328 let mut buf = [const { MaybeUninit::<u8>::uninit() }; 32];
329 let mut buf = LogBuf::from(&mut buf[..]);
330 let words = ["Hello", "World"];
331
332 write!(&mut buf, "{} {}!", words[0], words[1]).unwrap();
334 assert_eq!(str::from_utf8(buf.filled()), Ok("Hello World!"));
335
336 write!(&mut buf, " This is a test, {}", usize::MAX).unwrap();
338 assert_eq!(
339 str::from_utf8(buf.filled()),
340 Ok("Hello World! This is a test, 184")
341 );
342
343 write!(&mut buf, "test").unwrap();
345 assert_eq!(
346 str::from_utf8(buf.filled()),
347 Ok("Hello World! This is a test, 184")
348 );
349 }
350}