Having a good logger might be really beneficial when developing and monitoring apps. Simple loggers might just use printf
function, but what if we want to upgrade it a bit, and for instance see the log inside the Output windows in Visual Studio? In this post I'd like to present some useful code that should improve your loggers.
Last Update: 23rd July 2016
Here's a declaration of the logger:
// decide which one you want to use //#define UTILS_LOG_WITH_PRINTF #define UTILS_LOG_WITH_OUTPUT_DBG_STRING #ifdef UTILS_LOG_WITH_PRINTF #define LOG(msg, ...) { \ printf(msg, __VA_ARGS__); \ printf("\n"); \ } #define LOG_ERROR(msg, ...) { \ printf("ERR in %s at line %d: ", __FUNCTION__, __LINE__); \ printf(msg, __VA_ARGS__); \ printf("\n"); \ } #elif defined (UTILS_LOG_WITH_OUTPUT_DBG_STRING) && defined(WIN32) #define LOG(msg, ...) { \ winOutDebugString(0, 0, 0, 0, msg, __VA_ARGS__); \ } #define LOG_ERROR(msg, ...) { \ winOutDebugString(__FUNCTION__, __FILE__, __LINE__, \ "ERR", msg, __VA_ARGS__); \ } #else #define LOG(msg, ...) { } #define LOG_ERROR(msg, ...) { } #endif
The above code, heavily macro based, let's you decide what output function it will internally use. You can use normal printf
or OutputDebugString
. The latter function let's write to Debug stream, so you could see the logs in Visual Studio Output Window, or even DebugView application.
The logger also uses the following predefined macros
__VA_ARGS__
- variable args for macro__FUNCTION__
- function name where the log macro is invoked__FILE__
- file name where the log macro is invoked__LINE__
- line number of the file
Unfortunately OutputDebugString
does not have printf
functionality, so we need to create a helper function that will compose a string from a variable number of parameters.
Below there is some code that illustrate how to create such helper function:
#ifdef WIN32 void winOutDebugString(const char *func, const char * file, int line, const char *msg, const char *userMsg, ...) { static char strBuf[512]; static char strTemp[512]; strBuf[0] = '\0'; // handle "msg" with varying number of params: va_list ap; if (userMsg == NULL) return; strBuf[0] = '\0'; va_start(ap, userMsg); vsprintf_s(strBuf, userMsg, ap); va_end(ap); strcat_s(strBuf, "\n"); // display the results: OutputDebugString(strBuf); } #endif
There's even more to that, you can read in my debugging tips post how to format the log message so that you can easily jump betwenn the log message and the source code.
Read about useful predefined macros for logging in my previous post: __FUNCTION__ macro in Visual C++