Bump copyright date to 2019
[tor.git] / src / rust / tor_log / tor_log.rs
blob98fccba5a97870233f4ea5eae00f6d6cc4a665ed
1 // Copyright (c) 2016-2019, The Tor Project, Inc. */
2 // See LICENSE for licensing information */
4 // Note that these functions are untested due to the fact that there are no
5 // return variables to test and they are calling into a C API.
7 /// The related domain which the logging message is relevant. For example,
8 /// log messages relevant to networking would use LogDomain::LdNet, whereas
9 /// general messages can use LdGeneral.
10 #[derive(Eq, PartialEq)]
11 pub enum LogDomain {
12     Net,
13     General,
16 /// The severity level at which to log messages.
17 #[derive(Eq, PartialEq)]
18 pub enum LogSeverity {
19     Notice,
20     Warn,
23 /// Main entry point for Rust modules to log messages.
24 ///
25 /// # Inputs
26 ///
27 /// * A `severity` of type LogSeverity, which defines the level of severity the
28 /// message will be logged.
29 /// * A `domain` of type LogDomain, which defines the domain the log message
30 /// will be associated with.
31 /// * A `function` of type &str, which defines the name of the function where
32 /// the message is being logged. There is a current RFC for a macro that
33 /// defines function names. When it is, we should use it. See
34 /// https://github.com/rust-lang/rfcs/pull/1719
35 /// * A `message` of type &str, which is the log message itself.
36 #[macro_export]
37 macro_rules! tor_log_msg {
38     ($severity: path,
39      $domain: path,
40      $function: expr,
41      $($message:tt)*) =>
42     {
43         {
44             let msg = format!($($message)*);
45             $crate::tor_log_msg_impl($severity, $domain, $function, msg)
46         }
47     };
50 #[inline]
51 pub fn tor_log_msg_impl(severity: LogSeverity, domain: LogDomain, function: &str, message: String) {
52     use std::ffi::CString;
54     /// Default function name to log in case of errors when converting
55     /// a function name to a CString
56     const ERR_LOG_FUNCTION: &str = "tor_log_msg";
58     /// Default message to log in case of errors when converting a log
59     /// message to a CString
60     const ERR_LOG_MSG: &str = "Unable to log message from Rust \
61                                module due to error when converting to CString";
63     let func = match CString::new(function) {
64         Ok(n) => n,
65         Err(_) => CString::new(ERR_LOG_FUNCTION).unwrap(),
66     };
68     let msg = match CString::new(message) {
69         Ok(n) => n,
70         Err(_) => CString::new(ERR_LOG_MSG).unwrap(),
71     };
73     // Bind to a local variable to preserve ownership. This is essential so
74     // that ownership is guaranteed until these local variables go out of scope
75     let func_ptr = func.as_ptr();
76     let msg_ptr = msg.as_ptr();
78     let c_severity = unsafe { log::translate_severity(severity) };
79     let c_domain = unsafe { log::translate_domain(domain) };
81     unsafe { log::tor_log_string(c_severity, c_domain, func_ptr, msg_ptr) }
84 /// This implementation is used when compiling for actual use, as opposed to
85 /// testing.
86 #[cfg(not(test))]
87 pub mod log {
88     use super::LogDomain;
89     use super::LogSeverity;
90     use libc::{c_char, c_int};
92     /// Severity log types. These mirror definitions in src/lib/log/log.h
93     /// C_RUST_COUPLED: src/lib/log/log.c, log domain types
94     extern "C" {
95         static LOG_WARN_: c_int;
96         static LOG_NOTICE_: c_int;
97     }
99     /// Domain log types. These mirror definitions in src/lib/log/log.h
100     /// C_RUST_COUPLED: src/lib/log/log.c, log severity types
101     extern "C" {
102         static LD_NET_: u32;
103         static LD_GENERAL_: u32;
104     }
106     /// Translate Rust defintions of log domain levels to C. This exposes a 1:1
107     /// mapping between types.
108     #[inline]
109     pub unsafe fn translate_domain(domain: LogDomain) -> u32 {
110         match domain {
111             LogDomain::Net => LD_NET_,
112             LogDomain::General => LD_GENERAL_,
113         }
114     }
116     /// Translate Rust defintions of log severity levels to C. This exposes a
117     /// 1:1 mapping between types.
118     #[inline]
119     pub unsafe fn translate_severity(severity: LogSeverity) -> c_int {
120         match severity {
121             LogSeverity::Warn => LOG_WARN_,
122             LogSeverity::Notice => LOG_NOTICE_,
123         }
124     }
126     /// The main entry point into Tor's logger. When in non-test mode, this
127     /// will link directly with `tor_log_string` in torlog.c
128     extern "C" {
129         pub fn tor_log_string(
130             severity: c_int,
131             domain: u32,
132             function: *const c_char,
133             string: *const c_char,
134         );
135     }
138 /// This module exposes no-op functionality for testing other Rust modules
139 /// without linking to C.
140 #[cfg(test)]
141 pub mod log {
142     use super::LogDomain;
143     use super::LogSeverity;
144     use libc::{c_char, c_int};
146     pub static mut LAST_LOGGED_FUNCTION: *mut String = 0 as *mut String;
147     pub static mut LAST_LOGGED_MESSAGE: *mut String = 0 as *mut String;
149     pub unsafe fn tor_log_string(
150         _severity: c_int,
151         _domain: u32,
152         function: *const c_char,
153         message: *const c_char,
154     ) {
155         use std::ffi::CStr;
157         let f = CStr::from_ptr(function);
158         let fct = match f.to_str() {
159             Ok(n) => n,
160             Err(_) => "",
161         };
162         LAST_LOGGED_FUNCTION = Box::into_raw(Box::new(String::from(fct)));
164         let m = CStr::from_ptr(message);
165         let msg = match m.to_str() {
166             Ok(n) => n,
167             Err(_) => "",
168         };
169         LAST_LOGGED_MESSAGE = Box::into_raw(Box::new(String::from(msg)));
170     }
172     pub unsafe fn translate_domain(_domain: LogDomain) -> u32 {
173         1
174     }
176     pub unsafe fn translate_severity(_severity: LogSeverity) -> c_int {
177         1
178     }
181 #[cfg(test)]
182 mod test {
183     use tor_log::log::{LAST_LOGGED_FUNCTION, LAST_LOGGED_MESSAGE};
184     use tor_log::*;
186     #[test]
187     fn test_get_log_message() {
188         {
189             fn test_macro() {
190                 tor_log_msg!(
191                     LogSeverity::Warn,
192                     LogDomain::Net,
193                     "test_macro",
194                     "test log message {}",
195                     "a",
196                 );
197             }
199             test_macro();
201             let function = unsafe { Box::from_raw(LAST_LOGGED_FUNCTION) };
202             assert_eq!("test_macro", *function);
204             let message = unsafe { Box::from_raw(LAST_LOGGED_MESSAGE) };
205             assert_eq!("test log message a", *message);
206         }
208         // test multiple inputs into the log message
209         {
210             fn test_macro() {
211                 tor_log_msg!(
212                     LogSeverity::Warn,
213                     LogDomain::Net,
214                     "next_test_macro",
215                     "test log message {} {} {} {} {}",
216                     1,
217                     2,
218                     3,
219                     4,
220                     5
221                 );
222             }
224             test_macro();
226             let function = unsafe { Box::from_raw(LAST_LOGGED_FUNCTION) };
227             assert_eq!("next_test_macro", *function);
229             let message = unsafe { Box::from_raw(LAST_LOGGED_MESSAGE) };
230             assert_eq!("test log message 1 2 3 4 5", *message);
231         }
233         // test how a long log message will be formatted
234         {
235             fn test_macro() {
236                 tor_log_msg!(
237                     LogSeverity::Warn,
238                     LogDomain::Net,
239                     "test_macro",
240                     "{}",
241                     "All the world's a stage, and all the men and women \
242                      merely players: they have their exits and their \
243                      entrances; and one man in his time plays many parts, his \
244                      acts being seven ages."
245                 );
246             }
248             test_macro();
250             let expected_string = "All the world's a \
251                                    stage, and all the men \
252                                    and women merely players: \
253                                    they have their exits and \
254                                    their entrances; and one man \
255                                    in his time plays many parts, \
256                                    his acts being seven ages.";
258             let function = unsafe { Box::from_raw(LAST_LOGGED_FUNCTION) };
259             assert_eq!("test_macro", *function);
261             let message = unsafe { Box::from_raw(LAST_LOGGED_MESSAGE) };
262             assert_eq!(expected_string, *message);
263         }
264     }