Click here to Skip to main content
15,881,854 members
Articles / General Programming / Exceptions

Catch and Log Exceptions

Rate me:
Please Sign up or sign in to vote.
4.50/5 (3 votes)
16 Aug 2010CPOL4 min read 32.7K   329   16   2
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)


Written By
Turkey Turkey
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionVayy Arkadas!!! Pin
yales19-Aug-11 10:47
yales19-Aug-11 10:47 
GeneralThis may make sense for projects not built with Visual C++ ... Pin
SOlli3-Sep-10 16:06
SOlli3-Sep-10 16:06 
... but what is the advantage with VC (which you reference throughout the article ... and by making assumptions such as the expansion of /##/ or __FUNCSIG__ in the code)?

More often than not the PDB file created by VC along with a written mini-dump (via MiniDumpWriteDump) will be way more helpful than a textual record of the same information. One can add custom data into the dump and with extensions to WinDbg you could even automate the assessment of the dumps as they come in. And yes, even WinDbg has support for source level debugging and references to the source in dump analysis.

VC uses SEH to implement C++ exceptions, thus anything that holds for good old exceptions in the SEH sense holds - mostly - also for C++ exceptions.

So please, what is the advantage of using your method?

Anyhow, I have some criticism about the implementation itself:

1.) your code is clearly Windows/VC-centric and yet Boost appears as dependency.
2.) the implementation of EXLOG_END and EXLOG_END_WITH_RETURN is actually rather limiting since you assume all exceptions in that context to be derived from std::exception.
3.) compiling this into a release build creates a lot of bloat and for closed-source projects the risk of being way more easy to reverse.
4.) using something like "while(0){}" instead of "/##/" makes no assumptions about the preprocessor (try it in GCC Wink | ;) ) ... and will be optimized out by any sane compiler.

Edit: I've been using /##/ in own code for some time, until I had to compile some code across compilers and platforms Wink | ;)

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.