The preceding sections explained how to direct the logging output to several places. We noted that you can change your mind at runtime and direct the logging output somewhere else. Unfortunately, what you need to do when you change your mind isn't always consistent. Let's take a look at a simple class that attempts to hide some of those details:
class LogManager { public: LogManager (); ~LogManager (); void redirectToDaemon (const ACE_TCHAR *prog_name = ACE_TEXT ("")); void redirectToSyslog (const ACE_TCHAR *prog_name = ACE_TEXT ("")); void redirectToOStream (ACE_OSTREAM_TYPE *output); void redirectToFile (const char *filename); void redirectToStderr (void); ACE_Log_Msg_Callback * redirectToCallback (ACE_Log_Msg_Callback *callback); // ... };
The idea is pretty simple: An application will use the redirect* methods at any time to select the output destination:
void foo (void); int ACE_TMAIN (int, ACE_TCHAR *[]) { LOG_MANAGER->redirectToStderr (); ACE_TRACE (ACE_TEXT ("main")); LOG_MANAGER->redirectToSyslog (); ACE_DEBUG ((LM_INFO, ACE_TEXT ("%IHi Mom\n"))); foo (); LOG_MANAGER->redirectToDaemon (); ACE_DEBUG ((LM_INFO, ACE_TEXT ("%IGoodnight\n"))); return 0; } void foo (void) { ACE_TRACE (ACE_TEXT ("foo")); LOG_MANAGER->redirectToFile ("output.test"); ACE_DEBUG ((LM_INFO, ACE_TEXT ("%IHowdy Pardner\n"))); }
"But wait," you say. "Where did LOG_MANAGER come from?" This is an example of the ACE_Singleton template, mentioned in Section 1.6.3. That's what we're using behind LOG_MANAGER. ACE_Singleton simply ensures that we create one single instance of the LogManager class at runtime, even if multiple threads all try to create one at the same time. Using a singleton gives you quick access to a single instance of an object anywhere in your application. To declare our singleton, we add the following to our header file:
typedef ACE_Singleton<LogManager, ACE_Null_Mutex> LogManagerSingleton; #define LOG_MANAGER LogManagerSingleton::instance()
To deal with compilers that don't do automatic template instantiation, we must add the following to our .cpp file:
#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION) template class ACE_Singleton<LogManager, ACE_Null_Mutex>; #elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA) #pragma instantiate ACE_Singleton<LogManager, ACE_Null_Mutex> #elif defined (__GNUC__) && (defined (_AIX) || defined (__hpux)) template ACE_Singleton<LogManager, ACE_Null_Mutex> * ACE_Singleton<LogManager, ACE_Null_Mutex>::singleton_; #endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */
Our LogManager implementation is a straightforward application of the things discussed earlier in this chapter:
LogManager::LogManager () : log_stream_ (0), output_stream_ (0) { } LogManager::~LogManager () { if (log_stream_) log_stream_->close (); delete log_stream_; } void LogManager::redirectToSyslog (const ACE_TCHAR *prog_name) { ACE_LOG_MSG->open (prog_name, ACE_Log_Msg::SYSLOG, prog_name); } void LogManager::redirectToDaemon (const ACE_TCHAR *prog_name) { ACE_LOG_MSG->open (prog_name, ACE_Log_Msg::LOGGER, ACE_DEFAULT_LOGGER_KEY); } void LogManager::redirectToOStream (ACE_OSTREAM_TYPE *output) { output_stream_ = output; ACE_LOG_MSG->msg_ostream (this->output_stream_); ACE_LOG_MSG->clr_flags (ACE_Log_Msg::STDERR | ACE_Log_Msg::LOGGER); ACE_LOG_MSG->set_flags (ACE_Log_Msg::OSTREAM); } void LogManager::redirectToFile (const char *filename) { log_stream_ = new std::ofstream (); log_stream_->open (filename, ios::out | ios::app); this->redirectToOStream (log_stream_); } void LogManager::redirectToStderr (void) { ACE_LOG_MSG->clr_flags (ACE_Log_Msg::OSTREAM | ACE_Log_Msg::LOGGER); ACE_LOG_MSG->set_flags (ACE_Log_Msg::STDERR); } ACE_Log_Msg_Callback * LogManager::redirectToCallback (ACE_Log_Msg_Callback * callback) { ACE_Log_Msg_Callback *previous = ACE_LOG_MSG->msg_callback (callback); if (callback == 0) ACE_LOG_MSG->clr_flags (ACE_Log_Msg::MSG_CALLBACK); else ACE_LOG_MSG->set_flags (ACE_Log_Msg::MSG_CALLBACK); return previous; }
The primary limitation of the LogManager class is the assumption that output will go to only one place at a time. For our trivial examples, that may be sufficient but could be a problem for a real application. Modifying the LogManager class to overcome this should be a fairly easy task, and we leave that to the reader.