Search

3.5 — More debugging tactics

In the previous lesson (3.4 -- Basic debugging tactics), we started exploring how to manually debug problems. In that lesson, we offered some criticisms of using statements to print debug text:

  1. Debug statements clutter your code.
  2. Debug statements clutter the output of your program.
  3. Debug statements require modification of your code to both add and to remove, which can introduce new bugs.
  4. Debug statements must be removed after you’re done with them, which makes them non-reusable.

We can mitigate some of these issues. In this lesson, we’ll explore some basic techniques for doing so.

Conditionalizing your debugging code

Consider the following program that contains some debug statements:

When you’re done with the debugging statement, you’ll either need to remove them, or comment them out. Then if you want them again later, you’ll have to add them back, or uncomment them.

One way to make it easier to disable and enable debugging throughout your program is to make your debugging statements conditional using preprocessor directives:

Now we can enable debugging simply by commenting / uncommenting #define ENABLE_DEBUG. This allows us to reuse previously added debug statements and then just disable them when we’re done with them, rather than having to actually remove them from the code. If this were a multi-file program, the #define ENABLE_DEBUG would go in a header file that’s included into all code files so we can comment / uncomment the #define in a single location and have it propagate to all code files.

This addresses the issue with having to remove debug statements and the risk in doing so, but at the cost of even more code clutter. Another downside of this approach is that if you make a typo (e.g. misspell “DEBUG”) or forget to include the header into a code file, some or all of the debugging for that file may not be enabled. So although this is better than the unconditionalized version, there’s still room to improve.

Using a logger

An alternative approach to conditionalized debugging via the preprocessor is to send your debugging information to a log file. A log file is a file (normally stored on disk) that records events that occur in software. The process of writing information to a log file is called logging. Most applications and operating systems write log files that can be used to help diagnose issues that occur.

Log files have a few advantages. Because the information written to a log file is separated from your program’s output, you can avoid the clutter caused by mingling your normal output and debug output. Log files can also be easily sent to other people for diagnosis -- so if someone using your software has an issue, you can ask them to send you the log file, and it might help give you a clue where the issue is.

While you can write your own code to create log file and send output to them, you’re better off using one of the many existing third-party logging tools available. Which one you use is up to you.

For illustrative purposes, we’ll show what outputting to a logger looks like using the plog logger. Plog is implemented as a set of header files, so it’s easy to include anywhere you need it, and it’s lightweight and easy to use.

Here’s output from the above logger (in the Logfile.txt file):

2018-12-26 20:03:33.295 DEBUG [4752] [[email protected]] main() called
2018-12-26 20:03:33.296 DEBUG [4752] [[email protected]] getUserInput() called

How you include, initialize, and use a logger will vary depending on the specific logger you select.

Note that conditional compilation directives are also not required using this method, as most loggers have a method to reduce/eliminate writing output to the log. This makes the code a lot easier to read, as the conditional compilation lines add a lot of clutter. With plog, logging can be temporarily disabled by changing the init statement to the following:

We won’t use plog in any future lessons, so you don’t need to worry about learning it.

As an aside...

If you want to compile the above example yourself, or use plog in your own projects, you can follow these instructions to install it:

First, get the latest plog release:

  • Visit the plog repo.
  • Click the green Code button in the top right corner, and choose “Download zip”

Next, unzip the entire archive to <anywhere> on your hard drive.

Finally, for each project, set the <anywhere>\plog-master\include\ directory as an include directory inside your IDE. There are instructions on how to do this for Visual Studio here: A.2 -- Using libraries with Visual Studio and Code::Blocks here: A.3 -- Using libraries with Code::Blocks.


3.6 -- Using an integrated debugger: Stepping
Index
3.4 -- Basic debugging tactics

