Fixposition SDK 0.0.0-heads/main-0-g5c7edb5
Collection of c++ libraries and apps for use with Fixposition products on Linux
Loading...
Searching...
No Matches
logging.hpp
Go to the documentation of this file.
1/**
2 * \verbatim
3 * ___ ___
4 * \ \ / /
5 * \ \/ / Copyright (c) Fixposition AG (www.fixposition.com) and contributors
6 * / /\ \ License: see the LICENSE file
7 * /__/ \__\
8 * \endverbatim
9 *
10 * @file
11 * @brief Fixposition SDK: Logging
12 *
13 * @page FPSDK_COMMON_LOGGING Logging
14 *
15 * **API**: fpsdk_common/logging.hpp and fpsdk::common::logging
16 *
17 * @section FPSDK_COMMON_LOGGIN_CONFIG Logging configuration
18 *
19 * The defaults are:
20 *
21 * - fpsdk::common::logging::LoggingLevel::INFO
22 * - fpsdk::common::logging::LoggingColour::AUTO
23 * - fpsdk::common::logging::LoggingTimestamps::NONE
24 *
25 * The defaults can be changed by setting the FPSDK_LOGGING environment variable to a comma-separated list of words that
26 * correspond to the above parameters. The parameters are case-insensitive and they are applied in the order of
27 * appearance. Unknown words are silently ignored. Examples:
28 *
29 * @code{sh}
30 * export FPSDK_LOGGING=DEBUG # Changes the level to DEBUG
31 * export FPSDK_LOGGING=Warning,JOURNAL # Changes the level to WARNING and the "colour" to JOURNAL
32 * export FPSDK_LOGGING=Warning,relative,debug # Changes the level to DEBUG and the timestamps to RELATIVE
33 * export FPSDK_LOGGING=Warning,retalive,debgu # Changes the level to WARNING, and nothing else
34 * @endcode
35 *
36 * Apps can configure the logging using the fpsdk::common::logging::LoggingSetParams(). See also the implementation of
37 * fpsdk::common::app::ProgramOptions().
38 */
39#ifndef __FPSDK_COMMON_LOGGING_HPP__
40#define __FPSDK_COMMON_LOGGING_HPP__
41
42/* LIBC/STL */
43#include <cinttypes> // PRI.. macros, often used with formats
44#include <cstdarg>
45#include <cstdint>
46#include <sstream>
47
48/* EXTERNAL */
49
50/* PACKAGE */
51#include "string.hpp"
52#include "time.hpp"
53
54namespace fpsdk {
55namespace common {
56/**
57 * @brief Logging
58 */
59namespace logging {
60/* ****************************************************************************************************************** */
61
62/**
63 * @name Printf() style logging
64 *
65 * For example: `INFO("Hello world, the number is %d", 42);`
66 *
67 * @{
68 */
69// clang-format off
70/**
71 * @brief Print a fatal message @hideinitializer
72 */
73#define FATAL(...) _FPSDK_LOGGING_LOG(FATAL, "Fatal: " __VA_ARGS__)
74/**
75 * @brief Print a error message @hideinitializer
76 */
77#define ERROR(...) _FPSDK_LOGGING_LOG(ERROR, "Error: " __VA_ARGS__)
78/**
79 * @brief Print a warning message @hideinitializer
80 */
81#define WARNING(...) _FPSDK_LOGGING_LOG(WARNING, "Warning: " __VA_ARGS__)
82/**
83 * @brief Print a notice message @hideinitializer
84 */
85#define NOTICE(...) _FPSDK_LOGGING_LOG(NOTICE, __VA_ARGS__)
86/**
87 * @brief Print a info message @hideinitializer
88 */
89#define INFO(...) _FPSDK_LOGGING_LOG(INFO, __VA_ARGS__)
90/**
91 * @brief Print a debug message @hideinitializer
92 */
93#define DEBUG(...) _FPSDK_LOGGING_LOG(DEBUG, __VA_ARGS__)
94/**
95 * @brief Print a debug hexdump @hideinitializer
96 */
97#define DEBUG_HEXDUMP(data, size, prefix, ...) _FPSDK_LOGGING_HEX(DEBUG, data, size, prefix, __VA_ARGS__)
98#if !defined(NDEBUG) || defined(_DOXYGEN_) // Only for non-Release builds
99
100/**
101 * @brief Print a trace message (only debug builds, compiled out in release builds) @hideinitializer
102 */
103# define TRACE(...) _FPSDK_LOGGING_LOG(TRACE, __VA_ARGS__)
104/**
105 * @brief Print a trace hexdump (only debug builds, compiled out in release builds) @hideinitializer
106 */
107# define TRACE_HEXDUMP(data, size, prefix, ...) _FPSDK_LOGGING_HEX(TRACE, data, size, prefix, __VA_ARGS__)
108#else
109# define TRACE(...) /* nothing */
110# define TRACE_HEXDUMP(...) /* nothing */
111#endif
112
113/**
114 * @brief Print a error message (throttled) @hideinitializer
115 */
116#define ERROR_THR(millis, ...) _FPSDK_LOGGING_THR(ERROR, millis, __VA_ARGS__)
117/**
118 * @brief Print a warning message (throttled) @hideinitializer
119 */
120#define WARNING_THR(millis, ...) _FPSDK_LOGGING_THR(WARNING, millis, __VA_ARGS__)
121/**
122 * @brief Print a notice message (throttled) @hideinitializer
123 */
124#define NOTICE_THR(millis, ...) _FPSDK_LOGGING_THR(NOTICE, millis, __VA_ARGS__)
125/**
126 * @brief Print a info message (throttled) @hideinitializer
127 */
128#define INFO_THR(millis, ...) _FPSDK_LOGGING_THR(INFO, millis, __VA_ARGS__)
129/**
130 * @brief Print a debug message (throttled) @hideinitializer
131 */
132#define DEBUG_THR(millis, ...) _FPSDK_LOGGING_THR(DEBUG, millis, __VA_ARGS__)
133// clang-format on
134///@}
135
136/**
137 * @name C++ style logging
138 *
139 * For example, `INFO_S("Hello world, the number is " << 42);`
140 *
141 * @{
142 */
143// clang-format off
144/**
145 * @brief Print a fatal message @hideinitializer
146 */
147#define FATAL_S(expr) _FPSDK_LOGGING_STR(FATAL, expr)
148/**
149 * @brief Print a error message @hideinitializer
150 */
151#define ERROR_S(expr) _FPSDK_LOGGING_STR(ERROR, expr)
152/**
153 * @brief Print a warning message @hideinitializer
154 */
155#define WARNING_S(expr) _FPSDK_LOGGING_STR(WARNING, expr)
156/**
157 * @brief Print a debug message @hideinitializer
158 */
159#define DEBUG_S(expr) _FPSDK_LOGGING_STR(DEBUG, expr)
160/**
161 * @brief Print a notice message @hideinitializer
162 */
163#define NOTICE_S(expr) _FPSDK_LOGGING_STR(NOTICE, expr)
164/**
165 * @brief Print a info message @hideinitializer
166 */
167#define INFO_S(expr) _FPSDK_LOGGING_STR(INFO, expr)
168
169#if !defined(NDEBUG) || defined(_DOXYGEN_) // Only for non-Release builds
170/**
171 * @brief Print a trace message (only debug builds, compiled out in release builds) @hideinitializer
172 */
173# define TRACE_S(expr) _FPSDK_LOGGING_STR(TRACE, expr)
174#else
175# define TRACE_S(...) /* nothing */
176#endif
177// clang-format on
178///@}
179
180#if !defined(NDEBUG) || defined(_DOXYGEN_) // Only for non-Release builds
181//! Conditional compilation for non-Release builds
182# define IF_TRACE(...) __VA_ARGS__
183#else
184# define IF_TRACE(...) /* nothing */
185#endif
186// Note: for other levels use LoggingIsLevel()
187
188// ---------------------------------------------------------------------------------------------------------------------
189
190/**
191 * @brief Logging verbosity levels, default is INFO
192 *
193 * The logging levels loosely follow syslog levels (indicated in [] below, see also
194 * https://en.wikipedia.org/wiki/Syslog)
195 *
196 * Libraries (fpsdk_common, fpsdk_ros1, ...) code shall only use WARNING and DEBUG.
197 */
198enum class LoggingLevel : int
199{ // clang-format off
200 FATAL = 2, //!< [2/crit] Hard errors, critical conditions (for apps). Cannot be silenced.
201 ERROR = 3, //!< [3/err] Errors (for apps)
202 WARNING = 4, //!< [4/warning] Warnings (for libs and apps)
203 NOTICE = 5, //!< [5/notice] Significant stuff, for example headings (for apps)
204 INFO = 6, //!< [6/info] Interesting stuff, the default level (for apps)
205 DEBUG = 7, //!< [7/debug] Debugging (for libs and apps)
206 TRACE = 8, //!< [7/debug] Extra debugging, only compiled-in in non-Release builds
207}; // clang-format on
208
209LoggingLevel& operator++(LoggingLevel& level); //!< Increase verbosity (pre-increment)
210LoggingLevel& operator--(LoggingLevel& level); //!< Decrease verbosity (pre-decrement)
211LoggingLevel operator++(LoggingLevel& level, int); //!< Increase verbosity (post-increment)
212LoggingLevel operator--(LoggingLevel& level, int); //!< Decrease verbosity (post-decrement)
213
214/**
215 * @brief Stringify log level
216 *
217 * @param[in] level The logging level
218 *
219 * @returns a unique string identifying the level
220 */
221const char* LoggingLevelStr(const LoggingLevel level);
222
223/**
224 * @brief Check if given level would print
225 *
226 * @param[in] level The logging level in question
227 *
228 * @returns true if the given level would print, false otherwise
229 */
230bool LoggingIsLevel(const LoggingLevel level);
231
232/**
233 * @brief Logging "colours"
234 */
236{
237 AUTO = 0, //!< Automatic (default), use colours if stderr is an interactive terminal
238 YES, //!< Use colours (terminal escape sequences)
239 NO, //!< Do not use colours
240 JOURNAL, //!< Use systemd journal level indicators (instead of terminal colours), useful for systemd services
241};
242
243/**
244 * @brief Stringify log level
245 *
246 * @param[in] colour The logging colour
247 *
248 * @returns a unique string identifying the level
249 */
250const char* LoggingColourStr(const LoggingColour colour);
251
252/**
253 * @brief Logging timestamps
254 */
256{
257 NONE = 0, //!< No timestamps
258 RELATIVE, //!< Relative timestamps (since first logging message, sssss.sss format)
259 ABSOLUTE, //!< Absolute timestamps (local time, yyyy-mm-dd hh::mm:ss.sss format)
260};
261
262/**
263 * @brief Stringify log level
264 *
265 * @param[in] timestamps The logging timestamps
266 *
267 * @returns a unique string identifying the level
268 */
269const char* LoggingTimestampsStr(const LoggingTimestamps timestamps);
270
271struct LoggingParams; // forward declaration
272
273/**
274 * @brief Custom logging write (to screen, file, ...) function signature
275 *
276 * @param[in] params Logging parameters
277 * @param[in] level Logging level for the message
278 * @param[in] str The message
279 */
280using LoggingWriteFunc = void (*)(const LoggingParams& params, const LoggingLevel level, const char* str);
281
282/**
283 * @brief Logging parameters
284 */
286{
287 /**
288 * @brief Constructor
289 *
290 * @param[in] level Logging level
291 * @param[in] colour Logging colours
292 * @param[in] timestamps Logging timestamps
293 */
295 const LoggingTimestamps timestamps = LoggingTimestamps::NONE);
296 LoggingLevel level_; //!< Logging level
297 LoggingColour colour_; //!< Level colours
299 LoggingWriteFunc fn_; //!< Custom logging write function
300};
301
302/**
303 * @brief Configure logging
304 *
305 * @param[in] params Logging parameters
306 *
307 * Examples:
308 *
309 * @code{.cpp}
310 * LoggingSetParams({ LoggingLevel::DEBUG });
311 * LoggingSetParams({ LoggingLevel::DEBUG, LoggingColour::YES });
312 * LoggingSetParams({ LoggingLevel::DEBUG, LoggingColour::AUTO, LoggingTimestamps::RELATIVE });
313 * @endcode
314 *
315 * @returns a copy of the applied logging parameters
316 */
318
319/**
320 * @brief Get current logging params
321 *
322 * @returns a copy of the current logging params
323 */
325
326/**
327 * @brief Print a log message
328 *
329 * @note Use INFO(), DEBUG(), WARNING() etc. instead of this function.
330 *
331 * @param[in] level Logging level
332 * @param[in] repeat Number of times the message was repeated (for DEBUG_THR() etc.)
333 * @param[in] fmt printf() style format string and optional arguments to the format string
334 */
335void LoggingPrint(const LoggingLevel level, const std::size_t repeat, const char* fmt, ...) PRINTF_ATTR(3);
336
337/**
338 * @brief Print a hexdump
339 *
340 * @note Typically, use DEBUG_HEXDUMP() or TRACE_HEXDUMP() instead of this function.
341 *
342 * @param[in] level Logging level
343 * @param[in] data Pointer to start of data to dump
344 * @param[in] size Size of data to dump
345 * @param[in] prefix Prefix to add to each line, can be NULL to omit
346 * @param[in] fmt printf() style format string, can be NULL to omit
347 * @param[in] ... printf() style format arguments
348 */
349void LoggingHexdump(const LoggingLevel level, const uint8_t* data, const std::size_t size, const char* prefix,
350 const char* fmt, ...) PRINTF_ATTR(5);
351
352// Helper macros
353#ifndef _DOXYGEN_
354# define _FPSDK_LOGGING_LOG(_level_, ...) \
355 ::fpsdk::common::logging::LoggingPrint(::fpsdk::common::logging::LoggingLevel::_level_, 0, __VA_ARGS__)
356# define _FPSDK_LOGGING_HEX(_level_, _data_, _size_, _prefix_, ...) \
357 ::fpsdk::common::logging::LoggingHexdump( \
358 ::fpsdk::common::logging::LoggingLevel::_level_, _data_, _size_, _prefix_, __VA_ARGS__)
359# define _FPSDK_LOGGING_STR(_level_, _expr_) \
360 do { \
361 if (::fpsdk::common::logging::LoggingIsLevel(::fpsdk::common::logging::LoggingLevel::_level_)) { \
362 std::stringstream ss; \
363 ss << _expr_; \
364 ::fpsdk::common::logging::LoggingPrint( \
365 ::fpsdk::common::logging::LoggingLevel::_level_, 0, "%s", ss.str().c_str()); \
366 } \
367 } while (0)
368# define _FPSDK_LOGGING_THR(_level_, _millis_, ...) \
369 do { \
370 static uint32_t __last = 0; \
371 static uint32_t __repeat = 0; \
372 const uint32_t __now = ::fpsdk::common::time::GetMillis(); \
373 __repeat++; \
374 if ((__now - __last) >= (_millis_)) { \
375 __last = __now; \
376 ::fpsdk::common::logging::LoggingPrint( \
377 ::fpsdk::common::logging::LoggingLevel::_level_, __repeat, __VA_ARGS__); \
378 __repeat = 0; \
379 } \
380 } while (0)
381#endif // _DOXYGEN_
382
383/* ****************************************************************************************************************** */
384} // namespace logging
385} // namespace common
386} // namespace fpsdk
387#endif // __FPSDK_COMMON_LOGGING_HPP__
void LoggingPrint(const LoggingLevel level, const std::size_t repeat, const char *fmt,...) PRINTF_ATTR(3)
Print a log message.
void(*)(const LoggingParams &params, const LoggingLevel level, const char *str) LoggingWriteFunc
Custom logging write (to screen, file, ...) function signature.
Definition logging.hpp:280
LoggingParams LoggingGetParams()
Get current logging params.
LoggingParams LoggingSetParams(const LoggingParams &params)
Configure logging.
const char * LoggingLevelStr(const LoggingLevel level)
Stringify log level.
void LoggingHexdump(const LoggingLevel level, const uint8_t *data, const std::size_t size, const char *prefix, const char *fmt,...) PRINTF_ATTR(5)
Print a hexdump.
const char * LoggingTimestampsStr(const LoggingTimestamps timestamps)
Stringify log level.
bool LoggingIsLevel(const LoggingLevel level)
Check if given level would print.
LoggingLevel & operator++(LoggingLevel &level)
Increase verbosity (pre-increment)
LoggingTimestamps
Logging timestamps.
Definition logging.hpp:256
@ ABSOLUTE
Absolute timestamps (local time, yyyy-mm-dd hh::mm:ss.sss format)
@ RELATIVE
Relative timestamps (since first logging message, sssss.sss format)
LoggingLevel
Logging verbosity levels, default is INFO.
Definition logging.hpp:199
@ WARNING
[4/warning] Warnings (for libs and apps)
@ FATAL
[2/crit] Hard errors, critical conditions (for apps). Cannot be silenced.
@ TRACE
[7/debug] Extra debugging, only compiled-in in non-Release builds
@ INFO
[6/info] Interesting stuff, the default level (for apps)
@ NOTICE
[5/notice] Significant stuff, for example headings (for apps)
@ ERROR
[3/err] Errors (for apps)
@ DEBUG
[7/debug] Debugging (for libs and apps)
const char * LoggingColourStr(const LoggingColour colour)
Stringify log level.
LoggingLevel & operator--(LoggingLevel &level)
Decrease verbosity (pre-decrement)
LoggingColour
Logging "colours".
Definition logging.hpp:236
@ JOURNAL
Use systemd journal level indicators (instead of terminal colours), useful for systemd services.
@ YES
Use colours (terminal escape sequences)
@ AUTO
Automatic (default), use colours if stderr is an interactive terminal.
Fixposition SDK.
Fixposition SDK: String utilities.
#define PRINTF_ATTR(n)
Helper macro for marking functions as taking printf() style formatting strings.
Definition string.hpp:47
LoggingWriteFunc fn_
Custom logging write function.
Definition logging.hpp:299
LoggingColour colour_
Level colours.
Definition logging.hpp:297
LoggingLevel level_
Logging level.
Definition logging.hpp:296
LoggingParams(const LoggingLevel level=LoggingLevel::INFO, const LoggingColour colour=LoggingColour::AUTO, const LoggingTimestamps timestamps=LoggingTimestamps::NONE)
Constructor.
LoggingTimestamps timestamps_
Timestamps.
Definition logging.hpp:298
Fixposition SDK: Time utilities.