tor_log.rs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. // Copyright (c) 2016-2017, The Tor Project, Inc. */
  2. // See LICENSE for licensing information */
  3. /// The related domain which the logging message is relevant. For example,
  4. /// log messages relevant to networking would use LogDomain::LdNet, whereas
  5. /// general messages can use LdGeneral.
  6. #[derive(Eq, PartialEq)]
  7. pub enum LogDomain {
  8. LdNet,
  9. LdGeneral,
  10. }
  11. /// The severity level at which to log messages.
  12. #[derive(Eq, PartialEq)]
  13. pub enum LogSeverity {
  14. Notice,
  15. Warn,
  16. }
  17. /// Main entry point for Rust modules to log messages.
  18. ///
  19. /// # Inputs
  20. ///
  21. /// * A `severity` of type LogSeverity, which defines the level of severity the
  22. /// message will be logged.
  23. /// * A `domain` of type LogDomain, which defines the domain the log message
  24. /// will be associated with.
  25. /// * A `function` of type &str, which defines the name of the function where
  26. /// the message is being logged. There is a current RFC for a macro that
  27. /// defines function names. When it is, we should use it. See
  28. /// https://github.com/rust-lang/rfcs/pull/1719
  29. /// * A `message` of type &str, which is the log message itself.
  30. #[macro_export]
  31. macro_rules! tor_log_msg {
  32. ($severity: path,
  33. $domain: path,
  34. $function: expr,
  35. $($message:tt)*) =>
  36. {
  37. {
  38. use std::ffi::CString;
  39. /// Default function name to log in case of errors when converting
  40. /// a function name to a CString
  41. const ERR_LOG_FUNCTION: &'static str = "tor_log_msg";
  42. /// Default message to log in case of errors when converting a log
  43. /// message to a CString
  44. const ERR_LOG_MSG: &'static str = "Unable to log message from Rust
  45. module due to error when converting to CString";
  46. let func = match CString::new($function) {
  47. Ok(n) => n,
  48. Err(_) => CString::new(ERR_LOG_FUNCTION).unwrap(),
  49. };
  50. let msg = match CString::new(format!($($message)*)) {
  51. Ok(n) => n,
  52. Err(_) => CString::new(ERR_LOG_MSG).unwrap(),
  53. };
  54. let func_ptr = func.as_ptr();
  55. let msg_ptr = msg.as_ptr();
  56. unsafe {
  57. tor_log_string(translate_severity($severity),
  58. translate_domain($domain),
  59. func_ptr, msg_ptr
  60. )
  61. }
  62. }
  63. };
  64. }
  65. /// This module exposes no-op functionality purely for the purpose of testing
  66. /// Rust at the module level.
  67. #[cfg(any(test, feature = "testing"))]
  68. pub mod log {
  69. use libc::{c_char, c_int};
  70. use super::LogDomain;
  71. use super::LogSeverity;
  72. /// Expose a no-op logging interface purely for testing Rust modules at the
  73. /// module level.
  74. pub fn tor_log_string<'a>(
  75. severity: c_int,
  76. domain: u32,
  77. function: *const c_char,
  78. message: *const c_char,
  79. ) -> (c_int, u32, String, String) {
  80. use std::ffi::CStr;
  81. let func = unsafe { CStr::from_ptr(function) }.to_str().unwrap();
  82. let func_allocated = String::from(func);
  83. let msg = unsafe { CStr::from_ptr(message) }.to_str().unwrap();
  84. let msg_allocated = String::from(msg);
  85. (severity, domain, func_allocated, msg_allocated)
  86. }
  87. pub unsafe fn translate_domain(_domain: LogDomain) -> u32 {
  88. 1
  89. }
  90. pub unsafe fn translate_severity(_severity: LogSeverity) -> c_int {
  91. 1
  92. }
  93. }
  94. /// This implementation is used when compiling for actual use, as opposed to
  95. /// testing.
  96. #[cfg(all(not(test), not(feature = "testing")))]
  97. pub mod log {
  98. use libc::{c_char, c_int};
  99. use super::LogDomain;
  100. use super::LogSeverity;
  101. /// Severity log types. These mirror definitions in /src/common/torlog.h
  102. /// C_RUST_COUPLED: src/common/log.c, log domain types
  103. extern "C" {
  104. #[no_mangle]
  105. static _LOG_WARN: c_int;
  106. static _LOG_NOTICE: c_int;
  107. }
  108. /// Domain log types. These mirror definitions in /src/common/torlog.h
  109. /// C_RUST_COUPLED: src/common/log.c, log severity types
  110. extern "C" {
  111. #[no_mangle]
  112. static _LD_NET: u32;
  113. static _LD_GENERAL: u32;
  114. }
  115. /// Translate Rust defintions of log domain levels to C. This exposes a 1:1
  116. /// mapping between types.
  117. ///
  118. /// Allow for default cases in case Rust and C log types get out of sync
  119. #[allow(unreachable_patterns)]
  120. pub unsafe fn translate_domain(domain: LogDomain) -> u32 {
  121. match domain {
  122. LogDomain::LdNet => _LD_NET,
  123. LogDomain::LdGeneral => _LD_GENERAL,
  124. _ => _LD_GENERAL,
  125. }
  126. }
  127. /// Translate Rust defintions of log severity levels to C. This exposes a
  128. /// 1:1 mapping between types.
  129. ///
  130. /// Allow for default cases in case Rust and C log types get out of sync
  131. #[allow(unreachable_patterns)]
  132. pub unsafe fn translate_severity(severity: LogSeverity) -> c_int {
  133. match severity {
  134. LogSeverity::Warn => _LOG_WARN,
  135. LogSeverity::Notice => _LOG_NOTICE,
  136. _ => _LOG_NOTICE,
  137. }
  138. }
  139. /// The main entry point into Tor's logger. When in non-test mode, this
  140. /// will link directly with `tor_log_string` in /src/or/log.c
  141. extern "C" {
  142. pub fn tor_log_string(
  143. severity: c_int,
  144. domain: u32,
  145. function: *const c_char,
  146. string: *const c_char,
  147. );
  148. }
  149. }
  150. #[cfg(test)]
  151. mod test {
  152. use tor_log::*;
  153. use tor_log::log::*;
  154. use libc::c_int;
  155. #[test]
  156. fn test_get_log_message() {
  157. fn test_macro<'a>() -> (c_int, u32, String, String) {
  158. let (x, y, z, a) =
  159. tor_log_msg!(
  160. LogSeverity::Warn,
  161. LogDomain::LdNet,
  162. "test_macro",
  163. "test log message {}",
  164. "a",
  165. );
  166. (x, y, z, a)
  167. }
  168. let (severity, domain, function_name, log_msg) = test_macro();
  169. let expected_severity =
  170. unsafe { translate_severity(LogSeverity::Warn) };
  171. assert_eq!(severity, expected_severity);
  172. let expected_domain = unsafe { translate_domain(LogDomain::LdNet) };
  173. assert_eq!(domain, expected_domain);
  174. assert_eq!("test_macro", function_name);
  175. assert_eq!("test log message a", log_msg);
  176. }
  177. #[test]
  178. fn test_get_log_message_multiple_values() {
  179. fn test_macro<'a>() -> (c_int, u32, String, String) {
  180. let (x, y, z, a) = tor_log_msg!(
  181. LogSeverity::Warn,
  182. LogDomain::LdNet,
  183. "test_macro 2",
  184. "test log message {} {} {} {}",
  185. 10,
  186. 9,
  187. 8,
  188. 7
  189. );
  190. (x, y, z, a)
  191. }
  192. let (severity, domain, function_name, log_msg) = test_macro();
  193. let expected_severity =
  194. unsafe { translate_severity(LogSeverity::Warn) };
  195. assert_eq!(severity, expected_severity);
  196. let expected_domain = unsafe { translate_domain(LogDomain::LdNet) };
  197. assert_eq!(domain, expected_domain);
  198. assert_eq!("test_macro 2", function_name);
  199. assert_eq!("test log message 10 9 8 7", log_msg);
  200. }
  201. }