summaryrefslogtreecommitdiff
path: root/src/mongo/util/log.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/util/log.h')
-rw-r--r--src/mongo/util/log.h581
1 files changed, 581 insertions, 0 deletions
diff --git a/src/mongo/util/log.h b/src/mongo/util/log.h
new file mode 100644
index 00000000000..a393d4d29a5
--- /dev/null
+++ b/src/mongo/util/log.h
@@ -0,0 +1,581 @@
+// @file log.h
+
+/* Copyright 2009 10gen Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string.h>
+#include <errno.h>
+#include "../bson/util/builder.h"
+
+#ifndef _WIN32
+#include <syslog.h>
+#endif
+
+namespace mongo {
+
+ enum LogLevel { LL_DEBUG , LL_INFO , LL_NOTICE , LL_WARNING , LL_ERROR , LL_SEVERE };
+
+ inline const char * logLevelToString( LogLevel l ) {
+ switch ( l ) {
+ case LL_DEBUG:
+ case LL_INFO:
+ case LL_NOTICE:
+ return "";
+ case LL_WARNING:
+ return "warning" ;
+ case LL_ERROR:
+ return "ERROR";
+ case LL_SEVERE:
+ return "SEVERE";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+#ifndef _WIN32
+ inline const int logLevelToSysLogLevel( LogLevel l) {
+ switch ( l ) {
+ case LL_DEBUG:
+ return LOG_DEBUG;
+ case LL_INFO:
+ return LOG_INFO;
+ case LL_NOTICE:
+ return LOG_NOTICE;
+ case LL_WARNING:
+ return LOG_WARNING;
+ case LL_ERROR:
+ return LOG_ERR;
+ case LL_SEVERE:
+ return LOG_CRIT;
+ default:
+ return LL_INFO;
+ }
+ }
+#endif
+
+ class LabeledLevel {
+ public:
+
+ LabeledLevel( int level ) : _level( level ) {}
+ LabeledLevel( const char* label, int level ) : _label( label ), _level( level ) {}
+ LabeledLevel( const string& label, int level ) : _label( label ), _level( level ) {}
+
+ LabeledLevel operator+( int i ) const {
+ return LabeledLevel( _label, _level + i );
+ }
+
+ LabeledLevel operator+( const char* label ) const {
+ if( _label == "" )
+ return LabeledLevel( label, _level );
+ return LabeledLevel( _label + string("::") + label, _level );
+ }
+
+ LabeledLevel operator+( string& label ) const {
+ return LabeledLevel( _label + string("::") + label, _level );
+ }
+
+ LabeledLevel operator-( int i ) const {
+ return LabeledLevel( _label, _level - i );
+ }
+
+ const string& getLabel() const { return _label; }
+ int getLevel() const { return _level; }
+
+ private:
+ string _label;
+ int _level;
+ };
+
+ class LazyString {
+ public:
+ virtual ~LazyString() {}
+ virtual string val() const = 0;
+ };
+
+ // Utility class for stringifying object only when val() called.
+ template< class T >
+ class LazyStringImpl : public LazyString {
+ public:
+ LazyStringImpl( const T &t ) : t_( t ) {}
+ virtual string val() const { return t_.toString(); }
+ private:
+ const T& t_;
+ };
+
+ class Tee {
+ public:
+ virtual ~Tee() {}
+ virtual void write(LogLevel level , const string& str) = 0;
+ };
+
+ class Nullstream {
+ public:
+ virtual Nullstream& operator<< (Tee* tee) {
+ return *this;
+ }
+ virtual ~Nullstream() {}
+ virtual Nullstream& operator<<(const char *) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(const string& ) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(const StringData& ) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(char *) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(char) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(int) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(ExitCode) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(unsigned long) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(long) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(unsigned) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(unsigned short) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(double) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(void *) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(const void *) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(long long) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(unsigned long long) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(bool) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(const LazyString&) {
+ return *this;
+ }
+ template< class T >
+ Nullstream& operator<<(T *t) {
+ return operator<<( static_cast<void*>( t ) );
+ }
+ template< class T >
+ Nullstream& operator<<(const T *t) {
+ return operator<<( static_cast<const void*>( t ) );
+ }
+ template< class T >
+ Nullstream& operator<<(const shared_ptr<T> p ) {
+ T * t = p.get();
+ if ( ! t )
+ *this << "null";
+ else
+ *this << *t;
+ return *this;
+ }
+ template< class T >
+ Nullstream& operator<<(const T &t) {
+ return operator<<( static_cast<const LazyString&>( LazyStringImpl< T >( t ) ) );
+ }
+
+ virtual Nullstream& operator<< (ostream& ( *endl )(ostream&)) {
+ return *this;
+ }
+ virtual Nullstream& operator<< (ios_base& (*hex)(ios_base&)) {
+ return *this;
+ }
+
+ virtual void flush(Tee *t = 0) {}
+ };
+ extern Nullstream nullstream;
+
+ class Logstream : public Nullstream {
+ static mongo::mutex mutex;
+ static int doneSetup;
+ stringstream ss;
+ int indent;
+ LogLevel logLevel;
+ static FILE* logfile;
+ static boost::scoped_ptr<ostream> stream;
+ static vector<Tee*> * globalTees;
+ static bool isSyslog;
+ public:
+ inline static void logLockless( const StringData& s );
+
+ static void setLogFile(FILE* f) {
+ scoped_lock lk(mutex);
+ logfile = f;
+ }
+#ifndef _WIN32
+ static void useSyslog(const char * name) {
+ cout << "using syslog ident: " << name << endl;
+
+ // openlog requires heap allocated non changing pointer
+ // this should only be called once per pragram execution
+
+ char * newName = (char *) malloc( strlen(name) + 1 );
+ strcpy( newName , name);
+ openlog( newName , LOG_ODELAY , LOG_USER );
+ isSyslog = true;
+ }
+#endif
+
+ static int magicNumber() {
+ return 1717;
+ }
+
+ static int getLogDesc() {
+ int fd = -1;
+ if (logfile != NULL)
+#if defined(_WIN32)
+ // the ISO C++ conformant name is _fileno
+ fd = _fileno( logfile );
+#else
+ fd = fileno( logfile );
+#endif
+ return fd;
+ }
+
+ inline void flush(Tee *t = 0);
+
+ inline Nullstream& setLogLevel(LogLevel l) {
+ logLevel = l;
+ return *this;
+ }
+
+ /** note these are virtual */
+ Logstream& operator<<(const char *x) { ss << x; return *this; }
+ Logstream& operator<<(const string& x) { ss << x; return *this; }
+ Logstream& operator<<(const StringData& x) { ss << x.data(); return *this; }
+ Logstream& operator<<(char *x) { ss << x; return *this; }
+ Logstream& operator<<(char x) { ss << x; return *this; }
+ Logstream& operator<<(int x) { ss << x; return *this; }
+ Logstream& operator<<(ExitCode x) { ss << x; return *this; }
+ Logstream& operator<<(long x) { ss << x; return *this; }
+ Logstream& operator<<(unsigned long x) { ss << x; return *this; }
+ Logstream& operator<<(unsigned x) { ss << x; return *this; }
+ Logstream& operator<<(unsigned short x){ ss << x; return *this; }
+ Logstream& operator<<(double x) { ss << x; return *this; }
+ Logstream& operator<<(void *x) { ss << x; return *this; }
+ Logstream& operator<<(const void *x) { ss << x; return *this; }
+ Logstream& operator<<(long long x) { ss << x; return *this; }
+ Logstream& operator<<(unsigned long long x) { ss << x; return *this; }
+ Logstream& operator<<(bool x) { ss << x; return *this; }
+
+ Logstream& operator<<(const LazyString& x) {
+ ss << x.val();
+ return *this;
+ }
+ Nullstream& operator<< (Tee* tee) {
+ ss << '\n';
+ flush(tee);
+ return *this;
+ }
+ Logstream& operator<< (ostream& ( *_endl )(ostream&)) {
+ ss << '\n';
+ flush(0);
+ return *this;
+ }
+ Logstream& operator<< (ios_base& (*_hex)(ios_base&)) {
+ ss << _hex;
+ return *this;
+ }
+
+ Logstream& prolog() {
+ return *this;
+ }
+
+ void addGlobalTee( Tee * t ) {
+ if ( ! globalTees )
+ globalTees = new vector<Tee*>();
+ globalTees->push_back( t );
+ }
+
+ void indentInc(){ indent++; }
+ void indentDec(){ indent--; }
+ int getIndent() const { return indent; }
+
+ private:
+ static thread_specific_ptr<Logstream> tsp;
+ Logstream() {
+ indent = 0;
+ _init();
+ }
+ void _init() {
+ ss.str("");
+ logLevel = LL_INFO;
+ }
+ public:
+ static Logstream& get() {
+ if ( StaticObserver::_destroyingStatics ) {
+ cout << "Logstream::get called in uninitialized state" << endl;
+ }
+ Logstream *p = tsp.get();
+ if( p == 0 )
+ tsp.reset( p = new Logstream() );
+ return *p;
+ }
+ };
+
+ extern int logLevel;
+ extern int tlogLevel;
+
+ inline Nullstream& out( int level = 0 ) {
+ if ( level > logLevel )
+ return nullstream;
+ return Logstream::get();
+ }
+
+ /* flush the log stream if the log level is
+ at the specified level or higher. */
+ inline void logflush(int level = 0) {
+ if( level > logLevel )
+ Logstream::get().flush(0);
+ }
+
+ /* without prolog */
+ inline Nullstream& _log( int level = 0 ) {
+ if ( level > logLevel )
+ return nullstream;
+ return Logstream::get();
+ }
+
+ /** logging which we may not want during unit tests (dbtests) runs.
+ set tlogLevel to -1 to suppress tlog() output in a test program. */
+ inline Nullstream& tlog( int level = 0 ) {
+ if ( level > tlogLevel || level > logLevel )
+ return nullstream;
+ return Logstream::get().prolog();
+ }
+
+ // log if debug build or if at a certain level
+ inline Nullstream& dlog( int level ) {
+ if ( level <= logLevel || DEBUG_BUILD )
+ return Logstream::get().prolog();
+ return nullstream;
+ }
+
+ inline Nullstream& log( int level ) {
+ if ( level > logLevel )
+ return nullstream;
+ return Logstream::get().prolog();
+ }
+
+#define MONGO_LOG(level) if ( MONGO_likely(logLevel < (level)) ) { } else log( level )
+#define LOG MONGO_LOG
+
+ inline Nullstream& log( LogLevel l ) {
+ return Logstream::get().prolog().setLogLevel( l );
+ }
+
+ inline Nullstream& log( const LabeledLevel& ll ) {
+ Nullstream& stream = log( ll.getLevel() );
+ if( ll.getLabel() != "" )
+ stream << "[" << ll.getLabel() << "] ";
+ return stream;
+ }
+
+ inline Nullstream& log() {
+ return Logstream::get().prolog();
+ }
+
+ inline Nullstream& error() {
+ return log( LL_ERROR );
+ }
+
+ inline Nullstream& warning() {
+ return log( LL_WARNING );
+ }
+
+ /* default impl returns "" -- mongod overrides */
+ extern const char * (*getcurns)();
+
+ inline Nullstream& problem( int level = 0 ) {
+ if ( level > logLevel )
+ return nullstream;
+ Logstream& l = Logstream::get().prolog();
+ l << ' ' << getcurns() << ' ';
+ return l;
+ }
+
+ /**
+ log to a file rather than stdout
+ defined in assert_util.cpp
+ */
+ void initLogging( const string& logpath , bool append );
+ void rotateLogs( int signal = 0 );
+
+ std::string toUtf8String(const std::wstring& wide);
+
+#if defined(_WIN32)
+ inline string errnoWithDescription(DWORD x = GetLastError()) {
+#else
+ inline string errnoWithDescription(int x = errno) {
+#endif
+ stringstream s;
+ s << "errno:" << x << ' ';
+
+#if defined(_WIN32)
+ LPTSTR errorText = NULL;
+ FormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM
+ |FORMAT_MESSAGE_ALLOCATE_BUFFER
+ |FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ x, 0,
+ (LPTSTR) &errorText, // output
+ 0, // minimum size for output buffer
+ NULL);
+ if( errorText ) {
+ string x = toUtf8String(errorText);
+ for( string::iterator i = x.begin(); i != x.end(); i++ ) {
+ if( *i == '\n' || *i == '\r' )
+ break;
+ s << *i;
+ }
+ LocalFree(errorText);
+ }
+ else
+ s << strerror(x);
+ /*
+ DWORD n = FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, x,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf, 0, NULL);
+ */
+#else
+ s << strerror(x);
+#endif
+ return s.str();
+ }
+
+ /** output the error # and error message with prefix.
+ handy for use as parm in uassert/massert.
+ */
+ string errnoWithPrefix( const char * prefix );
+
+ void Logstream::logLockless( const StringData& s ) {
+ if ( s.size() == 0 )
+ return;
+
+ if ( doneSetup == 1717 ) {
+#ifndef _WIN32
+ if ( isSyslog ) {
+ syslog( LOG_INFO , "%s" , s.data() );
+ } else
+#endif
+ if (fwrite(s.data(), s.size(), 1, logfile)) {
+ fflush(logfile);
+ }
+ else {
+ int x = errno;
+ cout << "Failed to write to logfile: " << errnoWithDescription(x) << endl;
+ }
+ }
+ else {
+ cout << s.data();
+ cout.flush();
+ }
+ }
+
+ void Logstream::flush(Tee *t) {
+ // this ensures things are sane
+ if ( doneSetup == 1717 ) {
+ string msg = ss.str();
+ string threadName = getThreadName();
+ const char * type = logLevelToString(logLevel);
+
+ int spaceNeeded = (int)(msg.size() + 64 + threadName.size());
+ int bufSize = 128;
+ while ( bufSize < spaceNeeded )
+ bufSize += 128;
+
+ BufBuilder b(bufSize);
+ time_t_to_String( time(0) , b.grow(20) );
+ if (!threadName.empty()) {
+ b.appendChar( '[' );
+ b.appendStr( threadName , false );
+ b.appendChar( ']' );
+ b.appendChar( ' ' );
+ }
+
+ for ( int i=0; i<indent; i++ )
+ b.appendChar( '\t' );
+
+ if ( type[0] ) {
+ b.appendStr( type , false );
+ b.appendStr( ": " , false );
+ }
+
+ b.appendStr( msg );
+
+ string out( b.buf() , b.len() - 1);
+
+ scoped_lock lk(mutex);
+
+ if( t ) t->write(logLevel,out);
+ if ( globalTees ) {
+ for ( unsigned i=0; i<globalTees->size(); i++ )
+ (*globalTees)[i]->write(logLevel,out);
+ }
+#ifndef _WIN32
+ if ( isSyslog ) {
+ syslog( logLevelToSysLogLevel(logLevel) , "%s" , out.data() );
+ } else
+#endif
+ if(fwrite(out.data(), out.size(), 1, logfile)) {
+ fflush(logfile);
+ }
+ else {
+ int x = errno;
+ cout << "Failed to write to logfile: " << errnoWithDescription(x) << ": " << out << endl;
+ }
+#ifdef POSIX_FADV_DONTNEED
+ // This only applies to pages that have already been flushed
+ RARELY posix_fadvise(fileno(logfile), 0, 0, POSIX_FADV_DONTNEED);
+#endif
+ }
+ _init();
+ }
+
+ struct LogIndentLevel {
+ LogIndentLevel(){
+ Logstream::get().indentInc();
+ }
+ ~LogIndentLevel(){
+ Logstream::get().indentDec();
+ }
+ };
+
+ extern Tee* const warnings; // Things put here go in serverStatus
+
+} // namespace mongo