Line data Source code
1 :
2 : /* Copyright (c) 2005-2013, Stefan Eilemann <eile@equalizergraphics.com>
3 : * 2011-2012, Daniel Nachbaur <danielnachbaur@gmail.com>
4 : *
5 : * This library is free software; you can redistribute it and/or modify it under
6 : * the terms of the GNU Lesser General Public License version 2.1 as published
7 : * by the Free Software Foundation.
8 : *
9 : * This library is distributed in the hope that it will be useful, but WITHOUT
10 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 : * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12 : * details.
13 : *
14 : * You should have received a copy of the GNU Lesser General Public License
15 : * along with this library; if not, write to the Free Software Foundation, Inc.,
16 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 : */
18 :
19 : #include <pthread.h>
20 :
21 : #include "log.h"
22 :
23 : #include "clock.h"
24 : #include "perThread.h"
25 : #include "scopedMutex.h"
26 : #include "thread.h"
27 :
28 : #include <cstdio>
29 : #include <cstdlib>
30 : #include <fstream>
31 :
32 : #ifdef _MSC_VER
33 : # include <process.h>
34 : # define atoll _atoi64
35 : # define snprintf _snprintf
36 : # define getpid _getpid
37 : #else
38 : # include <unistd.h>
39 : #endif
40 :
41 : namespace lunchbox
42 : {
43 : static const size_t threadNameLength = 8;
44 :
45 : static unsigned getLogTopics();
46 29 : static Clock _defaultClock;
47 : static Clock* _clock = &_defaultClock;
48 29 : static Lock _lock; // The write lock
49 :
50 : namespace detail
51 : {
52 : /** @internal The string buffer used for logging. */
53 : class Log : public std::streambuf
54 : {
55 : public:
56 3504 : explicit Log( std::ostream& stream )
57 : : _indent(0), _blocked(0), _noHeader(0),
58 3504 : _newLine(true), _stream(stream)
59 3504 : { _file[0] = 0; }
60 :
61 7001 : virtual ~Log() {}
62 :
63 0 : void indent() { ++_indent; }
64 0 : void exdent() { --_indent; }
65 :
66 0 : void disableFlush() { ++_blocked; assert( _blocked < 100 ); }
67 0 : void enableFlush()
68 : {
69 0 : assert( _blocked );// Too many enableFlush on log stream
70 0 : --_blocked;
71 0 : }
72 :
73 0 : void disableHeader() { ++_noHeader; } // use counted variable to allow
74 0 : void enableHeader() { --_noHeader; } // nested enable/disable calls
75 :
76 3526 : void setThreadName( const std::string& name )
77 : {
78 3526 : LBASSERT( !name.empty( ));
79 3526 : _thread = name.substr( 0, threadNameLength );
80 25085 : while( _thread.size() < threadNameLength )
81 18033 : _thread += std::string( " " );
82 3526 : }
83 :
84 306 : const std::string& getThreadName() const { return _thread; }
85 :
86 3506 : void setLogInfo( const char* f, const int line )
87 : {
88 3506 : LBASSERT( f );
89 3506 : std::string file( f );
90 3506 : const size_t length = file.length();
91 :
92 3506 : if( length > 29 )
93 3506 : file = file.substr( length - 29, length );
94 :
95 3506 : snprintf( _file, 35, "%29s:%-4d", file.c_str(), line );
96 3506 : }
97 :
98 : protected:
99 194492 : virtual int_type overflow( Log::int_type c ) override
100 : {
101 194492 : if( c == EOF )
102 0 : return EOF;
103 :
104 194492 : if( _newLine )
105 : {
106 3506 : if( !_noHeader )
107 : {
108 3506 : _stringStream << getpid() << "." << _thread << " " << _file
109 7012 : << " " << _clock->getTime64() << " ";
110 : }
111 :
112 3506 : for( int i=0; i<_indent; ++i )
113 0 : _stringStream << " ";
114 3506 : _newLine = false;
115 : }
116 :
117 194492 : _stringStream << (char)c;
118 194494 : return c;
119 : }
120 :
121 10453 : virtual int sync() override
122 : {
123 10453 : if( !_blocked )
124 : {
125 10465 : const std::string& string = _stringStream.str();
126 : {
127 10451 : ScopedMutex< lunchbox::Lock > mutex( _lock );
128 10475 : _stream.write( string.c_str(), string.length( ));
129 10475 : _stream.rdbuf()->pubsync();
130 : }
131 10466 : _stringStream.str( "" );
132 : }
133 10465 : _newLine = true;
134 10465 : return 0;
135 : }
136 :
137 : private:
138 : Log( const Log& );
139 : Log& operator = ( const Log& );
140 :
141 : /** Short thread name. */
142 : std::string _thread;
143 :
144 : /** The current file logging. */
145 : char _file[35];
146 :
147 : /** The current indentation level. */
148 : int _indent;
149 :
150 : /** Flush reference counter. */
151 : int _blocked;
152 :
153 : /** The header disable counter. */
154 : int _noHeader;
155 :
156 : /** The flag that a new line has started. */
157 : bool _newLine;
158 :
159 : /** The temporary buffer. */
160 : std::ostringstream _stringStream;
161 :
162 : /** The wrapped ostream. */
163 : std::ostream& _stream;
164 : };
165 : }
166 :
167 : namespace
168 : {
169 145 : class LogTable
170 : {
171 : public:
172 145 : LogTable( const LogLevel _level, const std::string& _name )
173 145 : : level( _level ), name( _name ) {}
174 :
175 : LogLevel level;
176 : std::string name;
177 : };
178 :
179 : #define LOG_TABLE_ENTRY( name ) LogTable( LOG_ ## name, std::string( #name ))
180 : #define LOG_TABLE_SIZE (5)
181 :
182 58 : static LogTable _logTable[ LOG_TABLE_SIZE ] =
183 : {
184 : LOG_TABLE_ENTRY( ERROR ),
185 : LOG_TABLE_ENTRY( WARN ),
186 : LOG_TABLE_ENTRY( INFO ),
187 : LOG_TABLE_ENTRY( VERB ),
188 : LOG_TABLE_ENTRY( ALL )
189 29 : };
190 : }
191 :
192 29 : int Log::level = Log::getLogLevel( getenv( "LB_LOG_LEVEL" ));
193 29 : unsigned Log::topics = getLogTopics();
194 :
195 29 : static PerThread< Log > _logInstance;
196 :
197 : #ifdef NDEBUG
198 : static std::ostream* _logStream = &std::cout;
199 : #else
200 : static std::ostream* _logStream = &std::cerr;
201 : #endif
202 : static std::ostream* _logFile = 0;
203 :
204 3504 : Log::Log()
205 3504 : : std::ostream( new detail::Log( getOutput( )))
206 7008 : , impl_( dynamic_cast< detail::Log* >( rdbuf( )))
207 3504 : {}
208 :
209 13997 : Log::~Log()
210 : {
211 3501 : impl_->pubsync();
212 3502 : delete impl_;
213 10500 : }
214 :
215 0 : void Log::indent()
216 : {
217 0 : impl_->indent();
218 0 : }
219 :
220 0 : void Log::exdent()
221 : {
222 0 : impl_->exdent();
223 0 : }
224 :
225 0 : void Log::disableFlush()
226 : {
227 0 : impl_->disableFlush();
228 0 : }
229 :
230 0 : void Log::enableFlush()
231 : {
232 0 : impl_->enableFlush();
233 0 : }
234 :
235 3446 : void Log::forceFlush()
236 : {
237 3446 : impl_->pubsync();
238 3462 : }
239 :
240 0 : void Log::disableHeader()
241 : {
242 0 : impl_->disableHeader();
243 0 : }
244 :
245 0 : void Log::enableHeader()
246 : {
247 0 : impl_->enableHeader();
248 0 : }
249 :
250 3506 : void Log::setLogInfo( const char* file, const int line )
251 : {
252 3506 : impl_->setLogInfo( file, line );
253 3506 : }
254 :
255 3526 : void Log::setThreadName( const std::string& name )
256 : {
257 3526 : impl_->setThreadName( name );
258 3526 : }
259 :
260 306 : const std::string& Log::getThreadName() const
261 : {
262 306 : return impl_->getThreadName();
263 : }
264 :
265 58 : int Log::getLogLevel( const char* text )
266 : {
267 58 : if( text )
268 : {
269 0 : const int num = atoi( text );
270 0 : if( num > 0 && num <= LOG_ALL )
271 0 : return num;
272 :
273 0 : for( uint32_t i = 0; i < LOG_TABLE_SIZE; ++i )
274 0 : if( _logTable[i].name == text )
275 0 : return _logTable[i].level;
276 : }
277 :
278 : #ifdef NDEBUG
279 : return LOG_WARN;
280 : #else
281 58 : return LOG_INFO;
282 : #endif
283 : }
284 :
285 5 : std::string& Log::getLogLevelString()
286 : {
287 15 : for( uint32_t i=0; i<LOG_TABLE_SIZE; ++i )
288 15 : if( _logTable[i].level == level )
289 5 : return _logTable[i].name;
290 :
291 0 : return _logTable[0].name;
292 : }
293 :
294 29 : unsigned getLogTopics()
295 : {
296 29 : Log::level = Log::getLogLevel( getenv( "LB_LOG_LEVEL" ));
297 29 : const char *env = getenv( "LB_LOG_TOPICS" );
298 :
299 29 : if( env )
300 0 : return atoll(env);
301 :
302 29 : if( Log::level == LOG_ALL )
303 0 : return 0xffffffffu;
304 :
305 29 : return 0;
306 : }
307 :
308 14253 : Log& Log::instance()
309 : {
310 14253 : Log* log = _logInstance.get();
311 14244 : if( !log )
312 : {
313 3504 : log = new Log();
314 3504 : _logInstance = log;
315 : }
316 :
317 14254 : return *log;
318 : }
319 :
320 3506 : Log& Log::instance( const char* file, const int line )
321 : {
322 3506 : Log& log = instance();
323 3506 : log.setLogInfo( file, line );
324 3506 : return log;
325 : }
326 :
327 3465 : void Log::exit()
328 : {
329 3465 : Log* log = _logInstance.get();
330 3466 : _logInstance = 0;
331 3467 : delete log;
332 3459 : }
333 :
334 5 : void Log::reset()
335 : {
336 5 : exit();
337 :
338 5 : delete _logFile;
339 5 : _logFile = 0;
340 :
341 : #ifdef NDEBUG
342 : _logStream = &std::cout;
343 : #else
344 5 : _logStream = &std::cerr;
345 : #endif
346 5 : }
347 :
348 0 : void Log::setOutput( std::ostream& stream )
349 : {
350 0 : _logStream = &stream;
351 0 : exit();
352 0 : }
353 :
354 0 : bool Log::setOutput( const std::string& file )
355 : {
356 0 : std::ostream* oldLog = _logStream;
357 0 : std::ofstream* newLog = new std::ofstream( file.c_str( ));
358 :
359 0 : if( newLog->is_open( ))
360 : {
361 0 : setOutput( *newLog );
362 0 : *oldLog << "Redirected log to " << file << std::endl;
363 :
364 0 : delete _logFile;
365 0 : _logFile = newLog;
366 0 : return true;
367 : }
368 :
369 0 : LBWARN << "Can't open log file " << file << ": " << sysError << std::endl;
370 0 : delete newLog;
371 0 : return false;
372 : }
373 :
374 0 : void Log::setClock( Clock* clock )
375 : {
376 0 : if( clock )
377 0 : _clock = clock;
378 : else
379 0 : _clock = &_defaultClock;
380 0 : }
381 :
382 0 : const Clock& Log::getClock()
383 : {
384 0 : return *_clock;
385 : }
386 :
387 3504 : std::ostream& Log::getOutput()
388 : {
389 3504 : return *_logStream;
390 : }
391 :
392 0 : std::ostream& indent( std::ostream& os )
393 : {
394 0 : Log* log = dynamic_cast<Log*>(&os);
395 0 : if( log )
396 0 : log->indent();
397 0 : return os;
398 : }
399 0 : std::ostream& exdent( std::ostream& os )
400 : {
401 0 : Log* log = dynamic_cast<Log*>(&os);
402 0 : if( log )
403 0 : log->exdent();
404 0 : return os;
405 : }
406 :
407 0 : std::ostream& disableFlush( std::ostream& os )
408 : {
409 0 : Log* log = dynamic_cast<Log*>(&os);
410 0 : if( log )
411 0 : log->disableFlush();
412 0 : return os;
413 : }
414 0 : std::ostream& enableFlush( std::ostream& os )
415 : {
416 0 : Log* log = dynamic_cast<Log*>(&os);
417 0 : if( log )
418 0 : log->enableFlush();
419 0 : return os;
420 : }
421 0 : std::ostream& forceFlush( std::ostream& os )
422 : {
423 0 : Log* log = dynamic_cast<Log*>(&os);
424 0 : if( log )
425 0 : log->forceFlush();
426 0 : return os;
427 : }
428 :
429 0 : std::ostream& disableHeader( std::ostream& os )
430 : {
431 0 : Log* log = dynamic_cast<Log*>(&os);
432 0 : if( log )
433 0 : log->disableHeader();
434 0 : return os;
435 : }
436 0 : std::ostream& enableHeader( std::ostream& os )
437 : {
438 0 : Log* log = dynamic_cast<Log*>(&os);
439 0 : if( log )
440 0 : log->enableHeader();
441 0 : return os;
442 : }
443 :
444 87 : }
|