/* $Id: LogStream.cxx 15564 2013-01-07 14:25:32Z sloot $ $URL: https://ilk.uvt.nl/svn/sources/libticcutils/trunk/src/LogStream.cxx $ Copyright (c) 1998 - 2013 ILK - Tilburg University CLiPS - University of Antwerp This file is part of ticcutils ticcutils is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. ticcutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . For questions and suggestions, see: http://ilk.uvt.nl/software.html or send mail to: timbl@uvt.nl */ #include #include #include #include #include #include "ticcutils/LogStream.h" #include #if defined __GNUC__ #define DARE_TO_OPTIMIZE #endif //#define LSDEBUG using std::ostream; using std::streambuf; using std::cerr; using std::endl; using std::bad_cast; using std::string; namespace TiCC { LogStream::LogStream( int ) : ostream( static_cast(0) ), buf( cerr ), single_threaded_mode(false){ } LogStream null_stream( 0 ); LogStream::LogStream() : ostream( &buf ), buf( cerr, NULL, StampBoth ), single_threaded_mode(false) { } LogStream::LogStream( const string& message, LogFlag stamp ) : ostream( &buf ), buf( cerr, message, stamp ), single_threaded_mode(false) { } LogStream::LogStream( ostream& as, const string& message, LogFlag stamp ) : ostream( &buf ), buf( as, message, stamp ), single_threaded_mode(false){ } LogStream::LogStream( const LogStream& ls, const string& message, LogFlag stamp ): ostream( &buf ), buf( ls.buf.AssocStream(), ls.buf.Message(), stamp ), single_threaded_mode( ls.single_threaded_mode ){ buf.Level( ls.buf.Level() ); buf.Threshold( ls.buf.Threshold() ); addmessage( message ); } LogStream::LogStream( const LogStream& ls, const string& message ): ostream( &buf ), buf( ls.buf.AssocStream(), ls.buf.Message(), ls.buf.StampFlag() ), single_threaded_mode( ls.single_threaded_mode ){ buf.Level( ls.buf.Level() ); buf.Threshold( ls.buf.Threshold() ); addmessage( message ); } LogStream::LogStream( const LogStream *ls ): ostream( &buf ), buf( ls->buf.AssocStream(), ls->buf.Message(), ls->buf.StampFlag() ), single_threaded_mode( ls->single_threaded_mode ){ buf.Level( ls->buf.Level() ); buf.Threshold( ls->buf.Threshold() ); } void LogStream::addmessage( const string& s ){ if ( !s.empty() ){ string tmp = buf.Message(); tmp += s; buf.Message( tmp ); } } void LogStream::addmessage( const int i ){ char m[32]; sprintf( m, "-%d", i ); addmessage( m ); } static bool static_init = false; bool LogStream::set_single_threaded_mode( ){ if ( !static_init ){ single_threaded_mode = true; return true; } else return false; } ostream& setlevel_sup( ostream& os, LogLevel l ){ try { LogStream& tmp = dynamic_cast(os); tmp.setlevel( l ); } catch ( bad_cast ){ } return os; } o_manip setlevel( LogLevel l ){ return o_manip( &setlevel_sup, l ); } ostream& setthreshold_sup( ostream& os, LogLevel l ){ try { LogStream& tmp = dynamic_cast(os); tmp.setthreshold( l ); } catch ( bad_cast ){ } return os; } o_manip setthreshold( LogLevel l ){ return o_manip( &setthreshold_sup, l ); } ostream& setstamp_sup( ostream& os, LogFlag f ){ try { LogStream& tmp = dynamic_cast(os); tmp.setstamp( f ); } catch ( bad_cast ){ } return os; } o_manip setstamp( LogFlag f ){ return o_manip( &setstamp_sup, f ); } ostream& setmess_sup( ostream& os, const string& m ){ try { LogStream& tmp = dynamic_cast(os); tmp.message( m ); } catch ( bad_cast ){ } return os; } o_manip setmessage( const string& m ){ return o_manip( &setmess_sup, m ); } ostream& addmess_sup( ostream& os, const string& m ){ try { LogStream& tmp = dynamic_cast(os); tmp.addmessage( m ); } catch ( bad_cast ){ } return os; } o_manip addmessage( const string& m ){ return o_manip( &addmess_sup, m ); } o_manip addmessage( const int i ){ static char m[32]; // assume we are within the mutex here sprintf( m, "-%d", i ); return o_manip( &addmess_sup, m ); } ostream& write_sup( ostream& os, const string& m ){ try { LogStream& tmp = dynamic_cast(os); tmp.write( m.data(), m.size() ); } catch ( bad_cast ){ } return os; } o_manip write_buf( const string& m ){ return o_manip( &write_sup, m ); } pthread_mutex_t global_logging_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t global_lock_mutex = PTHREAD_MUTEX_INITIALIZER; struct lock_s { pthread_t id; int cnt; time_t tim; }; #define MAX_LOCKS 500 lock_s locks[MAX_LOCKS]; bool LogStream::Problems(){ #ifdef LSDEBUG cerr << "test for problems" << endl; #endif bool result = false; time_t lTime; time(&lTime); pthread_mutex_lock( &global_lock_mutex ); for ( int i=0; i < MAX_LOCKS; i++ ){ if ( locks[i].id != 0 && lTime - locks[i].tim > 30 ){ result = true; cerr << "ALERT" << endl; cerr << "ALERT" << endl; cerr << "Thread " << locks[i].id << "is blocking our LogStreams since " << lTime - locks[i].tim << " seconds!" << endl; cerr << "ALERT" << endl; cerr << "ALERT" << endl; } } pthread_mutex_unlock( &global_lock_mutex ); return result; } inline int get_lock( pthread_t ID ){ time_t lTime; time(&lTime); pthread_mutex_lock( &global_lock_mutex ); int free_lock = -1; for ( int i=0; i < MAX_LOCKS; i++ ){ if ( pthread_equal( locks[i].id, ID ) ){ pthread_mutex_unlock( &global_lock_mutex ); return i; } else if ( free_lock < 0 && locks[i].id == 0 ){ free_lock = i; } } if ( free_lock < 0 ){ throw( "LogStreams FATAL error: get_lock() failed " ); } locks[free_lock].id = ID; locks[free_lock].cnt = 0; locks[free_lock].tim = lTime; pthread_mutex_unlock( &global_lock_mutex ); return free_lock; } inline bool init_mutex(){ if ( !static_init ){ for (int i=0; i < MAX_LOCKS; i++ ) { locks[i].id = 0; locks[i].cnt = 0; } static_init = true; #ifdef LSDEBUG cerr << "MUTEX system initialized!" << endl; #endif } #ifdef LSDEBUG cerr << "voor Lock door thread " << pthread_self() << endl; #endif int pos = get_lock( pthread_self() ); if ( locks[pos].cnt == 0 ){ pthread_mutex_lock( &global_logging_mutex ); #ifdef LSDEBUG cerr << "Thread " << pthread_self() << " locked [" << pos << "]" << endl; #endif } locks[pos].cnt++; #ifdef LSDEBUG if ( locks[pos].cnt > 1 ){ cerr << "Thread " << pthread_self() << " regained [" << pos << "] cnt = " << locks[pos].cnt << endl; } #endif return static_init; } inline void mutex_release(){ #ifdef LSDEBUG cerr << "voor UnLock door thread " << pthread_self() << endl; #endif int pos = get_lock( pthread_self() ); locks[pos].cnt--; if ( locks[pos].cnt < 0 ){ throw( "LogStreams FATAL error: mutex_release() failed" ); } #ifdef LSDEBUG if ( locks[pos].cnt > 0 ){ cerr << "Thread " << pthread_self() << " still owns [" << pos << "] cnt = "<< locks[pos].cnt << endl; } #endif if ( locks[pos].cnt == 0 ){ locks[pos].id = 0; #ifdef LSDEBUG cerr << "Thread " << pthread_self() << " unlocked [" << pos << "]" << endl; #endif pthread_mutex_unlock( &global_logging_mutex ); } } bool LogStream::IsBlocking(){ if ( !bad() ){ return getlevel() <= getthreshold(); } else return true; } bool IsActive( LogStream &ls ){ return !ls.IsBlocking(); } bool IsActive( LogStream *ls ){ return ls && !ls->IsBlocking(); } Log::Log( LogStream *os ){ if ( !os ){ throw( "LogStreams FATAL error: No Stream supplied! " ); } if ( os->single_threaded() || init_mutex() ){ my_level = os->getthreshold(); my_stream = os; os->setthreshold( LogSilent ); } } Log::Log( LogStream& os ){ if ( os.single_threaded() || init_mutex() ){ my_level = os.getthreshold(); my_stream = &os; os.setthreshold( LogSilent ); } } Log::~Log(){ my_stream->flush(); my_stream->setthreshold( my_level ); if ( !my_stream->single_threaded() ) mutex_release(); } LogStream& Log::operator *(){ #ifdef DARE_TO_OPTIMIZE if ( my_stream->getlevel() > my_stream->getthreshold() ) return *my_stream; else return null_stream; #else return *my_stream; #endif } Dbg::Dbg( LogStream *os ){ if ( !os ){ throw( "LogStreams FATAL error: No Stream supplied! " ); } if ( os->single_threaded() || init_mutex() ){ my_stream = os; my_level = os->getthreshold(); os->setthreshold( LogNormal ); } } Dbg::Dbg( LogStream& os ){ if ( os.single_threaded() || init_mutex() ){ my_stream = &os; my_level = os.getthreshold(); os.setthreshold( LogNormal ); } } Dbg::~Dbg(){ my_stream->flush(); my_stream->setthreshold( my_level ); if ( !my_stream->single_threaded() ) mutex_release(); } LogStream& Dbg::operator *() { #ifdef DARE_TO_OPTIMIZE if ( my_stream->getlevel() > my_stream->getthreshold() ) return *my_stream; else return null_stream; #else return *my_stream; #endif } xDbg::xDbg( LogStream *os ){ if ( !os ){ throw( "LogStreams FATAL error: No Stream supplied! " ); } if ( os->single_threaded() || init_mutex() ){ my_stream = os; my_level = os->getthreshold(); os->setthreshold( LogDebug ); } } xDbg::xDbg( LogStream& os ){ if ( os.single_threaded() || init_mutex() ){ my_stream = &os; my_level = os.getthreshold(); os.setthreshold( LogDebug ); } } xDbg::~xDbg(){ my_stream->flush(); my_stream->setthreshold( my_level ); if ( !my_stream->single_threaded() ) mutex_release(); } LogStream& xDbg::operator *(){ #ifdef DARE_TO_OPTIMIZE if ( my_stream->getlevel() > my_stream->getthreshold() ) return *my_stream; else return null_stream; #else return *my_stream; #endif } xxDbg::xxDbg( LogStream *os ){ if ( !os ){ throw( "LogStreams FATAL error: No Stream supplied! " ); } if ( os->single_threaded() || init_mutex() ){ my_stream = os; my_level = os->getthreshold(); os->setthreshold( LogHeavy ); } } xxDbg::xxDbg( LogStream& os ){ if ( os.single_threaded() || init_mutex() ){ my_stream = &os; my_level = os.getthreshold(); os.setthreshold( LogHeavy ); } } xxDbg::~xxDbg(){ my_stream->flush(); my_stream->setthreshold( my_level ); if ( !my_stream->single_threaded() ) mutex_release(); } LogStream& xxDbg::operator *(){ #ifdef DARE_TO_OPTIMIZE if ( my_stream->getlevel() > my_stream->getthreshold() ) return *my_stream; else return null_stream; #else return *my_stream; #endif } }