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

MsgStream.cxx

Go to the documentation of this file.
00001 
00002 // $Id: MsgStream.cxx,v 1.31 2008/08/19 19:02:59 gmieg Exp $
00003 //
00004 // MsgStream
00005 //
00006 // Allows messages to be filtered, formatted and channeled to one or
00007 // many output streams
00008 //
00009 // messier@huhepl.harvard.edu
00011 #include "MessageService/MsgStream.h"
00012 #include <cstdlib>
00013 #include <cstdio>
00014 #include <cstring>
00015 #include <ctime>
00016 #include <vector>
00017 extern "C" {
00018 #include <unistd.h>
00019 }
00020 #include "MessageService/MsgFormat.h"
00021 #include "MessageService/MsgOStream.h"
00022 #include "MessageService/MsgOStreamService.h"
00023 #include "MessageService/MsgService.h"
00024 #include "MessageService/MsgTripWire.h"
00025 
00026 using namespace std;
00027 
00028 // Here is a complete set of ANSI/Xterm control sequences
00029 // font color.
00030 
00031 const char kColor_Reset[]    = { 0x1B, '[', '0', 'm', 0 };
00032 const char kColor_Bold[]     = { 0x1B, '[', '1', 'm', 0 };
00033 const char kColor_Dim[]      = { 0x1B, '[', '2', 'm', 0 };
00034 const char kColor_Underline[]= { 0x1B, '[', '3', 'm', 0 };
00035 const char kColor_Blink[]    = { 0x1B, '[', '5', 'm', 0 };
00036 const char kColor_Reverse[]  = { 0x1B, '[', '7', 'm', 0 };
00037 
00038 const char kColor_Black[]    = { 0x1B, '[', '3', '0', 'm', 0 };
00039 const char kColor_Red[]      = { 0x1B, '[', '3', '1', 'm', 0 };
00040 const char kColor_Green[]    = { 0x1B, '[', '3', '2', 'm', 0 };
00041 const char kColor_Yellow[]   = { 0x1B, '[', '3', '3', 'm', 0 };
00042 const char kColor_Blue[]     = { 0x1B, '[', '3', '4', 'm', 0 };
00043 const char kColor_Magenta[]  = { 0x1B, '[', '3', '5', 'm', 0 };
00044 const char kColor_Cyan[]     = { 0x1B, '[', '3', '6', 'm', 0 };
00045 const char kColor_White[]    = { 0x1B, '[', '3', '7', 'm', 0 };
00046 
00047 const char kColor_BgWhite[]    = { 0x1B, '[', '4', '0', 'm', 0 };
00048 const char kColor_BgRed[]      = { 0x1B, '[', '4', '1', 'm', 0 };
00049 const char kColor_BgGreen[]    = { 0x1B, '[', '4', '2', 'm', 0 };
00050 const char kColor_BgYellow[]   = { 0x1B, '[', '4', '3', 'm', 0 };
00051 const char kColor_BgBlue[]     = { 0x1B, '[', '4', '4', 'm', 0 };
00052 const char kColor_BgMagenta[]  = { 0x1B, '[', '4', '5', 'm', 0 };
00053 const char kColor_BgCyan[]     = { 0x1B, '[', '4', '6', 'm', 0 };
00054 const char kColor_BgBlack[]    = { 0x1B, '[', '4', '7', 'm', 0 };
00055 
00056 
00057 //......................................................................
00058 
00059 MsgStream::MsgStream() 
00060     :fLogLevel(0)
00061     ,fCurrentLogLevel(0)
00062 {
00063 //======================================================================
00064 // Purpose: Create a default message stream
00065 //======================================================================
00066   strcpy(fName, "");
00067   Init();
00068 }
00069 
00070 //......................................................................
00071 
00072 MsgStream::MsgStream(const char* name) 
00073     :fLogLevel(0)
00074     ,fCurrentLogLevel(0)
00075 {
00076 //======================================================================
00077 // Purpose: Create a named message stream
00078 //
00079 // Inputs: name - the name (case sensitive) for this message stream
00080 //======================================================================
00081   if (strlen(name)>kMaxNameSize) {
00082     strncpy(fName, name, kMaxNameSize);
00083     fName[kMaxNameSize] = '\0'; // Terminate string
00084     //std::cerr << 
00085     //  "Warning: over-long message stream name (" << name << ")" <<
00086     //  " truncated to " << kMaxNameSize << " characters." << std::endl;
00087   }
00088   else {
00089     strcpy(fName, name);
00090   }
00091   Init();
00092 }
00093 
00094 
00095 //......................................................................
00096 
00097 void MsgStream::SetLogLevel(Msg::LogLevel_t lvl)
00098 {
00099 //======================================================================
00100 // Purpose: Set the log level
00101 //
00102 // Not inlined anymore so I can tell the MsgService that there has been a global change.
00103 //
00104   fLogLevel = lvl;
00105   MsgService::SetGlobalLevel();
00106 }
00107 
00108 //......................................................................
00109 
00110 void MsgStream::AttachOStream(Msg::LogLevel_t lvl, const char *name) 
00111 {
00112 //======================================================================
00113 // Purpose: Attach a named output stream to the message stream
00114 //
00115 // Inputs: lvl - The threhold for output to this stream
00116 //         name - The name of the output stream we want to attach
00117 //======================================================================
00118 
00119   // Check if the stream is attached
00120   vector<MsgOStream*>::iterator vend(fMsgOStream[lvl].end());
00121   vector<MsgOStream*>::iterator itrOStream = fMsgOStream[lvl].begin();
00122   for (; itrOStream != vend; ++itrOStream) {
00123     if (strcmp(name, (*itrOStream)->GetName())==0) return;
00124   }
00125          
00126   // Output stream not found in list of attached streams -- add it
00127   MsgOStreamService* oStreamService = MsgOStreamService::Instance();
00128   MsgOStream*        msgOStream     = oStreamService->GetStream(name);
00129   fMsgOStream[lvl].push_back(msgOStream);
00130 }
00131 
00132 //......................................................................
00133 void MsgStream::DetachOStream(Msg::LogLevel_t lvl, const char *name) 
00134 {
00135 //======================================================================
00136 // Purpose: Attach a named output stream to the message stream
00137 //
00138 // Inputs: lvl - The threhold for output to this stream
00139 //         name - The name of the output stream we want to attach
00140 //======================================================================
00141 
00142 // Check if the stream is attached
00143   vector<MsgOStream*>::iterator vend(fMsgOStream[lvl].end());
00144   vector<MsgOStream*>::iterator itrOStream = fMsgOStream[lvl].begin();
00145   for (; itrOStream != vend; ++itrOStream) {
00146     if (strcmp(name, (*itrOStream)->GetName())==0) break;
00147   }
00148   if (itrOStream != vend) {
00149     (*itrOStream)->SubtractReference();
00150     fMsgOStream[lvl].erase(itrOStream);
00151   }
00152 }
00153 
00154 //......................................................................
00155 
00156 void MsgStream::AddFormat(Msg::LogLevel_t lvl, int fmt)
00157 {
00158   if((fmt & Msg::kFgColorMask)) fFormat[lvl] &=(~Msg::kFgColorMask); // Reset FG color if being set
00159   if((fmt & Msg::kBgColorMask)) fFormat[lvl] &=(~Msg::kBgColorMask); // Reset BG color if being set
00160   fFormat[lvl] |= fmt;
00161 }
00162 
00163 //......................................................................
00164 
00165 void MsgStream::Close()
00166 {
00167 //======================================================================
00168 // Purpose: Send close requests to all the streams attached to this 
00169 //          message stream
00170 //======================================================================
00171   for (int i=fLogLevel; i<Msg::kNLogLevel; ++i) {
00172     vector<MsgOStream*>::iterator vend(fMsgOStream[i].end());
00173     for (vector<MsgOStream*>::iterator itrOStreamPtr = fMsgOStream[i].begin();
00174          itrOStreamPtr != vend;
00175          ++itrOStreamPtr) {
00176       (*itrOStreamPtr)->Close();
00177     }
00178   }
00179 }
00180 
00181 //......................................................................
00182 
00183 void MsgStream::Flush()
00184 {
00185 //======================================================================
00186 // Purpose: Flush all the output stream attached to this message stream
00187 //======================================================================
00188   for (int i=fLogLevel; i<Msg::kNLogLevel; ++i) {
00189     vector<MsgOStream*>::iterator vend(fMsgOStream[i].end());
00190     for (vector<MsgOStream*>::iterator itrOStreamPtr = fMsgOStream[i].begin();
00191          itrOStreamPtr != vend;
00192          ++itrOStreamPtr) {
00193       (*itrOStreamPtr)->Flush();
00194     }
00195   }
00196 }
00197 
00198 //......................................................................
00199 
00200 MsgStream& MsgStream::operator()(Msg::LogLevel_t priority,
00201                                  const char* file,
00202                                  const char* cvsid,
00203                                  int line)
00204 {
00205 //======================================================================
00206 // Purpose: Prepare the message stream to accept messages. Also print
00207 //          the requested message header information
00208 //
00209 // Inputs: priority - the message's priority
00210 //         file     - the name of the file the message comes from
00211 //         cvsid    - A full CVS id string
00212 //         line     - the line number the message was sent from
00213 //
00214 // Returns: The message stream the message was sent to
00215 //======================================================================
00216   // Set the print threshold to this message's priority
00217   fCurrentLogLevel = priority;
00218 
00219   // Locate tripwire
00220   MsgTripWire& tw(MsgTripWire::Instance());
00221   tw.StartCall();
00222 
00223   // Suppress all messages if tripwire set but not triggered.
00224   if ( tw.SuppressMessage() ) fCurrentLogLevel = Msg::kMinLogLevel -1;
00225 
00226   // Check the threshold. If the message is above threshold track some
00227   // statistics and print the header
00228   if (fCurrentLogLevel>=fLogLevel ) {
00229     int fmt = fFormat[fCurrentLogLevel];
00230 
00231     // Override format and print prefix if tripwire has been triggered
00232     if ( tw.IsActive() ) {
00233       fmt = Msg::kFile | Msg::kLine;
00234       (*this) << tw.GetMessagePrefix();
00235     }
00236 
00237     // The file name frequently is very long. Strip off just the last
00238     // part rather than the full path name
00239     int len = strlen(file);
00240     file += len;
00241     for (;len>0 && *(file-1)!='/'; --len) { --file; }
00242 
00243     // Log the statistics
00244     this->LogPrint(fCurrentLogLevel,file);
00245 
00246     // Check format flags
00247     if (fmt) {
00248 
00249       static bool sSomeoneUsedColor = false;
00250       if(sSomeoneUsedColor) {
00251         (*this) << kColor_Reset;  // Reset whatever attrocities already committed.      
00252       }
00253 
00254       if ((fmt & Msg::kFontMask )) {
00255         sSomeoneUsedColor = true;
00256         if((fmt & Msg::kBold      )) (*this) << kColor_Bold;
00257         if((fmt & Msg::kDim       )) (*this) << kColor_Dim;      
00258         if((fmt & Msg::kUnderline )) (*this) << kColor_Underline; 
00259         if((fmt & Msg::kBlink     )) (*this) << kColor_Blink;     
00260         if((fmt & Msg::kReverse   )) (*this) << kColor_Reverse;
00261    
00262         int color = fmt & Msg::kFgColorMask;
00263         switch(color) {
00264         case Msg::kBlack:  (*this) << kColor_Black;    break;
00265         case Msg::kRed:    (*this) << kColor_Red;      break;
00266         case Msg::kGreen:  (*this) << kColor_Green;    break;
00267         case Msg::kYellow: (*this) << kColor_Yellow;   break;
00268         case Msg::kBlue:   (*this) << kColor_Blue;     break;
00269         case Msg::kMagenta:(*this) << kColor_Magenta;  break;
00270         case Msg::kCyan:   (*this) << kColor_Cyan;     break;
00271         case Msg::kWhite:  (*this) << kColor_White;    break;
00272         default: break;
00273         }
00274         
00275         color = fmt & Msg::kBgColorMask;
00276         switch(color) {
00277         case Msg::kBgBlack:  (*this) << kColor_BgBlack;    break;
00278         case Msg::kBgRed:    (*this) << kColor_BgRed;      break;
00279         case Msg::kBgGreen:  (*this) << kColor_BgGreen;    break;
00280         case Msg::kBgYellow: (*this) << kColor_BgYellow;   break;
00281         case Msg::kBgBlue:   (*this) << kColor_BgBlue;     break;
00282         case Msg::kBgMagenta:(*this) << kColor_BgMagenta;  break;
00283         case Msg::kBgCyan:   (*this) << kColor_BgCyan;     break;
00284         case Msg::kBgWhite:  (*this) << kColor_BgWhite;    break;
00285         default: break;
00286         }
00287       }
00288 
00289       if ((fmt & Msg::kPriority)) {
00290         switch(fCurrentLogLevel) {
00291         case Msg::kVerbose: (*this) << "=V="; break;
00292         case Msg::kDebug:   (*this) << "=D="; break;
00293         case Msg::kSynopsis:(*this) << "=S="; break;
00294         case Msg::kInfo:    (*this) << "=I="; break;
00295         case Msg::kWarning: (*this) << "=W="; break;
00296         case Msg::kError:   (*this) << "=E="; break;
00297         case Msg::kFatal:   (*this) << "=F="; break;
00298         default : abort();
00299         }
00300       }
00301       if ((fmt & Msg::kName)) (*this) << " " << fName;
00302       if ((fmt & Msg::kHost)) {
00303         static const int len = 256;
00304         char host[len];
00305         int err = gethostname(host,len);
00306         if (err==0) (*this) << " " << host;
00307         else        (*this) << " /host?/";
00308         if ((fmt & Msg::kPID)) {
00309           (*this) << ":" << getpid();
00310         }
00311       }
00312       else {
00313         if ((fmt & Msg::kPID)) {
00314           (*this) << " pid=" << getpid();
00315         }
00316       }
00317       if ((fmt & Msg::kTime)) {
00318         this->SetCurrentDateString();
00319         (*this) << " " << fCurrentDate;
00320       }
00321       if ((fmt && Msg::kRunSnarl )) {
00322         int run, snarl;
00323         MsgService::Instance()->GetCurrentRunSnarl(run,snarl);
00324         (*this) << " [" << run << "|" << snarl << "]";
00325       }
00326       if ((fmt & Msg::kCVSId)) {
00327         if (strlen(cvsid)<8) {
00328           // If the cvsid string is less than 8 characters long
00329           // assume it hasn't been filled yet -- use the file name
00330           (*this) << " " << file;
00331         }
00332         else {
00333           // This prints file name and version number
00334           this->SetCVSVersion(cvsid);
00335           (*this) << " " << fCVSVersion;
00336         }
00337       }
00338       else { 
00339         // This prints just the file name
00340         if ((fmt & Msg::kFile)) (*this) << " " << file;
00341       }
00342 
00343       if ((fmt & Msg::kLine)) (*this) << ":" << line;
00344       (*this) << "> ";
00345 
00346       if ((fmt & Msg::kStackTrace)) {
00347         (*this) << "Stack Trace: " << endl;
00348         MsgService::StackTrace(fName,fCurrentLogLevel,4,1);
00349         (*this) << "... Message: ";
00350       }
00351 
00352       if ((fmt & Msg::kFontMask )) {
00353         if((fmt & Msg::kColorAll)) {
00354           // Do nothing.. let the color stand.
00355         } else {
00356           (*this) << kColor_Reset;
00357         }
00358       }
00359 
00360     }
00361   }
00362   if (fCurrentLogLevel == Msg::kFatal) {
00363     MsgService::Instance()->SetkFatalAbort();
00364     cerr << endl << endl;
00365     cerr << "***************** MSGException *****************" << endl;
00366     cerr << "**** MSGException will be thrown for kFatal. ***" << endl;
00367     cerr << endl;
00368     cerr << "A MsgService::MSGException will been thrown at"   << endl;
00369     cerr << "the next entry to MsgService.  This is triggered" << endl;
00370     cerr << "when a kFatal MSG statement is activated."        << endl;
00371     cerr << endl;
00372     cerr << "To investigate the cause without aborting the"    << endl;
00373     cerr << "job, replace the following statement on line " << line;
00374     cerr << endl << "of file " << file << ":" << endl;
00375     cerr << endl;
00376     cerr << "MSG(" << fName << ", Msg::kFatal) << [...] << endl;"<< endl;
00377     cerr << endl;
00378     cerr << "with:" << endl;
00379     cerr << endl;
00380     cerr << "MsgService::Instance()->SetkFatalAbort(0);" << endl;
00381     cerr << "MSG(" << fName << ", Msg::kError) << [...] << endl;"<< endl;
00382     cerr << "// [Add diagnostic code here.]" << endl;
00383     cerr << "***************** MSGException *****************" << endl;
00384     cerr << endl;
00385 
00386     return *this;
00387   }
00388 
00389   return *this;
00390 }
00391 
00392 //......................................................................
00393 
00394 ostream& operator<<(ostream& os, const MsgStream& s)
00395 {
00396 //======================================================================
00397 // Purpose: Print information about the message stream
00398 //
00399 // Inputs: T - the type of output stream to send the information
00400 //======================================================================
00401   os << "*** MsgStream::" << s.fName
00402      << " LogLevel = " << (int)s.fLogLevel << "\n";
00403   for (int i=0; i<Msg::kNLogLevel; ++i) {
00404     os << "(" << i << ") ";
00405     if (s.fFormat[i]&Msg::kPriority) os << "x"; else os << "-";
00406     if (s.fFormat[i]&Msg::kName)     os << "x"; else os << "-";
00407     if (s.fFormat[i]&Msg::kTime)     os << "x"; else os << "-";
00408     if (s.fFormat[i]&Msg::kFile)     os << "x"; else os << "-";
00409     if (s.fFormat[i]&Msg::kCVSId)    os << "x"; else os << "-";
00410     if (s.fFormat[i]&Msg::kLine)     os << "x"; else os << "-";
00411     if (s.fFormat[i]&Msg::kHost)     os << "x"; else os << "-";
00412     if (s.fFormat[i]&Msg::kPID)      os << "x"; else os << "-";
00413     vector<MsgOStream*>::const_iterator iter(s.fMsgOStream[i].begin());
00414     vector<MsgOStream*>::const_iterator iterEnd(s.fMsgOStream[i].end());
00415     for (; iter != iterEnd; ++iter) {
00416       os << " " << (*iter)->GetName();
00417     }
00418     os << "\n";
00419   }
00420   os << "***\n";
00421   return os;
00422 }
00423 
00424 //......................................................................
00425 
00426 void MsgStream::Init() 
00427 {
00428 //======================================================================
00429 // Purpose: Set an initial state for a MsgStream
00430 //======================================================================
00431   fLogLevel = Msg::kInfo;
00432   for (int i=0; i<Msg::kNLogLevel; ++i) {
00433     fFormat[i]     = 0;
00434   }
00435   fCurrentLogLevel = fLogLevel;
00436 }
00437 
00438 //......................................................................
00439 
00440 void MsgStream:: LogPrint(Msg::LogLevel_t priority, const char* file) 
00441 {
00442 //======================================================================
00443 // Purpose: Log the statistics
00444 //======================================================================
00445   // The file name frequently is very long. Strip off just the last
00446   // part rather than the full path name
00447   int len = strlen(file);
00448   file += len;
00449   for (;len>0 && *(file-1)!='/'; --len) { --file; }
00450   // Log the statistics
00451   fFileStat[string(file)].LogPrint(priority);
00452 }
00453 
00454 //......................................................................
00455 
00456 void MsgStream::SetCurrentDateString()
00457 {
00458 //======================================================================
00459 // Purpose: Update the time stamp used by this message stream
00460 //======================================================================
00461   time_t tt;
00462   struct tm *tp;
00463   tt = time(NULL);
00464   tp = localtime(&tt);
00465   sprintf(fCurrentDate, "%.4d/%.2d/%.2d %.2d:%.2d:%.2d", 
00466           tp->tm_year+1900,
00467           tp->tm_mon+1,
00468           tp->tm_mday,
00469           tp->tm_hour,
00470           tp->tm_min,
00471           tp->tm_sec);
00472 }
00473 
00474 //......................................................................
00475 
00476 void MsgStream::SetCVSVersion(const char* CVSId)
00477 {
00478 //======================================================================
00479 // Purpose: Update the CVS version number used by this message stream
00480 //
00481 // Input: CVSId - Id string filled by CVS
00482 //======================================================================
00483   int nsegment = 0;
00484   register int j = 0;
00485   for (unsigned int i=1; (i<strlen(CVSId)) && (i<127); ++i) {
00486     if (CVSId[i] != ' ' && CVSId[i-1] == ' ') {
00487       if (++nsegment == 3) break;
00488     }
00489     if (CVSId[i]!=' ' && nsegment>0) {
00490       fCVSVersion[j++] = CVSId[i];
00491     }
00492   }
00493   fCVSVersion[j] = '\0';
00494 }
00495 
00496 //......................................................................
00497 
00498 void MsgStream::StatPrint(ostream& os, const char* label, int n) const
00499 {
00500 //======================================================================
00501 // Purpose: Print a nicely formatted entry in the statistics table
00502 //======================================================================
00503   if (n>0) {
00504     MsgFormat ifmt("%-5i");
00505     os << label << ifmt(n);
00506   }
00507   else {
00508     //     12345678
00509     os << "        ";
00510   }
00511 }
00512 
00513 //......................................................................
00514 
00515 void MsgStream::PrintStatistics(ostream& os)
00516 {
00517 //======================================================================
00518 // Purpose: Print the number of times each kind of message has been 
00519 //          printed using this stream
00520 //======================================================================
00521   map<string,MsgStatistic>::iterator mend(fFileStat.end());
00522   for (map<string,MsgStatistic>::iterator itrFileStat = fFileStat.begin();
00523        itrFileStat != mend;
00524        ++itrFileStat) {
00525 
00526     // Flag streams with error conditions
00527     if (itrFileStat->second.GetPrintCount(Msg::kFatal) +
00528         itrFileStat->second.GetPrintCount(Msg::kError)>0) {
00529       os << "*";
00530     }
00531     else os << " ";
00532 
00533     // Print the name of the stream
00534     os << fName;
00535     for (int i=strlen(fName); i<7; ++i) os << " ";
00536 
00537     // Print the file name
00538     os << itrFileStat->first;
00539     for (int i=strlen((*itrFileStat).first.c_str()); i<24; ++i) os << " ";
00540 
00541     // Print the statistics
00542     this->StatPrint(os, " F=",
00543                     itrFileStat->second.GetPrintCount(Msg::kFatal));
00544     this->StatPrint(os, " E=",
00545                     itrFileStat->second.GetPrintCount(Msg::kError));
00546     this->StatPrint(os, " W=",
00547                     itrFileStat->second.GetPrintCount(Msg::kWarning));
00548     this->StatPrint(os, " I=",
00549                     itrFileStat->second.GetPrintCount(Msg::kInfo));
00550     this->StatPrint(os, " S=",
00551                     itrFileStat->second.GetPrintCount(Msg::kSynopsis));
00552     this->StatPrint(os, " D=",
00553                     itrFileStat->second.GetPrintCount(Msg::kDebug));
00554     this->StatPrint(os, " V=",
00555                     itrFileStat->second.GetPrintCount(Msg::kVerbose));
00556     os << endl;
00557   }
00558 }

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