I recently had to come up with a logging solution for a JNI DLL/shared library that was written in C++ and is providing the data translation layer between Java and the native libraries. As usual, some logging was required to aid fault finding in a production environment, if necessary. A quick survey of the state of C++ logging showed that not a lot had changed since I last looked at logging libraries for C++. In fact, a lot of them seem to have survived unchanged for several years. I'm not sure if that is a good thing and a sign of maturity or a sign of "making do". Eventually I settled on pantheios as it offered a bunch of features that were crucial for this application. The major one was that it is extremely modular and will only link in the parts you really need. I consider this a major advantage over the more monolithic libraries that pull in all their functionality all the time, especially when you link them in as a static library (yes, log4cxx, I'm looking at you). Linking in the logging library as a static library was a necessity to avoid conflicts with other libraries that are being used in the same process.
Initial tests in a simple command line program suggested that worked well and matched the requirements. Unfortunately I couldn't get it to log at all inside the JNI DLL, so I ended up trawling Google's search results for quite a while and experimented quite a lot of different settings until I ended up with a working combination.
First, pantheios initialises itself automatically if you use it inside a regular executable. For various reasons, it can't do that inside a Windows DLL, so you have to do that explicitly. Fortunately, Matthew Wilson, the author of pantheios, had explained on a mailing list how to do this. Typically, I can't find the post anymore so here's the code that I'm using to initialise the library, which is more or less a verbatim copy of Matthew's code less a couple of lines that weren't required:
#include <iostream>
#include <pantheios/pantheios.hpp>
#include <pantheios/inserters.hpp>
#include <pantheios/frontends/stock.h>
const char PANTHEIOS_FE_PROCESS_IDENTITY[] = "JNITestDll.1";
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD dwReason,
LPVOID lpReserved
)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
if (pantheios::pantheios_init() < 0)
{
std::cout << "Failed to initialise the Pantheios logging libraries!\n" << std::endl;
return FALSE;
}
else
{
pantheios::log_INFORMATIONAL("Logger enabled!");
}
}
else if (dwReason == DLL_PROCESS_DETACH)
{
pantheios::log_INFORMATIONAL("Logger disabled!");
pantheios::pantheios_uninit();
}
return TRUE; // ok
}
This seemed to initialise the library correctly as I wasn't getting any error messages to the contrary, but unfortunately I still wasn't getting any output either. Yes, I could see that pantheios_init() returned a value that inidicated sucessful initialisation, the logging functions were called and the output went
straight into the bit bucket somewhere.
It took me a little while to work out what happened but in the end I tracked it down to something that I filed under "JNI oddity". Pantheios
supports implicit linking for both its frontends (the part that you interact with) and its backends which are responsible for sending the output somewhere. Being the usual lazy so-and-so programmer, I had borrowed one of the implicit link files from the samples. Which should have worked OK as it was for a command line executable, but didn't. After some poking and prodding I realised that the issue was that by default, in this particular implicit link file pantheios would use the Windows Console logger when the code was built Windows. This didn't work (probably because this was in a DLL and there
wasn't a console associated with it. Switching to the fprintf backend fixed this issue and I was finally seeing logging output from the JNI
DLL. Here is the code for the implicit linking:
/* Pantheios Header Files */
#include <pantheios/implicit_link/core.h>
#include <pantheios/implicit_link/fe.simple.h>
#include <platformstl/platformstl.h>
#include <pantheios/implicit_link/be.fprintf.h>
#if ( defined(UNIX) || \
defined(unix))&& \
( defined(_WIN32) || \
defined(_WIN64))
# include <unixem/implicit_link.h>
#endif /* _WIN32 || _WIN64 */
All in all I'm happy with Pantheios as a logging solution. If you're
looking for a versatile C++ logger, I'd recommend you look at
it.
Oh, and in case you were wondering how I finally managed to get the code displayed with syntax highlighting, I prepared this post in Emacs as usual, loaded the code into Emacs and ran htmlize on the regions I wanted to include in the blog post. Works a treat after I switched the htmlizer to use inline-css. Highly recommended.