Orocos Real-Time Toolkit
2.5.0
|
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 }