Guide | C++

What is Error Logging in C++

What is Error Logging in C++

In C++, an error logger class is typically used to track errors, warnings, and status messages at runtime. In a simple application, errors are printed directly to the console; however, in most cases, this class will log errors along with a timestamp, severity, and any other necessary details to a log file specified by the developer.

This article is designed to help intermediate C++ coders learn how to create an error logging class and customize it as they see fit.

 

An Example of an Error Logger Class

At runtime, the inner workings of a program are invisible to the user. This makes it difficult to understand why an application isn’t working as expected. An error logger is a module designed to assist in these situations. The logger sits within a program and tracks any errors or useful status messages created during runtime. Logs allow you to see the results of each independent process to determine exactly where faults are occurring.

Below is an example of a basic error logging class in C++. While a fully realized logger would be more robust, this gives a good starting point to understand how a logger is designed.

 enum LogPriority {
        TraceP, DebugP, InfoP, WarnP, ErrorP, FatalP
};

class BasicLogger {
private:
        static LogPriority verbosity;

public:
        static void Log(LogPriority priority, const char* message) {
            if (priority >= verbosity) {
                    switch (priority) {
                    case TraceP: std::cout << "Trace: \t"; break;
                    case DebugP: std::cout << "Debug: \t"; break;
                    case InfoP: std::cout << "Info: \t"; break;
                    case WarnP: std::cout << "Warn: \t"; break;
                    case ErrorP: std::cout << "Error: \t"; break;
                    case FatalP: std::cout << "Fatal: \t"; break;
                    }

                    std::cout << message << "\n";
            }
        }
};
LogPriority BasicLogger::verbosity = TraceP; 

An important feature to explore is the priority options, which define the seriousness of each message. The if statement will filter messages with a priority lower than what is defined, keeping them from appearing in the log. Adding a public function to change the verbosity can further simplify this process.

static void SetVerbosity(LogPriority new_priority) {
        verbosity = new_priority;
}

This allows the level of detail in the log to be changed dynamically. This is a great way to abstract away unnecessary information from end users without removing the developer’s ability to effectively debug.

 

How to Write C++ Error Logs to a File

In the above example, the log is printed to the console, but in most cases, the log needs to be stored permanently in a file. To accomplish this, the fstream library’s functionality to open, write to, and close a file can be used.

enum LogPriority {
        TraceP, DebugP, InfoP, WarnP, ErrorP, FatalP
};
 class BasicLogger {
private:
    static LogPriority verbosity;
     static const char* filepath;

public:
    static void SetVerbosity(LogPriority new_priority) {
        verbosity = new_priority;
}
    static void Log(LogPriority priority, const char* message) {
            if (priority >= verbosity) {
                    std::ofstream FILE(filepath, std::ios_base::app);
                    switch (priority) {
                            case TraceP: FILE << "Trace:\t"; break;
                            case DebugP: FILE << "Debug:\t"; break;
                            case InfoP: FILE << "Info:\t"; break;
                            case WarnP: FILE << "Warn:\t"; break;
                            case ErrorP: FILE << "Error:\t"; break;
                                case CriticalP: FILE << "Critical:\t"; break;
                            }
                    FILE << message << "\n";
                    FILE.close();
                    }
            }
};
LogPriority BasicLogger::verbosity = TraceP;
const char* BasicLogger::filepath = "log.txt";

Above, the ofstream command is used to open the file. Note the additional app argument which selects to append to the file rather than overwrite it. Next, the cout calls are replaced with the FILE variable, and finally, the file is closed. Keep in mind that you will have to include the fstream library to use these functions.

 

C++ Error Log Example Output

While logs will vary widely based on their design and purpose, most follow a similar pattern. Below is an example of an error log created when a program fails during execution. Notice that without the log, we would only know that the program crashed. However, thanks to this log, we can trace the problem back to an invalid argument passed to the baz() function.

06/04/2022 9:42:41:10  [Info]     Program Started 06/04/2022 9:42:41:10  [Info]     Program Started
06/04/2022 9:42:41:23  [Trace]  Initializing Variables
06/04/2022 9:42:42:00  [Error]   Error initializing int foo
06/04/2022 9:42:42:02  [Warn]   bar() attempting to operate on an uninitialized variable
06/04/2022 9:42:43:42  [Warn]   bar() returned -1
06/04/2022 9:42:44:10  [Fatal]    baz() failed due to invalid argument
Advanced Error Logging 

As your codebase grows, you may find your error logs become overloaded. Additionally, as you move into production, you may need to find ways to get logs from end users back to your team without the users seeing their contents. At this point, it can be beneficial to consider an error tracking platform.

 

Track, Analyze and Manage Errors With Rollbar

Managing errors and exceptions in your code is challenging. It can make deploying production code an unnerving experience. Being able to track, analyse, and manage errors in real-time can help you proceed with more confidence. Rollbar automates error monitoring and triaging, making fixing C++ errors easier than ever. Sign Up Today!

Related Resources