1 /*---------------------------------------------------------------------------*\
5 * Copyright (C) 2000-2003 by the OpenSG Forum *
9 * contact: dirk@opensg.org, gerrit.voss@vossg.org, jbehr@zgdv.de *
11 \*---------------------------------------------------------------------------*/
12 /*---------------------------------------------------------------------------*\
15 * This library is free software; you can redistribute it and/or modify it *
16 * under the terms of the GNU Library General Public License as published *
17 * by the Free Software Foundation, version 2. *
19 * This library is distributed in the hope that it will be useful, but *
20 * WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
22 * Library General Public License for more details. *
24 * You should have received a copy of the GNU Library General Public *
25 * License along with this library; if not, write to the Free Software *
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
28 \*---------------------------------------------------------------------------*/
29 /*---------------------------------------------------------------------------*\
37 \*---------------------------------------------------------------------------*/
39 //---------------------------------------------------------------------------
41 //---------------------------------------------------------------------------
46 #include "OSGConfig.h"
54 #include "OSGStringUtils.h"
55 #include "OSGBaseInitFunctions.h"
56 #include "OSGThread.h"
60 //---------------------------------------------------------------------------
62 //---------------------------------------------------------------------------
65 LogBuf::LogBuf(UInt32 bufferSize
) :
76 Char8
*buffer
= new Char8
[bufferSize
];
78 setp(buffer
, buffer
+ bufferSize
- 1);
93 /*! \brief clear the chunk bag
96 void LogBuf::clearChunkBag(void)
98 std::list
<LogBuf::Chunk
*>::iterator cI
;
100 for(cI
= _chunkBag
.begin(); cI
!= _chunkBag
.end(); ++cI
)
107 void LogBuf::setCallback ( LogBuf::Callback cb
, void *clientData
,
110 std::list
<LogBuf::Chunk
*>::iterator cI
;
111 LogBuf::Chunk
*chunk
;
117 for(cI
= _chunkBag
.begin(); cI
!= _chunkBag
.end(); cI
++)
120 cb(chunk
->data
,chunk
->size
,clientData
);
125 _clientData
= clientData
;
130 void LogBuf::removeCallback(void)
136 void LogBuf::write(const Char8
*buffer
, std::streamsize size
)
146 chunk
->data
= new Char8
[size
];
148 memcpy(chunk
->data
, buffer
, size
);
150 _chunkBag
.push_back(chunk
);
153 if ((cb
= _callback
))
154 cb(buffer
,size
, _clientData
);
157 int LogBuf::overflow(Int32 c
)
164 // Put character into write buffer
169 // Flush write buffer
170 std::streamsize size
= pptr() - pbase();
174 write(pbase(), size
);
182 int LogBuf::sync(void)
187 // Flush write buffer
188 std::streamsize size
= pptr() - pbase();
192 write(pbase(), size
);
199 std::streamsize
LogBuf::xsputn(const Char8
*buffer
, std::streamsize size
)
206 std::streamsize s
= epptr() - pptr();
210 // Put it into the write buffer
211 memcpy(pptr(), buffer
, size
);
216 // Flush write buffer
217 s
= pptr() - pbase();
233 //---------------------------------------------------------------------------
235 //---------------------------------------------------------------------------
237 /*! \brief holds the nil buffer
240 Log::nilbuf
*Log::_nilbufP
= NULL
;
241 std::ostream
*Log::_nilstreamP
= NULL
;
243 const Char8
*Log::_levelName
[] =
245 "LOG", "FATAL", "WARNING", "NOTICE", "DEBUG_GV", "INFO", "DEBUG", 0
248 const Char8
*Log::_levelColor
[] =
252 "\x1b[33m", // WARNING
260 Char8
*Log::_buffer
= NULL
;
261 int Log::_bufferSize
= 0;
263 LockRefPtr
Log::_pLogLock
= NULL
;
264 InitFuncWrapper
Log::_lockInit(&Log::initLock
);
266 /*! \brief colorHeader which takes the log level for level color
268 bool Log::colorHeader(LogLevel level
, const char *sep
)
270 #if defined (OSG_WIN_TYPES) && !defined(OSG_NO_WINDOWD_H_INCLUDE)
275 WORD oldColAttrs
, colAttrs
;
276 CONSOLE_SCREEN_BUFFER_INFO csbiInfo
;
277 HANDLE hOutput
= INVALID_HANDLE_VALUE
;
282 hOutput
= GetStdHandle(STD_ERROR_HANDLE
);
285 hOutput
= GetStdHandle(STD_OUTPUT_HANDLE
);
291 if(hOutput
== INVALID_HANDLE_VALUE
||
292 !GetConsoleScreenBufferInfo(hOutput
, &csbiInfo
) )
298 oldColAttrs
= csbiInfo
.wAttributes
;
303 colAttrs
= FOREGROUND_RED
| FOREGROUND_INTENSITY
;
306 colAttrs
= FOREGROUND_RED
| FOREGROUND_GREEN
;
309 colAttrs
= oldColAttrs
;
313 if(_levelName
[level
] != NULL
)
314 str
= _levelName
[level
];
319 colStr
= (LPSTR
)str
.c_str();
321 if(!SetConsoleTextAttribute(hOutput
, colAttrs
) ||
322 !WriteFile(hOutput
, colStr
, lstrlen(colStr
), &cWritten
, NULL
) ||
323 !SetConsoleTextAttribute(hOutput
, oldColAttrs
) )
335 Log::Log(LogType logType
, LogLevel logLevel
) :
336 std::ostream (_nilbufP
== NULL
?
337 _nilbufP
= new Log::nilbuf() : _nilbufP
),
339 _logLevel (logLevel
),
343 _moduleHandling(LOG_MODULE_ALL
)
345 if(_nilstreamP
== NULL
)
346 _nilstreamP
= new std::ostream(_nilbufP
);
348 for(UInt32 i
= 0; i
< sizeof(_streamVec
)/sizeof(LogOStream
*); i
++)
350 #ifdef OSG_HAS_NILBUF
351 _streamVec
[i
] = new LogOStream(_nilbufP
);
353 _streamVec
[i
] = new LogOStream(_nilStreamP
->rdbuf());
357 setHeaderElem(LOG_TYPE_HEADER
);
359 _refTime
= getSystemTime();
361 setLogLevel(logLevel
);
364 /*! \brief Constructor which takes a log file name
367 Log::Log(const Char8
*fileName
, LogLevel logLevel
) :
368 std::ostream (_nilbufP
== NULL
?
369 _nilbufP
= new Log::nilbuf() : _nilbufP
),
370 _logType (LOG_FILE
),
371 _logLevel (logLevel
),
375 _moduleHandling(LOG_MODULE_ALL
)
377 if(_nilstreamP
== NULL
)
378 _nilstreamP
= new std::ostream(_nilbufP
);
380 for(UInt32 i
= 0; i
< sizeof(_streamVec
)/sizeof(LogOStream
*); i
++)
382 #ifdef OSG_HAS_NILBUF
383 _streamVec
[i
] = new LogOStream(_nilbufP
);
385 _streamVec
[i
] = new LogOStream(_nilStreamP
->rdbuf());
389 _refTime
= getSystemTime();
391 setHeaderElem(LOG_TYPE_HEADER
);
392 setLogFile (fileName
);
393 setLogLevel (logLevel
);
398 setLogFile(NULL
, true);
406 for(UInt32 i
= 0; i
< sizeof(_streamVec
)/sizeof(LogOStream
*); i
++)
408 delete _streamVec
[i
];
410 _streamVec
[i
] = NULL
;
415 bool Log::initLock(void)
417 _pLogLock
= Lock::get("OSG::Log::_pLogLock", true);
419 addPreMPExitFunction(&Log::finalizeLock
);
424 bool Log::finalizeLock(void)
431 /*------------------------------ access -----------------------------------*/
433 /*! \brief set method for attribute _headerElem
436 void Log::setHeaderElem(UInt32 elemMask
, bool force
)
440 if((force
== false ) &&
441 (this == osgLogP
) &&
442 (env
= getenv(OSG_NAMESPACE_PREFIX
"_LOG_HEADER")) )
444 osgLog() << "Log::setHeaderElem: overriden by envvar OSG_LOG_HEADER '"
445 << env
<< "'." << endLog
;
447 elemMask
= atoi(env
);
450 _headerElem
= elemMask
;
453 /*! \brief add method for attribute _headerElem
456 void Log::addHeaderElem(LogHeaderElem elem
, bool force
)
458 setHeaderElem((_headerElem
| elem
), force
);
461 /*! \brief delete method for attribute _headerElem
464 void Log::delHeaderElem(LogHeaderElem elem
, bool force
)
466 setHeaderElem((_headerElem
& ~elem
),force
);
469 /*! \brief check for a single headerElem
472 bool Log::hasHeaderElem(LogHeaderElem elem
)
474 return (_headerElem
& elem
) != 0;
477 void Log::addModuleHandling(LogModuleHandling handling
,
478 bool OSG_CHECK_ARG(force
))
480 _moduleHandling
|= handling
;
483 void Log::delModuleHandling(LogModuleHandling handling
,
484 bool OSG_CHECK_ARG(force
))
486 _moduleHandling
&= ~handling
;
489 void Log::addModuleName(const Char8
*module
, bool isStatic
)
493 if(module
&& *module
)
495 _moduleList
.push_back(m
);
499 _moduleList
.back().name
= module
;
500 _moduleList
.back().isStatic
= true;
504 SizeT len
= strlen(module
);
506 _moduleList
.back().name
= new Char8
[len
+ 1];
508 strcpy(const_cast<Char8
*>(_moduleList
.back().name
), module
);
510 _moduleList
.back().isStatic
= false;
515 void Log::delModuleName(const Char8
*OSG_CHECK_ARG(module
))
519 bool Log::hasModule(const Char8
*module
)
521 bool retCode
= false;
522 std::list
<Module
>::iterator mI
;
524 if(module
&& *module
)
526 for( mI
= _moduleList
.begin();
527 retCode
|| (mI
!= _moduleList
.end());
530 retCode
= (mI
->isStatic
) ?
531 (module
== mI
->name
) : (!strcmp(module
,mI
->name
));
538 bool Log::checkModule(const Char8
*module
)
540 bool retCode
= false;
541 std::list
<Module
>::iterator mI
;
543 if(_moduleHandling
!= LOG_MODULE_NONE
)
545 if(_moduleHandling
== LOG_MODULE_ALL
)
551 if(module
&& *module
)
553 if(hasModule(module
))
555 if(_moduleHandling
& LOG_MODULE_KNOWN
)
560 if(_moduleHandling
& LOG_MODULE_UNKNOWN
)
566 if(_moduleHandling
& LOG_MODULE_UNDEFINED
)
576 LogType
Log::getLogType(void)
581 /*! \brief set method for attribute logType, checks OSG_LOG_TYPE env var
585 void Log::setLogType(LogType logType
, bool force
)
587 static const Char8
*typenames
[] =
598 static LogType types
[] =
608 static Int32 typeCount
= sizeof(types
) / sizeof(LogType
);
614 if((force
== false ) &&
615 (this == osgLogP
) &&
616 (et
= getenv(OSG_NAMESPACE_PREFIX
"_LOG_TYPE")) )
618 osgLog() << "Log::setLogType: overriden by envvar OSG_LOG_TYPE '"
619 << et
<< "'." << std::endl
;
621 if(sscanf(et
, "%d", <
) != 1)
623 for(i
= 0; typenames
[i
]; i
++)
625 if(!osgStringCaseCmp(et
, typenames
[i
]))
634 _logType
= LOG_STDERR
;
636 osgLog() << "Log::setLogType: couldn't interpret envvar, "
637 << "set to LOG_STDERR!"
653 _logType
= types
[lt
];
665 LogLevel
Log::getLogLevel(void)
671 /*! Set method for attribute logLevel, checks the env vars
672 OSG_LOG_LEVEL_STARTUP and OSG_LOG_LEVEL and uses their value, unless
675 \param[in] logLevel The level for log messages to be emitted.
676 \param[in] force If true, ignore env variables and set level to \a logLevel.
678 void Log::setLogLevel(LogLevel logLevel
, bool force
)
680 static const Char8
*levelnames
[] =
692 static LogLevel levels
[] =
703 static Int32 levelCount
= sizeof(levels
) / sizeof(LogLevel
);
709 // unless forced, envvars override the argument
710 if(!force
&& (this == osgLogP
))
712 // startup allows different log level
713 if(GlobalSystemState
== Startup
)
715 if((el
= getenv(OSG_NAMESPACE_PREFIX
"_LOG_LEVEL_STARTUP")) != 0)
717 // OSG_LOG_LEVEL_STARTUP has highest precedence
718 osgLog() << "OSGLog::setLogLevel: overridden by envvar "
719 << "OSG_LOG_LEVEL_STARTUP '" << el
<< "'."
722 else if((el
= getenv(OSG_NAMESPACE_PREFIX
"_LOG_LEVEL")) != 0)
724 // fallback to OSG_LOG_LEVEL
725 osgLog() << "OSGLog::setLogLevel: overridden by envvar "
726 << "OSG_LOG_LEVEL '" << el
<< "'."
732 if((el
= getenv(OSG_NAMESPACE_PREFIX
"_LOG_LEVEL")) != 0)
734 osgLog() << "OSGLog::setLogLevel: overridden by envvar "
735 << "OSG_LOG_LEVEL '" << el
<< "'."
740 // if an envvar was set, interpret its value
743 if(sscanf(el
, "%d", &ll
) != 1)
745 // interpret the value as name of a level
746 for(i
= 0; levelnames
[i
]; ++i
)
748 if(!osgStringCaseCmp(el
, levelnames
[i
]))
750 logLevel
= levels
[i
];
757 logLevel
= LOG_DEBUG
;
759 osgLog() << "OSGLog::setLogLevel: Could not interpret "
760 << "envvar, setting to LOG_DEBUG."
766 // interpret value as numerical level
771 else if(ll
>= levelCount
)
776 logLevel
= levels
[ll
];
781 // if force is true this is the unmodified argument, otherwise envvars
782 // might have changed the value of logLevel
783 _logLevel
= logLevel
;
787 /*! \brief method to set and activate the log file, checks OSG_LOG_FILE env
791 void Log::setLogFile(const Char8
*fileName
, bool force
)
795 #ifdef OSG_STREAM_HAS_ISOPEN
796 if(_fileStream
.is_open())
798 if(_fileStream
.rdbuf()->is_open())
804 if((force
== false ) &&
805 (this == osgLogP
) &&
806 (name
= getenv(OSG_NAMESPACE_PREFIX
"_LOG_FILE")))
808 osgLog() << "Log::setLogFile: overriden by envvar OSG_LOG_FILE '"
809 << name
<< "'." << std::endl
;
818 _fileStream
.open(name
, std::ios::out
);
820 #ifdef OSG_STREAM_HAS_ISOPEN
821 if(_fileStream
.is_open())
823 if(_fileStream
.rdbuf()->is_open())
832 /*! Write log header.
834 std::ostream
&Log::doHeader( LogLevel level
,
838 const Char8
*funcName
)
840 LogOStream
&sout
= *(_streamVec
[level
]);
841 const char *sep
= ( (_headerElem
& LOG_TAB_HEADER
) ? "\t" : ":" );
842 const char *color
= ( (_headerElem
& LOG_COLOR_HEADER
) ?
843 _levelColor
[level
] : 0 );
844 const char *resetColor
= "\x1b[0m";
848 if(_headerElem
& LOG_BEGIN_NEWLINE_HEADER
)
851 if(_headerElem
& LOG_TYPE_HEADER
)
854 if(_headerElem
& LOG_ANSICOLOR_HEADER
)
859 sout
<< _levelName
[level
] << sep
;
866 if(!color
|| !colorHeader(level
, sep
))
867 sout
<< _levelName
[level
] << sep
;
873 sout
<< _levelName
[level
] << sep
;
880 if(_headerElem
& LOG_ASPECT_HEADER
)
882 sout
<< "A" << Thread::getCurrentAspect() << sep
;
885 if(_headerElem
& LOG_TIMESTAMP_HEADER
)
886 sout
<< (getSystemTime() - _refTime
) << sep
;
888 if(module
&& *module
&& (_headerElem
& LOG_MODULE_HEADER
))
889 sout
<< module
<< sep
;
891 if(file
&& *file
&& (_headerElem
& LOG_FILE_HEADER
))
895 if(_headerElem
& LOG_LINE_HEADER
)
902 if(_headerElem
& LOG_LINE_HEADER
)
903 sout
<< " line:" << line
;
906 if(funcName
&& *funcName
&& (_headerElem
& LOG_FUNCNAME_HEADER
))
908 sout
<< funcName
<< sep
;
911 if(_headerElem
& LOG_END_NEWLINE_HEADER
)
920 /*! \brief print for C-interface helper method
923 void Log::doLog(const Char8
* format
, ...)
928 va_start( args
, format
);
930 #if defined(OSG_HAS_VSNPRINTF) && !defined(__sgi)
936 _buffer
= new Char8
[_bufferSize
];
939 // on windows it returns -1 if the output
940 // was truncated due to the buffer size limit.
941 // on irix this returns always buffer_size-1 ????
943 count
= vsnprintf(_buffer
, _bufferSize
, format
, args
);
945 while(count
>= _bufferSize
|| count
== -1)
947 _bufferSize
= osgMax(_bufferSize
* 2, count
+ 1);
952 _buffer
= new Char8
[_bufferSize
];
954 va_start(args
, format
);
956 count
= vsnprintf(_buffer
, _bufferSize
, format
, args
);
959 if(_bufferSize
< 8192)
966 _buffer
= new Char8
[_bufferSize
];
969 vsprintf(_buffer
, format
, args
);
973 // *this << std::flush;
974 // Work around VC71. Patch by Chad Austin.
975 std::ostream
& os
= *this;
983 /*! \brief reconnects the streams for the current settings
986 void Log::connect(void)
990 #ifndef OSG_STREAM_RDBUF_HAS_PARAM
994 this->bp
= std::cout
.rdbuf();
997 this->bp
= std::cerr
.rdbuf();
1000 this->bp
= _fileStream
.rdbuf();
1007 this->bp
= _nilStreamP
->rdbuf();
1014 this->rdbuf(std::cout
.rdbuf());
1017 this->rdbuf(std::cerr
.rdbuf());
1020 this->rdbuf(_fileStream
.rdbuf());
1023 this->rdbuf(&_logBuf
);
1027 this->rdbuf(_nilbufP
);
1032 for(i
= 0; i
< int(sizeof(_streamVec
)/sizeof(std::ostream
*)); ++i
)
1036 _streamVec
[i
]->setrdbuf(this->rdbuf());
1040 #ifdef OSG_HAS_NILBUF
1041 _streamVec
[i
]->setrdbuf(_nilbufP
);
1043 _streamVec
[i
]->setrdbuf(_nilStreamP
->rdbuf());
1049 void Log::terminate(void)
1051 #ifdef OSG_HAS_NILBUF
1052 delete Log::_nilbufP
;
1054 Log::_nilbufP
= NULL
;
1056 if(Log::_nilstreamP
!= NULL
)
1058 Log::_nilstreamP
->close();
1060 delete Log::_nilstreamP
;
1062 Log::_nilstreamP
= NULL
;
1070 delete [] Log::_buffer
;
1072 Log::_bufferSize
= 0;
1073 Log::_buffer
= NULL
;
1076 /** \var LogType Log::_logType;
1077 * \brief holds the log type
1080 /** \var LogLevel Log::_logLevel;
1081 * \brief holds the log level
1084 /** \var fstream Log::_fileStream;
1085 * \brief file stream
1088 /** \var LogOStream *Log::_streamVec[6];
1089 * \brief stream vector
1095 OSG_BASE_DLLMAPPING LogP osgLogP
= NULL
;
1100 void OSG::doInitLog(void)
1102 // Make sure no one reanimates the Log from the dead
1103 if(GlobalSystemState
>= Shutdown
)
1106 #ifdef OSG_HAS_NILBUF
1107 if(Log::_nilbufP
== NULL
)
1108 Log::_nilbufP
= new Log::nilbuf();
1110 if(Log::_nilstreamP
== NULL
)
1111 Log::_nilstreamP
= new std::fstream("/dev/null", std::ios::out
);
1116 osgLogP
= new Log();
1118 osgLogP
->setLogLevel (OSG_DEFAULT_LOG_LEVEL
);
1119 osgLogP
->setLogFile (NULL
);
1120 osgLogP
->setLogType (OSG_DEFAULT_LOG_TYPE
);
1121 osgLogP
->setHeaderElem(OSG_DEFAULT_LOG_HEADER_ELEM
);