1 //
2 // Copyright 2011 Ettus Research LLC
3 // Copyright 2018 Ettus Research, a National Instruments Company
4 //
5 // SPDX-License-Identifier: GPL-3.0-or-later
6 //
7 
8 #pragma once
9 
10 #include <uhd/config.hpp>
11 #include <boost/current_function.hpp>
12 #include <boost/thread/thread.hpp>
13 #include <iomanip>
14 #include <iostream>
15 #include <ostream>
16 #include <sstream>
17 #include <string>
18 
19 /*! \file log.hpp
20  *
21  * \section loghpp_logging The UHD logging facility
22  *
23  * The logger enables UHD library code to easily log events into a file and
24  * display messages above a certain level in the terminal.
25  * Log entries are time-stamped and stored with file, line, and function.
26  * Each call to the UHD_LOG macros is thread-safe. Each thread will aquire the
27  * lock for the logger.
28  *
29  * Note: More information on the logging subsystem can be found on
30  * \ref page_logging.
31  *
32  * To disable console logging completely at compile time specify
33  * `-DUHD_LOG_CONSOLE_DISABLE` during configuration with CMake.
34  *
35  * By default no file logging will occur. Set a log file path:
36  *  - at compile time by specifying `-DUHD_LOG_FILE=$file_path`
37  *  - and/or override at runtime by setting the environment variable
38  *    `UHD_LOG_FILE`
39  *
40  * \subsection loghpp_levels Log levels
41  *
42  * See also \ref logging_levels.
43  *
44  * All log messages with verbosity greater than or equal to the log level
45  * (in other words, as often or less often than the current log level)
46  * are recorded to std::clog and/or the log file.
47  * Log levels can be specified using string or numeric values of
48  * uhd::log::severity_level.
49  *
50  * The default log level is "info", but can be overridden:
51  *  - at compile time by setting the pre-processor define `-DUHD_LOG_MIN_LEVEL`.
52  *  - at runtime by setting the environment variable `UHD_LOG_LEVEL`.
53  *  - for console logging by setting `(-D)UHD_LOG_CONSOLE_LEVEL` at
54  *    run-/compiletime
55  *  - for file logging by setting `(-D)UHD_LOG_FILE_LEVEL` at run-/compiletime
56  *
57  * UHD_LOG_LEVEL can be the name of a verbosity enum or integer value:
58  *   - Example pre-processor define: `-DUHD_LOG_MIN_LEVEL=3`
59  *   - Example pre-processor define: `-DUHD_LOG_MIN_LEVEL=info`
60  *   - Example environment variable: `export UHD_LOG_LEVEL=3`
61  *   - Example environment variable: `export UHD_LOG_LEVEL=info`
62  *
63  * \subsection loghpp_formatting Log formatting
64  *
65  * The log format for messages going into a log file is CSV.
66  * All log messages going into a logfile will contain following fields:
67  * - timestamp
68  * - thread-id
69  * - source-file + line information
70  * - severity level
71  * - component/channel information which logged the information
72  * - the actual log message
73  *
74  * The log format of log messages displayed on the terminal is plain text with
75  * space separated tags prepended.
76  * For example:
77  *    - `[INFO] [X300] This is a informational log message`
78  *
79  * The log format for log output on the console by using these preprocessor
80  * defines in CMake:
81  * - `-DUHD_LOG_CONSOLE_TIME` adds a timestamp [2017-01-01 00:00:00.000000]
82  * - `-DUHD_LOG_CONSOLE_THREAD` adds a thread-id `[0x001234]`
83  * - `-DUHD_LOG_CONSOLE_SRC` adds a sourcefile and line tag `[src_file:line]`
84  */
85 
86 /*
87  * Advanced logging macros
88  * UHD_LOG_MIN_LEVEL definitions
89  * trace: 0
90  * debug: 1
91  * info: 2
92  * warning: 3
93  * error: 4
94  * fatal: 5
95  */
96 
97 namespace uhd { namespace log {
98 /*! Logging severity levels
99  *
100  * Either numeric value or string can be used to define loglevel in
101  * CMake and environment variables
102  */
103 enum severity_level {
104     trace   = 0, /**< displays every available log message */
105     debug   = 1, /**< displays most log messages necessary for debugging internals */
106     info    = 2, /**< informational messages about setup and what is going on*/
107     warning = 3, /**< something is not right but operation can continue */
108     error   = 4, /**< something has gone wrong */
109     fatal   = 5, /**< something has gone horribly wrong */
110     off     = 6, /**< logging is turned off */
111 };
112 
113 /*! Logging info structure
114  *
115  * Information needed to create a log entry is fully contained in the
116  * logging_info structure.
117  */
118 struct UHD_API logging_info
119 {
logging_infouhd::log::logging_info120     logging_info() : verbosity(uhd::log::off) {}
logging_infouhd::log::logging_info121     logging_info(const boost::posix_time::ptime& time_,
122         const uhd::log::severity_level& verbosity_,
123         const std::string& file_,
124         const unsigned int& line_,
125         const std::string& component_,
126         const boost::thread::id& thread_id_)
127         : time(time_)
128         , verbosity(verbosity_)
129         , file(file_)
130         , line(line_)
131         , component(component_)
132         , thread_id(thread_id_)
133     { /* nop */
134     }
135 
136     boost::posix_time::ptime time;
137     uhd::log::severity_level verbosity;
138     std::string file;
139     unsigned int line;
140     std::string component;
141     boost::thread::id thread_id;
142     std::string message;
143 };
144 
145 /*! Set the global log level
146  *
147  * The global log level gets applied before the specific log level.
148  * So, if the global log level is 'info', no logger can can print
149  * messages at level 'debug' or below.
150  */
151 UHD_API void set_log_level(uhd::log::severity_level level);
152 
153 /*! Set the log level for the console logger (if defined).
154  *
155  * Short-hand for `set_logger_level("console", level);`
156  */
157 UHD_API void set_console_level(uhd::log::severity_level level);
158 
159 /*! Set the log level for the file logger (if defined)
160  *
161  * Short-hand for `set_logger_level("file", level);`
162  */
163 UHD_API void set_file_level(uhd::log::severity_level level);
164 
165 /*! Set the log level for any specific logger.
166  *
167  * \param logger Name of the logger
168  * \param level New log level for this logger.
169  *
170  * \throws uhd::key_error if \p logger was not defined
171  */
172 UHD_API void set_logger_level(const std::string& logger, uhd::log::severity_level level);
173 }} // namespace uhd::log
174 
175 //! \cond
176 //! Internal logging macro to be used in other macros
177 #define _UHD_LOG_INTERNAL(component, level) \
178     uhd::_log::log(level, __FILE__, __LINE__, component, boost::this_thread::get_id())
179 //! \endcond
180 
181 // macro-style logging (compile-time determined)
182 #if UHD_LOG_MIN_LEVEL < 1
183 #    define UHD_LOG_TRACE(component, message) \
184         _UHD_LOG_INTERNAL(component, uhd::log::trace) << message;
185 #else
186 #    define UHD_LOG_TRACE(component, message)
187 #endif
188 
189 #if UHD_LOG_MIN_LEVEL < 2
190 #    define UHD_LOG_DEBUG(component, message) \
191         _UHD_LOG_INTERNAL(component, uhd::log::debug) << message;
192 #else
193 #    define UHD_LOG_DEBUG(component, message)
194 #endif
195 
196 #if UHD_LOG_MIN_LEVEL < 3
197 #    define UHD_LOG_INFO(component, message) \
198         _UHD_LOG_INTERNAL(component, uhd::log::info) << message;
199 #else
200 #    define UHD_LOG_INFO(component, message)
201 #endif
202 
203 #if UHD_LOG_MIN_LEVEL < 4
204 #    define UHD_LOG_WARNING(component, message) \
205         _UHD_LOG_INTERNAL(component, uhd::log::warning) << message;
206 #else
207 #    define UHD_LOG_WARNING(component, message)
208 #endif
209 
210 #if UHD_LOG_MIN_LEVEL < 5
211 #    define UHD_LOG_ERROR(component, message) \
212         _UHD_LOG_INTERNAL(component, uhd::log::error) << message;
213 #else
214 #    define UHD_LOG_ERROR(component, message)
215 #endif
216 
217 #if UHD_LOG_MIN_LEVEL < 6
218 #    define UHD_LOG_FATAL(component, message) \
219         _UHD_LOG_INTERNAL(component, uhd::log::fatal) << message;
220 #else
221 #    define UHD_LOG_FATAL(component, message)
222 #endif
223 
224 #define RFNOC_LOG_TRACE(message) UHD_LOG_TRACE(this->get_unique_id(), message)
225 #define RFNOC_LOG_DEBUG(message) UHD_LOG_DEBUG(this->get_unique_id(), message)
226 #define RFNOC_LOG_INFO(message) UHD_LOG_INFO(this->get_unique_id(), message)
227 #define RFNOC_LOG_WARNING(message) UHD_LOG_WARNING(this->get_unique_id(), message)
228 #define RFNOC_LOG_ERROR(message) UHD_LOG_ERROR(this->get_unique_id(), message)
229 #define RFNOC_LOG_FATAL(message) UHD_LOG_FATAL(this->get_unique_id(), message)
230 
231 #ifndef UHD_LOG_FASTPATH_DISABLE
232 //! Extra-fast logging macro for when speed matters.
233 // No metadata is tracked. Only the message is displayed. This does not go
234 // through the regular backends. Mostly used for printing the UOSDL characters
235 // during streaming.
236 #    define UHD_LOG_FASTPATH(message) uhd::_log::log_fastpath(message);
237 #else
238 #    define UHD_LOG_FASTPATH(message)
239 #endif
240 
241 // iostream-style logging
242 #define UHD_LOGGER_TRACE(component) _UHD_LOG_INTERNAL(component, uhd::log::trace)
243 #define UHD_LOGGER_DEBUG(component) _UHD_LOG_INTERNAL(component, uhd::log::debug)
244 #define UHD_LOGGER_INFO(component) _UHD_LOG_INTERNAL(component, uhd::log::info)
245 #define UHD_LOGGER_WARNING(component) _UHD_LOG_INTERNAL(component, uhd::log::warning)
246 #define UHD_LOGGER_ERROR(component) _UHD_LOG_INTERNAL(component, uhd::log::error)
247 #define UHD_LOGGER_FATAL(component) _UHD_LOG_INTERNAL(component, uhd::log::fatal)
248 
249 
250 #if defined(__GNUG__)
251 //! Helpful debug tool to print site info
252 #    define UHD_HERE()            \
253         UHD_LOGGER_DEBUG("DEBUG") \
254             << __FILE__ << ":" << __LINE__ << " (" << __PRETTY_FUNCTION__ << ")";
255 #else
256 //! Helpful debug tool to print site info
257 #    define UHD_HERE() UHD_LOGGER_DEBUG("DEBUG") << __FILE__ << ":" << __LINE__;
258 #endif
259 
260 //! Helpful debug tool to print a variable
261 #define UHD_VAR(var) UHD_LOGGER_DEBUG("DEBUG") << #var << " = " << var;
262 
263 //! Helpful debug tool to print a variable in hex
264 #define UHD_HEX(var)                                                              \
265     UHD_LOGGER_DEBUG("DEBUG") << #var << " = 0x" << std::hex << std::setfill('0') \
266                               << std::setw(8) << var << std::dec;
267 
268 //! \cond
269 namespace uhd {
270 namespace _log {
271 
272 //! Fastpath logging
273 void UHD_API log_fastpath(const std::string&);
274 
275 //! Internal logging object (called by UHD_LOG* macros)
276 class UHD_API log
277 {
278 public:
279     log(const uhd::log::severity_level verbosity,
280         const std::string& file,
281         const unsigned int line,
282         const std::string& component,
283         const boost::thread::id thread_id);
284 
285     ~log(void);
286 
287 // Macro for overloading insertion operators to avoid costly
288 // conversion of types if not logging.
289 #define INSERTION_OVERLOAD(x) \
290     log& operator<<(x)        \
291     {                         \
292         if (_log_it) {        \
293             _ss << val;       \
294         }                     \
295         return *this;         \
296     }
297 
298     // General insertion overload
299     template <typename T>
300     INSERTION_OVERLOAD(T val)
301 
302     // Insertion overloads for std::ostream manipulators
303     INSERTION_OVERLOAD(std::ostream& (*val)(std::ostream&))
304         INSERTION_OVERLOAD(std::ios& (*val)(std::ios&))
305             INSERTION_OVERLOAD(std::ios_base& (*val)(std::ios_base&))
306 
307                 private : uhd::log::logging_info _log_info;
308     std::ostringstream _ss;
309     const bool _log_it;
310 };
311 
312 } // namespace _log
313 //! \endcond
314 } /* namespace uhd */
315