Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / exceptions

Catch and Log Exceptions

4.50/5 (3 votes)
16 Aug 2010CPOL4 min read 33.5K   330  
An easy way to catch exceptions and log them in C++.

Introduction

C++ developers usually try to find errors/exceptions by putting logging statements (cout, etc.) in their code to find what is going on. Some people choose to use software like Dr.Watson to get the callstack and find the exception location with some effort. As you know, the only way to catch exceptions is to use try/catch blocks. But putting these blocks makes the code less readable, and perhaps more error-prone because of the statements in the catch block.

I present here some easily usable macros which put these try/catch blocks in the code. The macros will let you catch exceptions and log the exception location in a file. If you use the macros in your code, whenever an exception occurs, you will be able to see a callstack.

It is also possible to disable exception throwing so that your program will live without exceptions.

Using the Code

Using the macros is very easy. Assume you have a function named myMethod:

C++
void myMethod()
{
    EXLOG_START
    ...
    ...
    EXLOG_END
}

EXLOG_START is a simple a try expression. On the other hand, EXLOG_END is the catch block with the error logging mechanism.

When an exception occurs in the myMethod function, it will be caught and logged in a file. After that, if exception throwing is not disabled (will be told), the exception will be thrown to the caller function of myMethod. If you have put the EXLOG statements in the functions, the exception will be logged up to the main function.

You can disable EXLOG's exception throwing so that the exception is caught, logged, and the program will try to continue running. You can set it in any part of your code using:

C++
EXLOG::setIsThrowExceptions(false); //default: true

Some functions may return a value instead of void. For these functions, you should use another macro EXLOG_END_WITH_RETURN to allow these functions to return a value when an exception occurs:

C++
int myMethodWithReturn() 
{ 
    EXLOG_START
    ...
    ...
    EXLOG_END_WITH_RETURN(-1) 
}

So if there is an exception, myMethodWithReturn will return -1 (which is assumed the default value for this function) and the caller gets -1, and the program will go on running.

Along with the macros, the ExLogger class is also important. It is the class that logs the info to the file. It uses boost's date time classes to get a unique name for the files/logs. So you should add boost's libraries to your project.

You can also use ExLogger to log anything to the file. It is a Singleton class that has overloaded << operators which let you write easy and readable code as if you are writing to cout. You can get an instance by using the macro EXLOG:

C++
EXLOG << myInt << "sth" << myValue << EXLOG_ENDL("");

EXLOG_ENDL is similar to std::endl which causes the log stream to be flushed into the file.

Points of Interest

In the code, the EXLOG macros are changeable with a define: EXLOG_ENABLED. I have put this define in the header, but you can put it in your preprocessor section so that you can enable/disable EXLOG related code with just one define.

If EXLOG_ENABLED is not enabled, the macros will be replaced with the // comment expression. So, the expressions will be commented out. There is only a small problem with this usage: if you want to disable the EXLOG statements with this define, you should put the EXLOG statements in a single line.

So, for example:

C++
EXLOG << myInt << "sth" << myValue << EXLOG_ENDL(""); 

is safe. But:

C++
EXLOG << myInt << 
               "sth" << myValue 
<< EXLOG_ENDL("");  

will cause compilation errors if you don't define EXLOG_ENABLED.

But I think this approach is more readable and maintainable than using:

C++
#ifdef _DEBUG
    Exlogger::instance() << myInt << "sth" << myValue << EXLOG_ENDL("");
#endif

Notes

  • With the default project settings, hardware related exceptions like division by zero, dangling pointer usage, etc., cannot be caught with the standard try/catch blocks. You should change your project settings to enable this by following the path:
  • Project Properties >> C/C++ >> Code Generation >> Enable C++ Exceptions 

    and setting the value to Yes With SEH Exceptions(/EHa).

  • Another setting is about STL classes. These classes contain many assertions. As you know, assertions cannot be caught. To avoid this, you should also define these values in the preprocessor settings of the project:
  • _SECURE_SCL=0 ve _HAS_ITERATOR_DEBUGGING=0

    But remember to set these preprocessor settings in all the imported projects! So this last setting is optional. If you do not define these preprocessor values, the STL exceptions may not be caught/logged, but will be informed to you by assertions.

Discussion

You may have thought that putting try/catch may affect performance. But you can be comfortable when using try/catch blocks. I have searched many forums and have also made performance tests. There is only an insignificant difference with try/catch blocks and without them.

Have a secure development. :)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)