|
1 | 1 | use std::{ |
| 2 | + borrow::Cow, |
2 | 3 | ffi::{c_char, c_void, CStr}, |
3 | 4 | ptr, |
4 | 5 | }; |
5 | 6 |
|
| 7 | +use log::Level; |
6 | 8 | use open62541_sys::{vsnprintf_va_copy, vsnprintf_va_end, UA_LogCategory, UA_LogLevel, UA_Logger}; |
7 | 9 |
|
8 | 10 | use crate::ua; |
9 | 11 |
|
| 12 | +// This matches the crate name. |
10 | 13 | const LOG_TARGET: &str = "open62541_sys"; |
11 | 14 |
|
12 | 15 | /// Creates logger that forwards to the `log` crate. |
13 | 16 | /// |
14 | | -/// We can use this to prevent `open62541` from installing its own default logger (which outputs any |
| 17 | +/// We can use this to prevent `open62541` from installing its own default logger (which outputs all |
15 | 18 | /// logs to stdout/stderr directly). |
16 | 19 | pub(crate) fn logger() -> ua::Logger { |
17 | 20 | unsafe extern "C" fn log_c( |
18 | 21 | _log_context: *mut c_void, |
19 | 22 | level: UA_LogLevel, |
20 | | - _category: UA_LogCategory, |
| 23 | + category: UA_LogCategory, |
21 | 24 | msg: *const c_char, |
22 | 25 | args: open62541_sys::va_list_, |
23 | 26 | ) { |
24 | | - let Some(msg) = format_message(msg, args) else { |
25 | | - log::error!(target: LOG_TARGET, "Unknown log message"); |
| 27 | + let level = match level { |
| 28 | + // Without fatal level in `log`, fall back to error. |
| 29 | + UA_LogLevel::UA_LOGLEVEL_FATAL | UA_LogLevel::UA_LOGLEVEL_ERROR => Level::Error, |
| 30 | + UA_LogLevel::UA_LOGLEVEL_WARNING => Level::Warn, |
| 31 | + UA_LogLevel::UA_LOGLEVEL_INFO => Level::Info, |
| 32 | + UA_LogLevel::UA_LOGLEVEL_DEBUG => Level::Debug, |
| 33 | + UA_LogLevel::UA_LOGLEVEL_TRACE => Level::Trace, |
| 34 | + // Handle unexpected level by escalating to error. |
| 35 | + #[expect(clippy::match_same_arms, reason = "distinction of cases")] |
| 36 | + _ => Level::Error, |
| 37 | + }; |
| 38 | + |
| 39 | + if !log::log_enabled!(target: LOG_TARGET, level) { |
| 40 | + // Bail out early to skip formatting message. |
26 | 41 | return; |
| 42 | + } |
| 43 | + |
| 44 | + let msg = format_message(msg, args); |
| 45 | + let msg = match msg { |
| 46 | + Some(ref msg) => CStr::from_bytes_with_nul(msg) |
| 47 | + .unwrap_or(c"Invalid log message") |
| 48 | + .to_string_lossy(), |
| 49 | + None => Cow::Borrowed("Unknown log message"), |
27 | 50 | }; |
28 | 51 |
|
29 | | - let msg = CStr::from_bytes_with_nul(&msg) |
30 | | - .unwrap_or(c"Invalid log message") |
31 | | - .to_string_lossy(); |
| 52 | + let category = log_category(&category); |
32 | 53 |
|
33 | | - if level == UA_LogLevel::UA_LOGLEVEL_FATAL { |
34 | | - // Without fatal level in `log`, fall back to error. |
35 | | - log::error!(target: LOG_TARGET, "{msg}"); |
36 | | - } else if level == UA_LogLevel::UA_LOGLEVEL_ERROR { |
37 | | - log::error!(target: LOG_TARGET, "{msg}"); |
38 | | - } else if level == UA_LogLevel::UA_LOGLEVEL_WARNING { |
39 | | - log::warn!(target: LOG_TARGET, "{msg}"); |
40 | | - } else if level == UA_LogLevel::UA_LOGLEVEL_INFO { |
41 | | - log::info!(target: LOG_TARGET, "{msg}"); |
42 | | - } else if level == UA_LogLevel::UA_LOGLEVEL_DEBUG { |
43 | | - log::debug!(target: LOG_TARGET, "{msg}"); |
44 | | - } else if level == UA_LogLevel::UA_LOGLEVEL_TRACE { |
45 | | - log::trace!(target: LOG_TARGET, "{msg}"); |
46 | | - } else { |
47 | | - // Handle unexpected level by escalating to error. |
48 | | - log::error!(target: LOG_TARGET, "{msg}"); |
49 | | - } |
| 54 | + log::log!(target: LOG_TARGET, level, "({category}) {msg}"); |
50 | 55 | } |
51 | 56 |
|
52 | 57 | unsafe extern "C" fn clear_c(logger: *mut UA_Logger) { |
@@ -156,3 +161,32 @@ fn format_message(msg: *const c_char, args: open62541_sys::va_list_) -> Option<V |
156 | 161 |
|
157 | 162 | Some(msg_buffer) |
158 | 163 | } |
| 164 | + |
| 165 | +// These match the category names from `ua_log_stdout.c` and `ua_log_syslog.c`. |
| 166 | +const LOG_CATEGORY_NETWORK: &str = "network"; |
| 167 | +const LOG_CATEGORY_SECURECHANNEL: &str = "channel"; |
| 168 | +const LOG_CATEGORY_SESSION: &str = "session"; |
| 169 | +const LOG_CATEGORY_SERVER: &str = "server"; |
| 170 | +const LOG_CATEGORY_CLIENT: &str = "client"; |
| 171 | +const LOG_CATEGORY_USERLAND: &str = "userland"; |
| 172 | +const LOG_CATEGORY_SECURITYPOLICY: &str = "security"; |
| 173 | +const LOG_CATEGORY_EVENTLOOP: &str = "eventloop"; |
| 174 | +const LOG_CATEGORY_PUBSUB: &str = "pubsub"; |
| 175 | +const LOG_CATEGORY_DISCOVERY: &str = "discovery"; |
| 176 | +const LOG_CATEGORY_UNKNOWN: &str = "unknown"; |
| 177 | + |
| 178 | +const fn log_category(category: &UA_LogCategory) -> &'static str { |
| 179 | + match *category { |
| 180 | + UA_LogCategory::UA_LOGCATEGORY_NETWORK => LOG_CATEGORY_NETWORK, |
| 181 | + UA_LogCategory::UA_LOGCATEGORY_SECURECHANNEL => LOG_CATEGORY_SECURECHANNEL, |
| 182 | + UA_LogCategory::UA_LOGCATEGORY_SESSION => LOG_CATEGORY_SESSION, |
| 183 | + UA_LogCategory::UA_LOGCATEGORY_SERVER => LOG_CATEGORY_SERVER, |
| 184 | + UA_LogCategory::UA_LOGCATEGORY_CLIENT => LOG_CATEGORY_CLIENT, |
| 185 | + UA_LogCategory::UA_LOGCATEGORY_USERLAND => LOG_CATEGORY_USERLAND, |
| 186 | + UA_LogCategory::UA_LOGCATEGORY_SECURITYPOLICY => LOG_CATEGORY_SECURITYPOLICY, |
| 187 | + UA_LogCategory::UA_LOGCATEGORY_EVENTLOOP => LOG_CATEGORY_EVENTLOOP, |
| 188 | + UA_LogCategory::UA_LOGCATEGORY_PUBSUB => LOG_CATEGORY_PUBSUB, |
| 189 | + UA_LogCategory::UA_LOGCATEGORY_DISCOVERY => LOG_CATEGORY_DISCOVERY, |
| 190 | + _ => LOG_CATEGORY_UNKNOWN, |
| 191 | + } |
| 192 | +} |
0 commit comments