Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

MsgService.cxx

Go to the documentation of this file.
00001 
00002 // $Id: MsgService.cxx,v 1.31 2008/08/19 19:02:59 gmieg Exp $
00003 //
00004 // A class to create and provide access to MsgStreams
00005 //
00006 // messier@huhepl.harvard.edu
00008 #include "MessageService/MsgService.h"
00009 
00010 #include <cstdio> // sprintf
00011 #include <cassert>
00012 #include <iostream>
00013 extern "C" {
00014 #include <unistd.h> // getpid
00015 }
00016 #include "MessageService/Msg.h"
00017 #include "MessageService/MsgFormat.h"
00018 #include "MessageService/MsgOStream.h"
00019 #include "MessageService/MsgOStreamService.h"
00020 #include "MessageService/MsgStream.h"
00021 #include "MessageService/MsgCatStream.h"
00022 #include "MessageService/MsgTripWire.h"
00023 
00024 using namespace std;
00025 
00026 MsgService* MsgService::fInstance = 0;
00027 int MsgService::Init::count = 0;
00028 Msg::LogLevel_t MsgService::fsGlobalLogLevel = Msg::kInfo;
00029 
00030 //......................................................................
00031 
00032 std::ostream& operator<<(std::ostream& os, const MsgService& m)
00033 {
00034 //======================================================================
00035 // Purpose: Print out infomation about the message service
00036 //
00037 // Inputs os - the output stream to print to
00038 //         m - the message service to report on
00039 //
00040 // Returns: The stream printed to
00041 //======================================================================
00042   int i;
00043   static const MsgFormat hexfmt("8x");
00044 
00045   os << "***** MsgService *******************"
00046      << "************************************\n";
00047   os << " Defaults:\n";
00048   os << "  Loglevel: " << (int)m.fDefaultLogLevel << "\n";
00049   for (i = 0; i<Msg::kNLogLevel; ++i) {
00050     os << "  OStream : " << m.fDefaultOStream[i]
00051        << " Format : " << hexfmt((int)m.fDefaultFormat[i]) << "\n";
00052   }
00053 
00054   os << " Active Streams:\n";
00055   i = 0;
00056   map<std::string,MsgStream*>::const_iterator mend(m.fMsgStreamTable.end());
00057   for (map<std::string,MsgStream*>::const_iterator 
00058          itr = m.fMsgStreamTable.begin();
00059        itr != mend;
00060        ++itr) {
00061     os << "<" << itr->first << ">";
00062     if (++i%10 == 0) os << "\n";
00063   }
00064   os << "\n";
00065 
00066   
00067   os << " Concatenated Streams:\n";
00068   vector<MsgCatStream>::const_iterator vend(m.fMsgCatList.end());
00069   for (vector<MsgCatStream>::const_iterator 
00070          itrCatStream(m.fMsgCatList.begin());
00071        itrCatStream != vend;
00072        ++itrCatStream) {
00073     os << "  " << (*itrCatStream);
00074   }
00075   os << "************************************"
00076      << "************************************\n";
00077   return os;
00078 }
00079 
00080 //......................................................................
00081 
00082 MsgService::~MsgService()
00083 {
00084 //======================================================================
00085 // Purpose: Clean up after the message service - for now this includes:
00086 //          - Closing all output streams
00087 //          - Taking care of concatenated stream output
00088 //======================================================================
00089   
00090   // Close all output message streams
00091   map<std::string,MsgStream*>::iterator mend(fMsgStreamTable.end());
00092   for (map<std::string,MsgStream*>::iterator itr = fMsgStreamTable.begin();
00093        itr != mend;
00094        ++itr) {
00095     itr->second->Close();
00096     delete itr->second;
00097   }
00098 
00099   // Take care of any streams that need to be concatenated
00100   vector<MsgCatStream>::iterator vend(fMsgCatList.end());
00101   for (vector<MsgCatStream>::iterator iter = fMsgCatList.begin();
00102        iter != vend;
00103        ++iter) {
00104     iter->DoConcatenation();
00105   }
00106 }
00107 
00108 //......................................................................
00109 
00110 void MsgService::SetDefaultFormat(int fmt, int level) 
00111 {
00112 //=======================================================================
00113 // Add flags specified by fmt to the defaults for the log level "level"
00114 // Only affects streams created after this call, ie. action is not
00115 // retroactive. Use "level" which is out of range to affect all
00116 // printing levels
00117 //=======================================================================
00118   if ((level>=0) && (level<Msg::kNLogLevel)) {
00119     if((fmt & Msg::kFgColorMask)) fDefaultFormat[level] &=(~Msg::kFgColorMask); // Reset FG color if being set
00120     if((fmt & Msg::kBgColorMask)) fDefaultFormat[level] &=(~Msg::kBgColorMask); // Reset BG color if being set
00121     fDefaultFormat[level] |= fmt;
00122   }
00123   else {
00124     for (int lvl=0; lvl<Msg::kNLogLevel; ++lvl) {
00125       if((fmt & Msg::kFgColorMask)) fDefaultFormat[lvl] &=(~Msg::kFgColorMask); // Reset FG color if being set
00126       if((fmt & Msg::kBgColorMask)) fDefaultFormat[lvl] &=(~Msg::kBgColorMask); // Reset BG color if being set
00127       fDefaultFormat[lvl] |= fmt;
00128     }
00129   }
00130 
00131 }
00132 
00133 //......................................................................
00134 
00135 MsgService *MsgService::Instance()
00136 {
00137 //======================================================================
00138 // Purpose: Return a pointer to the sole instance of the MsgService
00139 //
00140 // Returns: A pointer to the message service
00141 //======================================================================
00142   if (fInstance == 0) {
00143     fInstance = new MsgService();
00144     static int nbirth = 0;
00145     nbirth++;
00146     if (nbirth>1) 
00147       cout 
00148         << "MsgService::Instance() creating the singleton a 2nd time"
00149         << endl
00150         << "first one must have been destructed prematurely"
00151         << endl;
00152   }
00153 
00154 // Set MessageException if fkFatalAbort has been set previously
00155   try {if (fInstance->GetkFatalAbort()!=0) throw MSGException();}
00156   catch(MSGException) {
00157     cerr << endl << endl;
00158     cerr << "***************** MSGException *****************" << endl;
00159     cerr << "***** MSGException was thrown for kFatal. ******" << endl;
00160     cerr << endl;
00161     cerr << "A MsgStream MSGException has been thrown.  This"  << endl;
00162     cerr << "occurs when a kFatal MSG statement is activated." << endl;
00163     cerr << "***************** MSGException *****************" << endl;
00164     cerr << endl;
00165 
00166     throw MSGException();
00167     return fInstance;
00168   }
00169 
00170 // Trip-wire handling
00171   static bool trip_wire_activated = false;
00172   if ( ! trip_wire_activated && MsgTripWire::Instance().IsActive() ) {
00173     trip_wire_activated = true;   
00174 
00175 // Set the log level on all current and future MsgStreams
00176     Msg::LogLevel_t lvl =  MsgTripWire::Instance().GetLogLevel();
00177     fInstance->fDefaultLogLevel = lvl;
00178     map<std::string,MsgStream*>::iterator mend(fInstance->fMsgStreamTable.end());
00179     for (map<std::string,MsgStream*>::iterator itr = fInstance->fMsgStreamTable.begin();
00180          itr != mend;
00181          ++itr) itr->second->SetLogLevel(lvl);
00182   }
00183     
00184   return fInstance;
00185 }
00186 
00187 //......................................................................
00188 
00200 #if defined(MACOSX) || defined(IRIX6) || defined(PROBLEM_WITH_COMPILING_THIS)
00201 # include <TSystem.h>
00202 void MsgService::StackTrace(const char* stream, Msg::LogLevel_t lvl, int, int)
00203 {
00204   if(!MsgService::Instance()->IsActive(stream,lvl)) return;
00205   gSystem->StackTrace();
00206 }
00207 #else
00208 
00209 # include <stdio.h>
00210 # include <stdlib.h>
00211 # include <execinfo.h>
00212 # include <dlfcn.h>
00213 # include <string.h>
00214 void MsgService::StackTrace(const char* stream, Msg::LogLevel_t lvl, int depth, int ignore)
00215 {
00216   if(!MsgService::Instance()->IsActive(stream,lvl)) return;
00217   MsgStream& s = *((MsgService::Instance())->GetStream(stream));
00218 
00219     void *trace[10]; //only care about last 7 functions (3 taken with tracing support)
00220   size_t size;
00221 
00222   size_t i;
00223   char cmd[512];
00224 
00225   size = backtrace (trace, 10);
00226 
00227   // Need -rdynamic gcc (linker) flag for this to work
00228   // but the result is not pretty.
00229   //char **strings = NULL;
00230   //strings = backtrace_symbols (trace, size); 
00231   //for (i = 2; i < size; i++){ //skip useless functions
00232   //  printf("going(%02d) [%s]\n",i-2,strings[i]);
00233   //free(strings);
00234 
00235   size_t start = 1 + ignore;    // skip usless functions
00236   size_t end   = depth + start; // Set depth.
00237   if(end>size) end=size;
00238 
00239   for (i = start; i < end; i++){ //skip useless functions
00240     // printf("going(%02d) [%s]\n",i-2,strings[i]);
00241 
00242     unsigned long addr = (unsigned long) trace[i];
00243     Dl_info info;
00244     if(dladdr(trace[i], &info) && info.dli_fname && info.dli_fname[0]) {
00245       const char   *libname = info.dli_fname;
00246       const char   *symname = (info.dli_sname && info.dli_sname[0])
00247         ? info.dli_sname : "unknown";
00248       unsigned long libaddr = (unsigned long) info.dli_fbase;
00249       //unsigned long symaddr = (unsigned long) info.dli_saddr;
00250       unsigned long offset = (addr >= libaddr) ? addr - libaddr :
00251         libaddr - addr;
00252 
00253       char linestr[1024];
00254       sprintf(linestr,"addr:0x%lx",addr); // Default: dump the address raw.
00255 
00256       sprintf(cmd,"addr2line -e %s 0x%016lx 2> /dev/null", libname, offset);
00257       if (FILE *pf = ::popen(cmd, "r")) {
00258         if (fgets(linestr, 1024, pf)) {
00259           linestr[strlen(linestr)-1] = 0;  // remove trailing \n
00260           }
00261         ::pclose(pf);
00262       }
00263 
00264       char funcname[1024];
00265       strcpy(funcname,symname);
00266       sprintf(cmd,"c++filt %s 2>/dev/null",  symname);
00267       if (FILE *pf = ::popen(cmd, "r")) {
00268         if (fgets(funcname, 1024, pf)) {
00269           funcname[strlen(funcname)-1] = 0;  // remove trailing \n
00270         }
00271         ::pclose(pf);
00272       }
00273 
00274       // Strip off most of the path.
00275       int slashes = 0;
00276       for(char* c = linestr+strlen(linestr)-1; c>linestr; c--) {
00277         if( (*c) == '/' ) slashes++;
00278         if(slashes==2) {
00279           char tmp[1024];
00280           strcpy(tmp,c+1);
00281           strcpy(linestr,tmp);
00282           break;
00283         }
00284       }
00285       
00286       // Write out the line.
00287       s << "  [" << i-start << "] " << funcname << " @ " << linestr << endl;
00288       //printf("%s @ %s\n",funcname,linestr);
00289     }
00290     
00291   }
00292 }
00293 #endif
00294 
00295 //......................................................................
00296 
00297 void MsgService::AddCatStream(const char* fileName, 
00298                               const char* streamName, 
00299                               Msg::LogLevel_t lvl)
00300 {
00301 //======================================================================
00302 // Purpose: Add a stream to the list that will get concatenated to 
00303 //          the file fileName
00304 //
00305 // Inputs: fileName   - The file name where the concat'ed output goes
00306 //         streamName - The stream whose output should be concat'ed
00307 //         lvl        - The threshold to apply to the stream
00308 //======================================================================
00309   
00310   // Attach a temporary file to the message stream
00311   const char *tmpFileName = GetTmpCatFileName(streamName, lvl);
00312   this->GetStream(streamName)->AttachOStream(lvl, tmpFileName);
00313   
00314   // Add this temporary file to the list of files that should get
00315   // concatenated together
00316 
00317   // Check if this concatenated file already exists
00318   MsgCatStream *catStream = 0;
00319   for (vector<MsgCatStream>::iterator iter = fMsgCatList.begin();
00320        iter != fMsgCatList.end();
00321        ++iter) {
00322     if (strcmp((*iter).GetOutputFileName(), fileName)==0) {
00323       catStream = &(*iter);
00324       break;
00325     }
00326   }
00327   if (catStream == 0) {
00328     // Doesn't exist -- create it and insert in list
00329     catStream = new MsgCatStream(fileName);
00330     catStream->AddFileToList(tmpFileName);    
00331     fMsgCatList.push_back(*catStream);
00332     delete catStream;
00333   }
00334   else {
00335     // Already exists -- just add to it
00336     catStream->AddFileToList(tmpFileName);
00337   }
00338 }
00339 
00340 //......................................................................
00341 
00342 MsgStream* MsgService::CreateStream(const char *name)
00343 {
00344 //======================================================================
00345 // Purpose: Create and configure a new message stream. If the stream
00346 //          already exists, just return the existing stream
00347 //
00348 // Inputs: name - The name of the stream requested
00349 //
00350 // Returns: The message stream with this name
00351 //======================================================================
00352   std::string msgName(name);
00353   MsgStream *msgStream;
00354 
00355   // Test if the stream already exists
00356   if ((msgStream = fMsgStreamTable[msgName]) != 0) {
00357     return msgStream;
00358   }
00359   
00360   // If not then create it using the defaults
00361   msgStream = new MsgStream(name); // Ownership is given away --
00362   assert(msgStream);
00363 
00364   // Configure
00365   msgStream->SetLogLevel(fDefaultLogLevel);
00366   for (Msg::LogLevel_t lvl=0; lvl<Msg::kNLogLevel; ++lvl) {
00367     msgStream->AttachOStream(lvl, fDefaultOStream[lvl]);
00368     msgStream->SetFormat(lvl, fDefaultFormat[lvl]);
00369   }
00370   fMsgStreamTable[msgName] = msgStream;
00371 
00372   MsgService::SetGlobalLevel(); // Reset the global level now that there's a new stream to watch.
00373 
00374   return msgStream;
00375 }
00376 
00377 //......................................................................
00378 
00379 MsgService::MsgService() :
00380   fkFatalAbort(0)
00381 {
00382 //======================================================================
00383 // Purpose: Create and initialize the message service
00384 //======================================================================
00385   // Set the default log level for all streams
00386   fDefaultLogLevel = Msg::kInfo;
00387 
00388   // Set the default output streams
00389   strcpy(fDefaultOStream[Msg::kVerbose], "cerr");
00390   strcpy(fDefaultOStream[Msg::kDebug],   "cerr");
00391   strcpy(fDefaultOStream[Msg::kSynopsis],"cout");
00392   strcpy(fDefaultOStream[Msg::kInfo],    "cout");
00393   strcpy(fDefaultOStream[4],             "cout");  // not yet used.
00394   strcpy(fDefaultOStream[Msg::kWarning], "cerr");
00395   strcpy(fDefaultOStream[Msg::kError],   "cerr");
00396   strcpy(fDefaultOStream[Msg::kFatal],   "cerr");
00397 
00398   // Set the default formats
00399   fDefaultFormat[Msg::kVerbose] = 
00400     Msg::kPriority + Msg::kName +
00401     Msg::kFile     + Msg::kLine;
00402   fDefaultFormat[Msg::kDebug] = 
00403     Msg::kPriority + Msg::kName +
00404     Msg::kFile     + Msg::kLine;
00405   fDefaultFormat[Msg::kSynopsis] = 0;
00406   fDefaultFormat[Msg::kInfo] = 0;
00407   fDefaultFormat[Msg::kWarning] =
00408     Msg::kPriority + Msg::kName  + 
00409     Msg::kFile     + Msg::kCVSId + Msg::kLine;
00410   fDefaultFormat[Msg::kError]   = 
00411     Msg::kPriority + Msg::kName  + Msg::kTime + 
00412     Msg::kFile     + Msg::kCVSId + Msg::kLine;
00413   fDefaultFormat[Msg::kFatal]   = 
00414     Msg::kPriority + Msg::kName  + Msg::kTime +
00415     Msg::kFile     + Msg::kCVSId + Msg::kLine;
00416 
00417   fsGlobalLogLevel = fDefaultLogLevel;
00418 
00419   fCurrentRun   = -1;
00420   fCurrentSnarl = -1;
00421 }
00422 
00423 //......................................................................
00424 
00425 const char *MsgService::GetTmpCatFileName(const char *streamName, 
00426                                           int lvl)
00427 {
00428 //======================================================================
00429 // Purpose: Create a unique temporary filename for a stream of a given
00430 //          log level
00431 //
00432 // Inputs: streamName - the name of the stream requesting the tmp file
00433 //         lvl - the threshold for this file to accept messages
00434 //
00435 // Returns: A temporary file name
00436 //======================================================================
00437   static char fileName[256];
00438   sprintf(fileName, ".%s.%d.%d", streamName, lvl, getpid());
00439   return fileName;
00440 }
00441 
00442 //......................................................................
00443 
00444 void MsgService::PrintStatistics()
00445 {
00446 //======================================================================
00447 // Purpose: Print usage statistics for the message service
00448 //======================================================================
00449   this->PrintStatistics(cerr);
00450 }
00451 
00452 //......................................................................
00453 
00454 void MsgService::PrintStatistics(std::ostream &os) 
00455 {
00456 //======================================================================
00457 // Purpose: Print usage statistics for the message service
00458 //
00459 // Inputs: os - the output stream to print to
00460 //======================================================================
00461 
00462   // Copy the map to a container we can sort
00463   list<pair<std::string, MsgStream*> > streamList;
00464   map<std::string, MsgStream*>::iterator mend(fMsgStreamTable.end());
00465   for (map<std::string, MsgStream*>::iterator itr = fMsgStreamTable.begin();
00466        itr != mend;
00467        ++itr) {
00468     // Push the key-value pair from the map into the list
00469     streamList.push_back(*itr);
00470   }
00471   streamList.sort();
00472   
00473   // Print the headers - note this has to stay in sync with MsgStream's
00474   // format for printing statistics
00475   os << "======================= "
00476      << "Message Service Usage Statistics"
00477      << " =======================\n";
00478   os << " Stream "
00479      << "File                     "
00480      << "Print Counts\n";
00481   os << " ------ "
00482      << "------------------------ "
00483      << "-----------------------------------------------\n";
00484 
00485   list<pair<std::string, MsgStream*> >::iterator lend(streamList.end());
00486   for (list<pair<std::string, MsgStream*> >::iterator itr = streamList.begin();
00487        itr != lend;
00488        ++itr) {
00489     itr->second->PrintStatistics(os);
00490   }
00491   
00492   os << "========================"
00493      << "================================"
00494      << "========================\n";
00495 }
00496 
00497 
00498 void MsgService::SetGlobalLevel() 
00499 {
00504   fsGlobalLogLevel = Msg::kFatal;
00505 
00506   MsgService* self = MsgService::Instance(); 
00507   std::map<std::string,MsgStream*>::iterator it = self->fMsgStreamTable.begin();
00508   for(it = self->fMsgStreamTable.begin(); it != self->fMsgStreamTable.end(); it++ ) {
00509     MsgStream* s = it->second;
00510     if(s) {
00511       if(s->GetLogLevel() < fsGlobalLogLevel) fsGlobalLogLevel = s->GetLogLevel();
00512     }
00513   }
00514 }

Generated on Mon Nov 23 05:27:25 2009 for loon by  doxygen 1.3.9.1