FesaLogger.cpp

Go to the documentation of this file.
00001 // Copyright CERN 2012 - Developed in collaboration with GSI
00002 //
00003 // FesaLogger.cpp: implementation of the Logger objects
00004 //
00006 
00007 #include <fesa-core/Diagnostic/FesaLogger.h>
00008 #include <fesa-core/Utilities/Lock.h>
00009 
00010 #include <iostream>
00011 #include <syslog.h>
00012 
00013 namespace fesa
00014 {
00015 
00016     class FesaLoggerBuffer;
00017 
00018     /* -------------------------------------------------------------------------
00019      */
00020     //Global data of the FesaLogger class - look at header file for comments
00021     const std::string FesaLogger::fwkSender = "FWK";
00022     const std::string FesaLogger::usrSender = "USR";
00023     std::map<std::string, FesaLogger *> FesaLogger::logFactoryMap_;
00024     Mutex FesaLogger::mutex_;
00025     UdpLogger* FesaLogger::pUDPLog_ = NULL;
00026     std::string FesaLogger::connectedHostName_ = "";
00027     int32_t FesaLogger::connectedPortNumber_ = -1;
00028     uint32_t FesaLogger::logCount_ = 0;
00029     TsCounter FesaLogger::logTsc_(true);
00030     bool FesaLogger::modeDiagnostic_ = false;
00031     //during start-up (any tracing will be rooted to the console output)
00032     bool FesaLogger::isInitPhase_ = true;
00033     int32_t FesaLogger::fwkTopic_ = 0xffffffff;
00034     FesaLogger* FesaLogger::defaultLog_ =
00035         FesaLogger::getLogger(defaultLogName, undefinedThreadName, undefinedClassName);
00036     //In case of modification, don't forget to update 'LogAction' enumeration.
00037     const char* FesaLogger::logActionStr_[] = { "|-|", "|START|", "|STOP|" };
00038 
00039     /* -------------------------------------------------------------------------
00040      */
00041     FesaLogger::FesaLogger(FesaLoggerBuffer* pBuf) :
00042         std::ostream(pBuf)
00043     {
00044         logBuffer_ = pBuf;
00045         pBuf->setFesaLogger(this); //Buffer use Logger inside sync() method
00046         //if FesaLogger is instantiated during start-up phase, diagnostic should be enabled.
00047         modeDiagnostic_ = isInitPhase_;
00048         customTopic_ = 0xffffffff;
00049         lastError_ = 0;
00050         errSeverity_ = none;
00051     }
00052 
00053     /* -------------------------------------------------------------------------
00054      * copy constructor
00055      */
00056     FesaLogger::FesaLogger(const FesaLogger& refLogger) :
00057         std::ostream(NULL)
00058     {
00059 
00060         //create a new instance of the FesaLogger resources
00061         FesaLoggerBuffer* pBuffer = new FesaLoggerBuffer();
00062         rdbuf(pBuffer); //set stream-buffer of the ostream base class
00063         logBuffer_ = pBuffer;
00064         pBuffer->setFesaLogger(this);
00065         modeDiagnostic_ = refLogger.modeDiagnostic_;
00066         lastError_ = 0;
00067 
00068         // the new FesaLogger is a copy of the refLogger
00069         className_ = refLogger.className_;
00070         logSource_ = refLogger.logSource_;
00071         logThread_ = refLogger.logThread_;
00072         logName_ = refLogger.logName_;
00073         logType_ = refLogger.logType_;
00074         customTopic_ = refLogger.customTopic_;
00075         errSeverity_ = refLogger.errSeverity_;
00076     }
00077 
00078     /* -------------------------------------------------------------------------
00079      */
00080     FesaLogger::~FesaLogger()
00081     {
00082 
00083         // FesaLogger clean-up (dynamic resources)
00084         if (logBuffer_ != NULL)
00085             delete logBuffer_;
00086     }
00087 
00088     /* -------------------------------------------------------------------------
00089      */
00090     void FesaLogger::init(char *bname)
00091     {
00092         /* Error reporting service: uses standard syslog mechanism
00093          * syslog uses file configuration (required root access):
00094          * /etc/<syslog.conf>     : to define the ouput channel (file, console, host)
00095          * /etc/<sysconfig/syslog>: to set-up the syslogd daemon
00096          */
00097         openlog(bname, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_USER);
00098     }
00099 
00100     bool FesaLogger::isEnabled()
00101     {
00102         return modeDiagnostic_;
00103     }
00104 
00105     /* -------------------------------------------------------------------------
00106      */
00107     void FesaLogger::enableConsoleOutput()
00108     {
00109         /* During the process start-up, client cannot be connected to get tracing
00110          * std::cout should be used instead.
00111          * Any tracing should be activated bypass macro tracing filtering.
00112          */
00113         isInitPhase_ = true;
00114         fwkTopic_ = 0xffffffff;
00115         std::map<std::string, FesaLogger *>::iterator itr;
00116         for (itr = logFactoryMap_.begin(); itr != logFactoryMap_.end(); itr++)
00117         {
00118             FesaLogger* pLogger = (FesaLogger *) ((*itr).second);
00119             pLogger->customTopic_ = 0xffffffff;
00120         }
00121 
00122         setDiagnosticMode(true);
00123     }
00124 
00125     /* -------------------------------------------------------------------------
00126      */
00127     void FesaLogger::disableConsoleOutput()
00128     {
00129         //From now tracing service is enabled, std::cout will not be used anymore.
00130         //Diagnostic mode is not activated yet (will be done by setDiagnosticSetting).
00131         setDiagnosticMode(false);
00132         fwkTopic_ = 0;
00133         std::map<std::string, FesaLogger *>::iterator itr;
00134         for (itr = logFactoryMap_.begin(); itr != logFactoryMap_.end(); itr++)
00135         {
00136             FesaLogger* pLogger = (FesaLogger *) ((*itr).second);
00137             pLogger->customTopic_ = 0;
00138         }
00139         isInitPhase_ = false;
00140     }
00141 
00142     /* -------------------------------------------------------------------------
00143      */
00144     void FesaLogger::reinit()
00145     {
00146         //Create a new trace logging object using new parameters and switch to it
00147         UdpLogger* oldTraceLog = NULL;
00148         if (pUDPLog_ != NULL)
00149             oldTraceLog = pUDPLog_;
00150         unsigned short lport = (unsigned short) connectedPortNumber_;
00151 
00152         //on that step, every TRACE FesaLogger refer the current trace channel
00153         // Must be in connected mode for dynamic port allocation issue
00154 
00155         void (*oldHandler)(int32_t) = signal(SIGPIPE, SIG_IGN);
00156         pUDPLog_ = new UdpLogger(connectedHostName_.c_str(), lport, /*connected mode=*/false);
00157 
00158         //reset tracing counter
00159         logCount_ = 0;
00160 
00161         if (oldTraceLog != NULL)
00162             delete oldTraceLog;
00163         signal(SIGPIPE, oldHandler);
00164     }
00165 
00166     /* -------------------------------------------------------------------------
00167      */
00168     void FesaLogger::destroy()
00169     {
00170 
00171         // FesaLogger clean-up (static resources)
00172         std::map<std::string, FesaLogger *>::iterator itr;
00173         for (itr = logFactoryMap_.begin(); itr != logFactoryMap_.end(); itr++)
00174         {
00175             delete ((FesaLogger *) ((*itr).second));
00176         }
00177 
00178         //remove Trace resources
00179         if (FesaLogger::pUDPLog_ != NULL)
00180             delete FesaLogger::pUDPLog_;
00181 
00182         //remove syslog resources
00183         closelog();
00184     }
00185 
00186     /* -------------------------------------------------------------------------
00187      */
00188     FesaLogger* FesaLogger::getLogger(const std::string& source, const std::string& thread)
00189     {
00190         return getLogger(source, thread, undefinedClassName);
00191     }
00192 
00193     /* -------------------------------------------------------------------------
00194      */
00195     FesaLogger* FesaLogger::getLogger(const std::string& source, const std::string& thread, const std::string& cname)
00196     {
00197 
00198         FesaLogger* pLogger;
00199         FesaLoggerBuffer* pBuffer;
00200 
00201         const std::string& cname_ = (cname.empty()) ? undefinedClassName : cname;
00202         const std::string& thread_ = (thread.empty()) ? undefinedThreadName : thread;
00203 
00204         // FesaLogger must be unique per source/thread. This prevent concurrent access on the same logger.
00205         // Force using shared-logger in case of className missing and source is not SHARED_LOGNAME.
00206         //        std::string loggerName = ((cname == undefinedClassName) ? defaultLogName : source);
00207         std::string loggerName = source + thread_ + cname_;
00208 
00209         // Protect critical resource by Mutex: logFactoryMap
00210         // The unlock is automatic so even in case of any exception
00211         // the unlock is done
00212         {
00213             Lock lock(mutex_);
00214 
00215             std::map<std::string, FesaLogger *>::iterator itr = logFactoryMap_.find(loggerName);
00216             if (itr != logFactoryMap_.end())
00217             {
00218 //                std::cout << "get FesaLogger name: " << ""
00219 //                                << "(source: " << source << " thread: " << thread_ << " cname: " << cname << std::endl;
00220                 //This FesaLogger has already been defined
00221                 pLogger = (FesaLogger *) ((*itr).second);
00222             }
00223             else
00224             {
00225 //                std::cout << "instantiate FesaLogger::FesaLogger() name: " <<  loggerName
00226 //                                << "(source: " << source << " thread: " << thread_ << " cname: " << cname << std::endl;
00227                 pBuffer = new FesaLoggerBuffer();
00228                 pLogger = new FesaLogger(pBuffer);
00229 
00230                 //register the new instance
00231                 logFactoryMap_[loggerName] = pLogger;
00232 
00233                 pLogger->className_ = cname_;
00234 
00235                 //store the log name which will be used as message header
00236                 pLogger->logSource_ = source + "|";
00237                 pLogger->logThread_ = "|" + thread_ + "|";
00238                 pLogger->logName_ = loggerName;
00239                 //Default logger type is TRACE
00240                 pLogger->logType_ = FesaLogger::traceType;
00241                 // std::cout << "NEW FesaLogger object (UDP): " << pLogger->logName
00242                     // _ << std::endl;
00243             }
00244             //the lock is released here
00245         }
00246         return pLogger;
00247     }
00248 
00249     /* -------------------------------------------------------------------------
00250      * Set Diagnostic mode for all Loggers of the process.
00251      */
00252     void FesaLogger::setDiagnosticMode(bool mode)
00253     {
00254         modeDiagnostic_ = mode;
00255 
00256         // clean each fesaLogger
00257         std::map<std::string, FesaLogger *>::iterator itr;
00258         for (itr = logFactoryMap_.begin(); itr != logFactoryMap_.end(); itr++)
00259         {
00260             FesaLogger* pLogger = (FesaLogger *) ((*itr).second);
00261             pLogger->lastError_ = 0;
00262         }
00263 
00264         // when the diagnostic is disabled the socket is closed
00265         if (mode == false && pUDPLog_ != NULL)
00266         {
00267             connectedHostName_.erase();
00268             connectedPortNumber_ = -1;
00269             delete pUDPLog_;
00270             pUDPLog_ = NULL;
00271         }
00272     }
00273 
00274     /* -------------------------------------------------------------------------
00275      * Set Diagnostic mode for all Loggers of the process.
00276      */
00277     void FesaLogger::setCustomTopic(int32_t customTopic, const std::string& cname)
00278     {
00279         //TODO : for the time being the cname is ignored
00280         // Set Diagnostic mode for each Logger
00281         std::map<std::string, FesaLogger *>::iterator itr;
00282         for (itr = logFactoryMap_.begin(); itr != logFactoryMap_.end(); itr++)
00283         {
00284             FesaLogger* pLogger = (FesaLogger *) ((*itr).second);
00285             // Update Custom topic for all the log instances of the concerned class.
00286             if (pLogger->className_ == cname)
00287             {
00288                 pLogger->customTopic_ = customTopic;
00289             }
00290         }
00291     }
00292 
00293     /* -------------------------------------------------------------------------
00294      */
00295     void FesaLogger::send(const char* msg, LogType type)
00296     {
00297 
00298         /* Select the appropriate logger object which has been instantiate
00299          * depending on the FesaLogger type.
00300          * TRACE: log on UDP or cout during init-phase
00301          */
00302         if (pUDPLog_ == NULL)
00303             return;
00304         if ((logType_ = type) == FesaLogger::traceType)
00305         {
00306             if (!isInitPhase_)
00307             {
00308                 /*UDP send could fail because of unexpected reason: network failure, ..
00309                  * or it could fail beacause of "acceptable reason": client does not get messages fast enough!
00310                  * In the latter, do nothing, just wait for normal situation back
00311                  */
00312                 int32_t err = FesaLogger::pUDPLog_->sendStr(msg);
00313                 switch (err)
00314                 {
00315                 //Expected cases -----------
00316                 case 0: //Everything was ok
00317                 case EAGAIN: //Resource temporarily unavailable
00318                     /*reset lastError on Diagnostic setting only:
00319                      UDP socket send is buffered and return OK even if client is deconnected!
00320                      */
00321                     break;
00322                     //Unexpected cases ---------
00323                     //case ENOBUFS:     //No buffer space available
00324                 default:
00325                     //log critical error of UDP service
00326                     if (lastError_ != err)
00327                     {
00328                         //do not notify the same error again
00329                         std::string str("UDP send failure: ");
00330                         str += strerror(err);
00331                         syslog(errSeverity_, (char *) str.c_str());
00332                         lastError_ = err;
00333                     }
00334                 }
00335             }
00336             else
00337             {
00338                 /* During the process start-up, client cannot be connected to get tracing
00339                  * std::cout should be used instead (with no tracing header).
00340                  */
00341                 std::string str = std::string(msg);
00342                 std::cout << str.substr(str.rfind('|') + 1) << std::endl;
00343             }
00344         }
00345         else
00346         {
00347             //ERROR: log on syslogger or root on UDP if diagnostic mode is enabled.
00348             //During process start-up, still root error to syslogger
00349             if (modeDiagnostic_ && !FesaLogger::isInitPhase_)
00350             {
00351                 int32_t err = FesaLogger::pUDPLog_->sendStr(msg);
00352                 switch (err)
00353                 {
00354                 //Expected cases -----------
00355                 case 0: //Everything was ok
00356                 case EAGAIN: //Resource temporarily unavailable
00357                     /*reset lastError on Diagnostic setting only:
00358                      UDP socket send is buffered and return OK even if client is deconnected!
00359                      */
00360                     break;
00361                     //Unexpected cases ---------
00362                     //case ENOBUFS:     //No buffer space available
00363                 default:
00364                     //log critical error of UDP service
00365                     if (lastError_ != err)
00366                     { //do not notify the same error again
00367                         std::string str("UDP send failure: ");
00368                         str += strerror(err);
00369                         syslog(errSeverity_, (char *) str.c_str());
00370                         lastError_ = err;
00371                     }
00372                 }
00373             }
00374             else
00375             {
00376                 //Diagnostic is disable, Errors go to normal error reporting channel.
00377                 syslog(errSeverity_, (char *) msg);
00378 
00379                 //TODO: We dont want to have console output during RUN? Not even on Critical Errors and Alarms??
00380                 //From my point of view(Alex) putting critic errors into the std-linux logfile via syslog is not enaugh.
00381                 //to be discussed
00382 
00383                 //if (FesaLogger::isInitPhase_)
00384                 //"error" or worse
00385                 if (errSeverity_ <= FesaLogger::error || FesaLogger::isInitPhase_)
00386                     std::cout << msg << std::endl; //add console output during start-up
00387             }
00388         }
00389     }
00390 
00391     /* -------------------------------------------------------------------------
00392      */
00393     int32_t FesaLoggerBuffer::sync()
00394     {
00395 
00396         // sendStr() method expects null terminated buffer but
00397         // streambuf class does not set string terminaison.
00398         *pptr() = '\0';
00399         pbump(1);
00400 
00401         int32_t num = static_cast<int32_t>(pptr() - pbase());
00402 
00403         /* Select the appropriate logger object which has been instantiate
00404          * depending on the FesaLogger type.
00405          * TRACE: log on UDP or cout during init-phase
00406          */
00407         if (logger_->logType_ == FesaLogger::traceType)
00408         {
00409 
00410             if ((!FesaLogger::isInitPhase_) && (FesaLogger::pUDPLog_ != NULL))
00411             {
00412                 /*UDP send could fail because of unexpected reason: network failure, ..
00413                  * or it could fail beacause of "acceptable reason": client does not get messages fast enough!
00414                  * In the latter, do nothing, just wait for normal situation back
00415                  */
00416                 int32_t err = FesaLogger::pUDPLog_->sendStr(pbase());
00417                 switch (err)
00418                 {
00419                 //Expected cases -----------
00420                 case 0: //Everything was ok
00421                 case EAGAIN: //Resource temporarily unavailable
00422                     /*reset lastError on Diagnostic setting only:
00423                      UDP socket send is buffered and return OK even if client is deconnected!
00424                      */
00425                     break;
00426                     //Unexpected cases ---------
00427                     //case ENOBUFS:     //No buffer space available
00428                 default:
00429                     //log critical error of UDP service
00430                     if (logger_->lastError_ != err)
00431                     { //do not notify the same error again
00432                         std::string str("UDP send failure: ");
00433                         str += strerror(err);
00434                         syslog(logger_->errSeverity_, (char *) str.c_str());
00435                         logger_->lastError_ = err;
00436                     }
00437                 }
00438             }
00439             else
00440             {
00441                 /* During the process start-up, client cannot be connected to get tracing
00442                  * std::cout should be used instead (with no tracing header).
00443                  */
00444                 if (pbase() != NULL)
00445                 {
00446                     std::string str = std::string(pbase());
00447                     uint32_t position = static_cast<uint32_t>(str.rfind("|"));
00448 
00449                     if (position == std::string::npos)
00450                         std::cout << str;
00451                     else
00452                         std::cout << str.substr(position + 1);
00453                 }
00454             }
00455         }
00456         else
00457         {
00458             //ERROR: log on syslogger or root on UDP if diagnostic mode is enabled.
00459             //During process start-up, still root error to syslogger
00460             if (logger_->modeDiagnostic_ && !FesaLogger::isInitPhase_ && (FesaLogger::pUDPLog_ != NULL))
00461             {
00462                 int32_t err = FesaLogger::pUDPLog_->sendStr(pbase());
00463                 switch (err)
00464                 {
00465                 //Expected cases -----------
00466                 case 0: //Everything was ok
00467                 case EAGAIN: //Resource temporarily unavailable
00468                     /*reset lastError on Diagnostic setting only:
00469                      UDP socket send is buffered and return OK even if client is deconnected!
00470                      */
00471                     break;
00472                     //Unexpected cases ---------
00473                     //case ENOBUFS:     //No buffer space available
00474                 default:
00475                     //log critical error of UDP service
00476                     if (logger_->lastError_ != err)
00477                     { //do not notify the same error again
00478                         std::string str("UDP send failure: ");
00479                         str += strerror(err);
00480                         syslog(logger_->errSeverity_, (char *) str.c_str());
00481                         logger_->lastError_ = err;
00482                     }
00483                 }
00484             }
00485             else
00486             {
00487                 //Diagnostic is disable, Errors go to normal error reporting channel.
00488                 syslog(logger_->errSeverity_, pbase());
00489 
00490                 //TODO: We dont want to have console output during RUN? Not even on Critical Errors and Alarms??
00491                 //From my point of view(Alex) putting critic errors into the std-linux logfile via syslog is not enaugh.
00492                 //to be discussed
00493 
00494                 //if (FesaLogger::isInitPhase_)
00495                 //"error" or worse
00496                 if (logger_->errSeverity_ <= FesaLogger::error || FesaLogger::isInitPhase_)
00497                     std::cout << pbase() << std::endl;
00498             }
00499         }
00500 
00501         //clear buffer: set write pointer to base pointer
00502         pbump(-num);
00503         return std::streambuf::sync();
00504     }
00505 
00506     /* -------------------------------------------------------------------------
00507      * FesaLogger buffer uses fixe size for performance question (no dynamic alloc).
00508      * In case of buffer overflow, truncated message will be sent.
00509      */
00510     int32_t FesaLoggerBuffer::overflow(int32_t c)
00511     {
00512 
00513         //buffer is full, do not bufferize character and act as null termination.
00514         return (int32_t) '\0';
00515     }
00516 
00517 }

Generated on 18 Jan 2013 for Fesa by  doxygen 1.6.1