Orocos Real-Time Toolkit  2.5.0
Logger.cpp
00001 /***************************************************************************
00002   tag: Peter Soetens  Mon Jan 10 15:59:15 CET 2005  Logger.cxx
00003 
00004                         Logger.cxx -  description
00005                            -------------------
00006     begin                : Mon January 10 2005
00007     copyright            : (C) 2005 Peter Soetens
00008     email                : peter.soetens@mech.kuleuven.ac.be
00009 
00010  ***************************************************************************
00011  *   This library is free software; you can redistribute it and/or         *
00012  *   modify it under the terms of the GNU General Public                   *
00013  *   License as published by the Free Software Foundation;                 *
00014  *   version 2 of the License.                                             *
00015  *                                                                         *
00016  *   As a special exception, you may use this file as part of a free       *
00017  *   software library without restriction.  Specifically, if other files   *
00018  *   instantiate templates or use macros or inline functions from this     *
00019  *   file, or you compile this file and link it with other files to        *
00020  *   produce an executable, this file does not by itself cause the         *
00021  *   resulting executable to be covered by the GNU General Public          *
00022  *   License.  This exception does not however invalidate any other        *
00023  *   reasons why the executable file might be covered by the GNU General   *
00024  *   Public License.                                                       *
00025  *                                                                         *
00026  *   This library is distributed in the hope that it will be useful,       *
00027  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00028  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00029  *   Lesser General Public License for more details.                       *
00030  *                                                                         *
00031  *   You should have received a copy of the GNU General Public             *
00032  *   License along with this library; if not, write to the Free Software   *
00033  *   Foundation, Inc., 59 Temple Place,                                    *
00034  *   Suite 330, Boston, MA  02111-1307  USA                                *
00035  *                                                                         *
00036  ***************************************************************************/
00037 
00038 // to retrieve RTAI version, if any.
00039 //#define OROBLD_OS_LXRT_INTERNAL
00040 #include "os/StartStopManager.hpp"
00041 #include "os/MutexLock.hpp"
00042 #include "os/Mutex.hpp"
00043 #include "os/TimeService.hpp"
00044 
00045 #include "Logger.hpp"
00046 #include <iomanip>
00047 
00048 #ifdef OROSEM_PRINTF_LOGGING
00049 #  include <stdio.h>
00050 #else
00051 #  include <iostream>
00052 #  include <ostream>
00053 #  ifdef OROSEM_FILE_LOGGING
00054 #   include <fstream>
00055 #  endif
00056 #  ifdef OROSEM_REMOTE_LOGGING
00057 #   include <sstream>
00058 #  endif
00059 #endif
00060 
00061 #include <stdlib.h>
00062 #include "rtt-config.h"
00063 #include "rtt-fwd.hpp"
00064 
00065 namespace RTT
00066 {
00067     using namespace std;
00068     using namespace detail;
00069 
00070     Logger* Logger::_instance = 0;
00071 
00072     Logger* Logger::Instance(std::ostream& str) {
00073         if (_instance == 0) {
00074             _instance =  new Logger(str);
00075         }
00076         return _instance;
00077     }
00078 
00079     void Logger::Release() {
00080       if (_instance) {
00081         _instance->shutdown();
00082         delete _instance;
00083         _instance = 0;
00084       }
00085     }
00086 
00087 #ifndef OROBLD_DISABLE_LOGGING
00088 
00089     Logger& Logger::log() {
00090         return *Instance();
00091     }
00092 
00093     Logger& Logger::log(LogLevel ll) {
00094         return Instance()->operator<<( ll );
00095     }
00096 
00100     struct Logger::D
00101     {
00102     public:
00103         D(std::ostream& str, char const* logfile_name) :
00104 #ifndef OROSEM_PRINTF_LOGGING
00105               stdoutput( &str ),
00106 #endif
00107 #ifdef OROSEM_REMOTE_LOGGING
00108               messagecnt(0),
00109 #endif
00110 #if defined(OROSEM_FILE_LOGGING) && !defined(OROSEM_PRINTF_LOGGING)
00111               logfile(logfile_name ? logfile_name : "orocos.log"),
00112 #endif
00113               inloglevel(Info),
00114               outloglevel(Warning),
00115               timestamp(0),
00116               started(false), showtime(true), allowRT(false),
00117               mlogStdOut(true), mlogFile(true),
00118               moduleptr("Logger")
00119         {
00120 #if defined(OROSEM_FILE_LOGGING) && defined(OROSEM_PRINTF_LOGGING)
00121             logfile = fopen(logfile_name ? logfile_name : "orocos.log","w");
00122 #endif
00123         }
00124 
00125         bool maylog() const {
00126             if (!started || (outloglevel == RealTime && allowRT == false))
00127                 return false;
00128             return true;
00129         }
00130 
00131         bool maylogStdOut() const {
00132             if ( inloglevel <= outloglevel && outloglevel != Never && inloglevel != Never && mlogStdOut)
00133                 return true;
00134             return false;
00135         }
00136 
00137         bool maylogFile() const {
00138             if ( (inloglevel <= Info || inloglevel <= outloglevel)  && mlogFile)
00139                 return true;
00140             return false;
00141         }
00142 
00148         void logit(std::ostream& (*pf)(std::ostream&))
00149         {
00150             // only on Logger::nl or Logger::endl, a time+log-line is written.
00151             os::MutexLock lock( inpguard );
00152             std:: string res = showTime() +" " + showLevel(inloglevel) + showModule() + " ";
00153 
00154             // do not log if not wanted.
00155             if ( maylogStdOut() ) {
00156 #ifndef OROSEM_PRINTF_LOGGING
00157                 *stdoutput << res << logline.str() << pf;
00158 #else
00159                 printf("%s%s\n", res.c_str(), logline.str().c_str() );
00160 #endif
00161                 logline.str("");   // clear stringstream.
00162             }
00163 
00164             if ( maylogFile() ) {
00165 #ifdef OROSEM_FILE_LOGGING
00166 #ifndef OROSEM_PRINTF_LOGGING
00167                 logfile << res << fileline.str() << pf;
00168 #else
00169                 fprintf( logfile, "%s%s\n", res.c_str(), fileline.str().c_str() );
00170 #endif
00171 #ifdef OROSEM_REMOTE_LOGGING
00172                 // detect buffer 'overflow'
00173                 if ( messagecnt >= ORONUM_LOGGING_BUFSIZE ) {
00174                     std::string dummy;
00175                     remotestream >> dummy; // FIFO principle: read 1 line
00176                     --messagecnt;
00177                 }
00178                 remotestream << res << fileline.str() << pf;
00179                 ++messagecnt;
00180 #endif
00181 #if defined(OROSEM_FILE_LOGGING) || defined(OROSEM_REMOTE_LOGGING)
00182                 fileline.str("");
00183 #endif
00184 #endif
00185             }
00186         }
00187 
00188 #ifndef OROSEM_PRINTF_LOGGING
00189         std::ostream* stdoutput;
00190 #endif
00191         std::stringstream logline;
00192 #if defined(OROSEM_FILE_LOGGING) || defined(OROSEM_REMOTE_LOGGING)
00193         std::stringstream fileline;
00194 #endif
00195 #if defined(OROSEM_REMOTE_LOGGING)
00196         std::stringstream remotestream;
00197         unsigned int messagecnt;
00198 #endif
00199 #if defined(OROSEM_FILE_LOGGING)
00200 # ifndef OROSEM_PRINTF_LOGGING
00201         std::ofstream logfile;
00202 # else
00203         FILE* logfile;
00204 # endif
00205 #endif
00206         LogLevel inloglevel, outloglevel;
00207 
00208         TimeService::ticks timestamp;
00209 
00210         Logger::LogLevel intToLogLevel(int ll) {
00211             switch (ll)
00212                 {
00213                 case -1:
00214                 case 0:
00215                     return Never;
00216                 case 1:
00217                     return Fatal;
00218                 case 2:
00219                     return Critical;
00220                 case 3:
00221                     return Error;
00222                 case 4:
00223                     return Warning;
00224                 case 5:
00225                     return Info;
00226                 case 6:
00227                     return Debug;
00228                 }
00229             return Debug; // > 6
00230         }
00231 
00232 
00233         std::string showTime() const
00234         {
00235             std::stringstream time;
00236             if ( showtime )
00237                 time <<fixed<< showpoint << setprecision(3) << TimeService::Instance()->secondsSince(timestamp);
00238             return time.str();
00239         }
00240 
00244         std::string showLevel( LogLevel ll) const {
00245             std::string prefix;
00246             switch (ll)
00247                 {
00248                 case Fatal:
00249                     prefix="[ FATAL  ]";
00250                     break;
00251                 case Critical:
00252                     prefix="[CRITICAL]";
00253                     break;
00254                 case Error:
00255                     prefix="[ ERROR  ]";
00256                     break;
00257                 case Warning:
00258                     prefix="[ Warning]";
00259                     break;
00260                 case Info:
00261                     prefix="[ Info   ]";
00262                     break;
00263                 case Debug:
00264                     prefix="[ Debug  ]";
00265                     break;
00266                 case RealTime:
00267                     prefix="[RealTime]";
00268                     break;
00269                 case Never:
00270                     break;
00271                 }
00272             return prefix;
00273         }
00274 
00275 
00276 
00277         std::string showModule() const
00278         {
00279             // moduleptr is protected by lock in logIt()
00280             return "["+moduleptr+"]";
00281         }
00282 
00283         bool started;
00284 
00285         bool showtime;
00286 
00287         bool allowRT;
00288 
00289         bool mlogStdOut, mlogFile;
00290 
00291         std::string moduleptr;
00292 
00293         os::Mutex inpguard;
00294     };
00295 
00296     Logger::Logger(std::ostream& str)
00297         :d ( new Logger::D(str, getenv("ORO_LOGFILE")) ),
00298          inpguard(d->inpguard), logline(d->logline), fileline(d->fileline)
00299     {
00300       this->startup();
00301     }
00302 
00303     Logger::~Logger()
00304     {
00305         delete d;
00306     }
00307 
00308     bool Logger::mayLog() const {
00309         return d->maylog();
00310     }
00311 
00312     bool Logger::mayLogFile() const {
00313         return d->maylogFile();
00314     }
00315 
00316     bool Logger::mayLogStdOut() const {
00317         return d->maylogStdOut();
00318     }
00319 
00320     void Logger::mayLogStdOut(bool tf) {
00321         d->mlogStdOut = tf;
00322     }
00323 
00324     void Logger::mayLogFile(bool tf) {
00325         d->mlogFile = tf;
00326     }
00327 
00328     void Logger::allowRealTime() {
00329         *this << Logger::Warning << "Enabling Real-Time Logging !" <<Logger::endl;
00330         d->allowRT = true;
00331     }
00332     void Logger::disallowRealTime() {
00333         *this << Logger::Warning << "Disabling Real-Time Logging !" <<Logger::endl;
00334         d->allowRT = false;
00335     }
00336 
00337     TimeService::ticks Logger::getReferenceTime()const
00338     {
00339         return d->timestamp;
00340     }
00341 
00342     std::ostream&
00343     Logger::nl(std::ostream& __os)
00344     {
00345 #ifndef OROSEM_PRINTF_LOGGING
00346         return __os.put(__os.widen('\n'));
00347 #else
00348         return __os;
00349 #endif
00350     }
00351 
00352     std::ostream&
00353     Logger::endl(std::ostream& __os)
00354     {
00355 #ifndef OROSEM_PRINTF_LOGGING
00356         return flush(__os.put(__os.widen('\n')));
00357 #else
00358         return __os;
00359 #endif
00360     }
00361 
00362     std::ostream&
00363     Logger::flush(std::ostream& __os)
00364     {
00365 #ifndef OROSEM_PRINTF_LOGGING
00366         return __os.flush();
00367 #else
00368         return __os;
00369 #endif
00370     }
00371 
00372 
00373     Logger::In::In(const std::string& modname)
00374         : oldmod( Logger::log().getLogModule() )
00375     {
00376         Logger::log().in(modname);
00377     }
00378 
00379     Logger::In::~In()
00380     {
00381         Logger::log().out(oldmod);
00382     }
00383 
00384     Logger& Logger::in(const std::string& modname)
00385     {
00386         os::MutexLock lock( d->inpguard );
00387         d->moduleptr = modname.c_str();
00388         return *this;
00389     }
00390 
00391     Logger& Logger::out(const std::string& oldmod)
00392     {
00393         os::MutexLock lock( d->inpguard );
00394         d->moduleptr = oldmod.c_str();
00395         return *this;
00396     }
00397 
00398     std::string Logger::getLogModule() const {
00399         os::MutexLock lock( d->inpguard );
00400         std::string ret = d->moduleptr.c_str();
00401         return ret;
00402     }
00403 
00404 
00405 #define ORO_xstr(s) ORO_str(s)
00406 #define ORO_str(s) #s
00407 
00408     void Logger::startup() {
00409         if (d->started)
00410             return;
00411 #ifndef OROBLD_DISABLE_LOGGING
00412         std::string xtramsg = "No ORO_LOGLEVEL environment variable set.";
00413         *this << Logger::Info; // default log to Info
00414 
00415         int wantedlevel=4; // default log level is 4.
00416 
00417         if ( getenv( "ORO_LOGLEVEL" ) != 0 ) {
00418             std::stringstream conv;
00419             conv.str( std::string( getenv( "ORO_LOGLEVEL" ) ) );
00420             conv >> wantedlevel;
00421             if ( conv.fail() ) {
00422                 xtramsg = std::string( "Failed to extract loglevel from environment variable ORO_LOGLEVEL.")
00423                     + " It contained the string '"+conv.str()+"', while it should contain an integer value.";
00424                 *this<<Logger::Error;
00425             }
00426             else {
00427                 d->outloglevel = d->intToLogLevel(wantedlevel);
00428                 xtramsg = "Successfully extracted environment variable ORO_LOGLEVEL";
00429             }
00430         }
00431 
00432         // Completely disable logging on negative values.
00433         if ( wantedlevel < 0 )
00434             return;
00435         d->started = true;
00436 
00437         d->timestamp = TimeService::Instance()->getTicks();
00438         *this<<xtramsg<<Logger::nl;
00439         *this<< " OROCOS version '" ORO_xstr(RTT_VERSION) "'";
00440 #ifdef __GNUC__
00441         *this << " compiled with GCC " ORO_xstr(__GNUC__) "." ORO_xstr(__GNUC_MINOR__) "." ORO_xstr(__GNUC_PATCHLEVEL__) ".";
00442 #endif
00443 #ifdef OROPKG_OS_LXRT
00444         *this<<" Running in LXRT/RTAI."<< Logger::nl;
00445 #endif
00446 #ifdef OROPKG_OS_GNULINUX
00447         *this<<" Running in GNU/Linux."<< Logger::nl;
00448 #endif
00449 #ifdef OROPKG_OS_XENOMAI
00450         *this<<" Running in Xenomai."<< Logger::nl;
00451 #endif
00452         *this<<"Orocos Logging Activated at level : " << d->showLevel( d->outloglevel ) << " ( "<<int(d->outloglevel)<<" ) "<< Logger::nl;
00453         *this<<"Reference System Time is : " << d->timestamp << " ticks ( "<< Seconds(TimeService::ticks2nsecs(d->timestamp))/NSECS_IN_SECS <<" seconds )." << Logger::nl;
00454         *this<<"Logging is relative to this time." <<Logger::endl;
00455 #endif
00456     }
00457 
00458     void Logger::shutdown() {
00459         if (!d->started)
00460             return;
00461         *this<<Logger::Info<<"Orocos Logging Deactivated." << Logger::endl;
00462         this->logflush();
00463         d->started = false;
00464     }
00465 
00466     std::string Logger::getLogLine() {
00467 #ifdef OROSEM_REMOTE_LOGGING
00468         if (!d->started)
00469             return "";
00470         std::string line;
00471         {
00472             os::MutexLock lock( d->inpguard );
00473             getline( d->remotestream, line );
00474             if ( !d->remotestream )
00475                 d->remotestream.clear();
00476         }
00477         if ( !line.empty() )
00478             --d->messagecnt;
00479         return line;
00480 #else
00481         return "";
00482 #endif
00483     }
00484 
00485     void Logger::setStdStream( std::ostream& stdos ) {
00486 #ifndef OROSEM_PRINTF_LOGGING
00487         d->stdoutput = &stdos;
00488 #endif
00489     }
00490 
00491     Logger& Logger::operator<<( const char* t ) {
00492         if ( !d->maylog() )
00493             return *this;
00494 
00495         os::MutexLock lock( d->inpguard );
00496         if ( d->maylogStdOut() )
00497             d->logline << t;
00498 
00499 #if defined(OROSEM_FILE_LOGGING) || defined(OROSEM_REMOTE_LOGGING)
00500         // log Info or better to log file, even if not started.
00501         if ( d->maylogFile() )
00502             d->fileline << t;
00503 #endif
00504         return *this;
00505     }
00506 
00507     Logger& Logger::operator<<( const std::string& t ) {
00508         return this->operator<<( t.c_str() );
00509     }
00510 
00511     Logger& Logger::operator<<(LogLevel ll) {
00512         if ( !d->maylog() )
00513             return *this;
00514         d->inloglevel = ll;
00515         return *this;
00516     }
00517 
00518     Logger& Logger::operator<<(std::ostream& (*pf)(std::ostream&))
00519     {
00520         if ( !d->maylog() )
00521             return *this;
00522         if ( pf == Logger::endl )
00523             this->logendl();
00524         else if ( pf == Logger::nl )
00525             this->lognl();
00526         else if ( pf == Logger::flush )
00527             this->logflush();
00528         else {
00529             os::MutexLock lock( d->inpguard );
00530             if ( d->maylogStdOut() )
00531                 d->logline << pf; // normal std operator in stream.
00532 #if defined(OROSEM_FILE_LOGGING) || defined(OROSEM_REMOTE_LOGGING)
00533             if ( d->maylogFile() )
00534                 d->fileline << pf;
00535 #endif
00536         }
00537         return *this;
00538     }
00539 
00540     void Logger::logflush() {
00541         if (!d->maylog())
00542             return;
00543         {
00544             // just flush all buffers, do not produce a new logline
00545             os::MutexLock lock( d->inpguard );
00546             if ( d->maylogStdOut() ) {
00547 #ifndef OROSEM_PRINTF_LOGGING
00548                 d->stdoutput->flush();
00549 #endif
00550 #if defined(OROSEM_REMOTE_LOGGING)
00551                 d->remotestream.flush();
00552 #endif
00553             }
00554 #if defined(OROSEM_FILE_LOGGING)
00555             if ( d->maylogFile() ) {
00556 #ifndef OROSEM_PRINTF_LOGGING
00557                 d->logfile.flush();
00558 #endif
00559             }
00560 #endif
00561         }
00562      }
00563 
00564     void Logger::lognl() {
00565         if (!d->maylog())
00566             return;
00567         d->logit( Logger::nl );
00568      }
00569 
00570     void Logger::logendl() {
00571         if (!d->maylog())
00572             return;
00573         d->logit( Logger::endl );
00574      }
00575 
00576     void Logger::setLogLevel( LogLevel ll ) {
00577         d->outloglevel = ll;
00578     }
00579 
00580     Logger::LogLevel Logger::getLogLevel() const {
00581         return d->outloglevel ;
00582     }
00583 
00584 
00585 #else // OROBLD_DISABLE_LOGGING
00586 
00587     Logger::Logger(std::ostream& )
00588         : d(0)
00589     {
00590     }
00591 
00592     Logger::~Logger()
00593     {
00594     }
00595 
00596 #endif
00597 }