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