81 comments to 3.5 — More debugging tactics

  • Rajin100000

    There is an error.

    the build log is saying:-

    ||=== Build: Debug in plogTest (compiler: GNU GCC Compiler) ===|
    D:\Program1\C++\plogTest\main.cpp|3|fatal error: plog/Initializers/RollingFileInitializer.h: No such file or directory|
    ||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

    I'm using Code::blocks and I added the include directory by using settings->compiler->search directories->linker. Is here something wrong in my code?

    And then I went to plog-<version>/include and then I went to plog folder and I saw that there is no folder named Initializer in that directory. Then I removed the 3rd line of my code.

    And then this error occured:-

    ||=== Build: Debug in plogTest (compiler: GNU GCC Compiler) ===|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Util.h||In function 'int plog::util::vasprintf(char**, const char*, va_list)':|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Util.h|141|error: conversion to 'size_t' {aka 'unsigned int'} from 'int' may change the sign of the result [-Werror=sign-conversion]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Util.h|150|error: conversion to 'size_t' {aka 'unsigned int'} from 'int' may change the sign of the result [-Werror=sign-conversion]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Util.h|150|error: conversion to 'size_t' {aka 'unsigned int'} from 'int' may change the sign of the result [-Werror=sign-conversion]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Util.h||In function 'int plog::util::vaswprintf(wchar_t**, const wchar_t*, va_list)':|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Util.h|170|error: conversion to 'unsigned int' from 'int' may change the sign of the result [-Werror=sign-conversion]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Util.h|179|error: conversion to 'size_t' {aka 'unsigned int'} from 'int' may change the sign of the result [-Werror=sign-conversion]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Util.h|179|error: conversion to 'size_t' {aka 'unsigned int'} from 'int' may change the sign of the result [-Werror=sign-conversion]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Util.h||In function 'std::__cxx11::wstring plog::util::toWide(const char*)':|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Util.h|225|error: conversion to 'std::__cxx11::basic_string<wchar_t>::size_type' {aka 'unsigned int'} from 'int' may change the sign of the result [-Werror=sign-conversion]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Util.h||In function 'std::__cxx11::string plog::util::toNarrow(const wstring&, long int)':|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Util.h|237|error: conversion to 'plog::UINT' {aka 'unsigned int'} from 'long int' may change the sign of the result [-Werror=sign-conversion]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Util.h|238|error: conversion to 'std::__cxx11::basic_string<char>::size_type' {aka 'unsigned int'} from 'int' may change the sign of the result [-Werror=sign-conversion]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Util.h||In constructor 'plog::util::Mutex::Mutex()':|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Util.h|399|error: 'plog::util::Mutex::m_sync' should be initialized in the member initialization list [-Werror=effc++]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Record.h|111|error: 'class plog::Record' has pointer data members [-Werror=effc++]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Record.h|111|error:   but does not override 'plog::Record(const plog::Record&)' [-Werror=effc++]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Record.h|111|error:   or 'operator=(const plog::Record&)' [-Werror=effc++]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Record.h||In constructor 'plog::Record::Record(plog::Severity, const char*, size_t, const char*, const void*, int)':|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Record.h|114|error: 'plog::Record::m_time' should be initialized in the member initialization list [-Werror=effc++]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Record.h|114|error: 'plog::Record::m_message' should be initialized in the member initialization list [-Werror=effc++]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Record.h|114|error: 'plog::Record::m_funcStr' should be initialized in the member initialization list [-Werror=effc++]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Record.h|114|error: 'plog::Record::m_messageStr' should be initialized in the member initialization list [-Werror=effc++]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Logger.h||In instantiation of 'class plog::Logger<0>':|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Logger.h|75|required from here|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Logger.h|17|error: base class 'class plog::util::Singleton<plog::Logger<0> >' has accessible non-virtual destructor [-Werror=non-virtual-dtor]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Logger.h||In instantiation of 'plog::Logger<instanceId>::Logger(plog::Severity) [with int instanceId = 0]':|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Init.h|29|required from 'plog::Logger<instanceId>& plog::init(plog::Severity, plog::IAppender*) [with int instanceId = 0]'|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Init.h|35|required from here|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Logger.h|20|error: 'plog::Logger<0>::m_appenders' should be initialized in the member initialization list [-Werror=effc++]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Appenders\RollingFileAppender.h||In instantiation of 'plog::RollingFileAppender<Formatter, Converter>::RollingFileAppender(const nchar*, size_t, int) [with Formatter = plog::CsvFormatter; Converter = plog::NativeEOLConverter<plog::UTF8Converter>; plog::util::nchar = wchar_t; size_t = unsigned int]':|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Init.h|44|required from 'plog::Logger<instanceId>& plog::init(plog::Severity, const nchar*, size_t, int) [with Formatter = plog::CsvFormatter; int instanceId = 0; plog::util::nchar = wchar_t; size_t = unsigned int]'|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Init.h|60|required from 'plog::Logger<instanceId>& plog::init(plog::Severity, const nchar*, size_t, int) [with int instanceId = 0; plog::util::nchar = wchar_t; size_t = unsigned int]'|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Init.h|65|required from here|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Appenders\RollingFileAppender.h|14|error: 'plog::RollingFileAppender<plog::CsvFormatter, plog::NativeEOLConverter<plog::UTF8Converter> >::m_mutex' should be initialized in the member initialization list [-Werror=effc++]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Appenders\RollingFileAppender.h|14|error: 'plog::RollingFileAppender<plog::CsvFormatter, plog::NativeEOLConverter<plog::UTF8Converter> >::m_file' should be initialized in the member initialization list [-Werror=effc++]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Appenders\RollingFileAppender.h|14|error: 'plog::RollingFileAppender<plog::CsvFormatter, plog::NativeEOLConverter<plog::UTF8Converter> >::m_fileExt' should be initialized in the member initialization list [-Werror=effc++]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Appenders\RollingFileAppender.h|14|error: 'plog::RollingFileAppender<plog::CsvFormatter, plog::NativeEOLConverter<plog::UTF8Converter> >::m_fileNameNoExt' should be initialized in the member initialization list [-Werror=effc++]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Appenders\RollingFileAppender.h||In instantiation of 'plog::RollingFileAppender<Formatter, Converter>::RollingFileAppender(const nchar*, size_t, int) [with Formatter = plog::TxtFormatter; Converter = plog::NativeEOLConverter<plog::UTF8Converter>; plog::util::nchar = wchar_t; size_t = unsigned int]':|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Init.h|44|required from 'plog::Logger<instanceId>& plog::init(plog::Severity, const nchar*, size_t, int) [with Formatter = plog::TxtFormatter; int instanceId = 0; plog::util::nchar = wchar_t; size_t = unsigned int]'|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Init.h|60|required from 'plog::Logger<instanceId>& plog::init(plog::Severity, const nchar*, size_t, int) [with int instanceId = 0; plog::util::nchar = wchar_t; size_t = unsigned int]'|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Init.h|65|required from here|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Appenders\RollingFileAppender.h|14|error: 'plog::RollingFileAppender<plog::TxtFormatter, plog::NativeEOLConverter<plog::UTF8Converter> >::m_mutex' should be initialized in the member initialization list [-Werror=effc++]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Appenders\RollingFileAppender.h|14|error: 'plog::RollingFileAppender<plog::TxtFormatter, plog::NativeEOLConverter<plog::UTF8Converter> >::m_file' should be initialized in the member initialization list [-Werror=effc++]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Appenders\RollingFileAppender.h|14|error: 'plog::RollingFileAppender<plog::TxtFormatter, plog::NativeEOLConverter<plog::UTF8Converter> >::m_fileExt' should be initialized in the member initialization list [-Werror=effc++]|
    D:\Program1\C++\plog-1.1.5\plog-1.1.5\include\plog\Appenders\RollingFileAppender.h|14|error: 'plog::RollingFileAppender<plog::TxtFormatter, plog::NativeEOLConverter<plog::UTF8Converter> >::m_fileNameNoExt' should be initialized in the member initialization list [-Werror=effc++]|
    ||=== Build failed: 27 error(s), 13 warning(s) (0 minute(s), 1 second(s)) ===|

    So, what am I doing wrong here? Or, is there any wrong with the new version of plog?

    • Alex

      The 1.1.15 release of Plog doesn't have plog/Initializers/RollingFileInitializer.h. Only the master branch does. I updated the instructions to download the master branch version instead, so try again with that version.

      • Rajin100000

        Thanks Alex! It worked! However, it doesn't seem to work with cygwin gcc. That's why I'm using Code::blocks gcc and it is working nicely. But it seems to add the lines in logfile.txt every time I build and run it. And now the logfile.txt is being larger...

  • Adam

    Hi this is Adam. I am pretty new to C++ and I am still trying to stick to this guide. Since I am using CLion, and it seems to have a different approach of including a directory? Something called "CMakeList.txt" that I have to modify to include plog library, and I have tried googling for a day but in vain. I don't understand the answers they're talking about also too afraid to ask on stackOverflow. Can you make a tutorial of how to link or add a library into a project using CMake, please? Thanks in advance

  • Hi Alex and nascardriver,

    The

    library has now been updated to require an additional header file to be included

    Otherwise, the `plog::init` function becomes undefined and the compiler error out (as of June 2021).

  • Acelator

    I have tried to import plog into my program, and I have spent quite a lot of time trying, and it is still not working. I'm using Cmake (linux too) and I have the following Cmake

    I have added Plog as a git submodule inside /lib/plog/. Could someone help me?

  • Mal

    In project properties, when I click on VC++ Directories I can't find the option in the Right Hand Pane to add directories.  If I go to Library Directories, the only option is to edit.

  • Mal

    I got nowhere trying to add plog to VS 2019 and gave up. after a search on YouTube, came across this video:  https://www.youtube.com/watch?v=wL2FHiBSh2E
    It is a simple guide to use the Local Windows Debugger to set break points and to monitor your variables.  As I'm just setting out on my C++ journey, this will do me fine and I can learn to use more of its functions and I get better at programming C++ - Rather than using plog at this stage, maybe this is a option you could recommend to your students who use VS 2019.

  • Sven

    I didn't understand the linking process of the plog library.

    From the past lessons experience and from what's states in the A.3 chapter about using external libraries, I would guess that I have to add the "include" directory with the header files in the compiler include directory settings, and the actual .lib file in the linker include directory.

    However, I cannot seem to find the .lib file in the plog library.

    I just added the plog "include" directory to both compiler and linker search directories and the code seems to work, but I didn't understand why. Ok, the compiler finds the header files just fine, but where is the linker actually getting the source from?

    • Alex

      plog isn't distributed as a pre-compiled lib file, its a header-only library, which means all of the "code" is contained directly in the header files. When you #include the header file, the preprocessor will copy this code into your code files, and the compiler will compile it.

  • Allan Anderson

    When I try using the plog library outside of an IDE (i.e., command line compile) I get an error no directory or file <plog/Log.h>. I have the plog directory in the same directory as the code file, and in /usr/local/include. My code is shown below:
    #include <iostream>
    #include <plog/Log.h> //Step 1: include the logger header

    int getUserInput()
    {
        LOGD << "getUserInout() called"; // LOGD is defined by the plog library

        std::cout << "Enter a number: ";
        int x{};
        std::cin >> x;
        return x;
    }

    int main()
    {
        plog::init(plog::debug, "Logfile.txt"); //Step 2: initialize the logger

        LOGD << "main() called"; //Step 3: Output to the log as if you were writing to the console

        int x{ getUserInput()};
        std::cout << "You entered: " << x;

        return 0;
    }

  • kind_of_confused

    Hello,

    When I try to implement plog (1.1.5), I get the following error:
    "error: 'timeb' is deprecated [-Werror=deprecated-declarations]" at line 90 of the Util.h file. I am using Code::Blocks 20.03 with all compiler warning flags set as recommended earlier in the tutorial and other compiler options set as "-Wsign-conversion -Werror -std=gnu++2a". I am using a Win 10 (64x) system. Any advice?

    • kind_of_confused

      Hello,

      Update: I removed the compiler option "-Wsign-conversion -Werror -std=gnu++2a" and it's now working. Does that mean that plog isn't compatible with C++20/C++2a?

      • Alex

        It should be compatible, it's just using some function that may be removed in the future. The author of plog will need to update the code to make it fully compliant with modern standards.

  • Chris Kakonkwe

    In what directory may I find the log file written by plog? I can't find it the directory from which I ran the executable.

  • Marcel

    In chapter 2.11 you wrote
    "Use angled brackets to include header files that come with the compiler. Use double quotes to include any other header files."
    Shouldn't you include plog with double quotes then, rather than the angled brackets you're using in the example? Does it even matter if both versions work?

    • Alex

      I updated lesson 2.11 to make it more clear what angled brackes vs double quotes do for #includes.

      You'd use double quotes if you installed plog relative to your source code directory. But since this lesson suggest installing it elsewhere and updating your include paths, using angled brackets is more correct.

  • J34NP3T3R

    do LOG statements remain within your code permanently ?
    or is it only added when debugging and removed before compiling the finished product.

  • Ali

    Just for anyone, who is having trouble to run plog recently, the main reason is that the recent version (as of May 11) doesn't include the plog::init() (for IDK reasons) I saw that as one of the github issues, and you can either wait, as the author said he will fix it, or basically, download an older version of it. (I tried the previous release and it worked)

  • yeokaiwei

    Can we use #include <Msplog.h> instead?

    It was the recommended version popup in Visual Studio.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]