diff options
author | Andy Schwerin <schwerin@10gen.com> | 2013-07-01 14:35:03 -0400 |
---|---|---|
committer | Andy Schwerin <schwerin@10gen.com> | 2013-07-09 16:43:33 -0400 |
commit | ea22c3173541606519ddcb6da578b837a092b1c1 (patch) | |
tree | d3736e74ee67126f00466eec1686578ba8797749 /src/mongo | |
parent | 4bab9b0ee8dbffa00c4c86ea20195524a90f81f4 (diff) | |
download | mongo-ea22c3173541606519ddcb6da578b837a092b1c1.tar.gz |
SERVER-10084 New logging implementation.
This change-set:
* Introduces a new top-level directory, mongo/logger, containing most of the implementation of logging functionality formerly found in log.cpp/log.h.
* Cleans up existing, unusual uses of the logging system that were not trivially compatible with the new implementation.
* Replaces Logstream/Nulstream with a LogstreamBuilder object, whose destructor writes log messages. This new LogstreamBuilder is reentrant, unlike the old logging code, which was thread-safe but not reentrant. Additionally, std::endl is no longer required to terminate a log line. When a LogstreamBuilder goes out of scope, the log message gets committed.
* Separates the log system into several components: a global LogManager, several LogDomains, various kinds of Appenders (e.g., SyslogAppender) and Encoders (for formatting messages).
* Allows unit tests to capture and examine log output.
This patch does _not_ introduce support for hierarchical log domains, or for enabling and disabling specific log domains when the server is running in a multi-threaded mode. This is future work.
Diffstat (limited to 'src/mongo')
128 files changed, 2831 insertions, 1305 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript index b74f20f2d3e..adf013719fa 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -24,7 +24,8 @@ env.SConscript(['base/SConscript', 'logger/SConscript', 'platform/SConscript', 's/SConscript', - 'unittest/SConscript']) + 'unittest/SConscript', + 'util/concurrency/SConscript']) def add_exe( v ): return "${PROGPREFIX}%s${PROGSUFFIX}" % v @@ -46,7 +47,9 @@ env.StaticLibrary('foundation', ], LIBDEPS=['stacktrace', '$BUILD_DIR/mongo/base/base', + '$BUILD_DIR/mongo/logger/logger', '$BUILD_DIR/mongo/platform/platform', + '$BUILD_DIR/mongo/util/concurrency/thread_name', '$BUILD_DIR/third_party/shim_allocator', '$BUILD_DIR/third_party/shim_boost']) @@ -196,7 +199,6 @@ commonFiles = [ "pch.cpp", "util/util.cpp", "util/file_allocator.cpp", "util/trace.cpp", - "util/ramlog.cpp", "util/progress_meter.cpp", "util/concurrency/task.cpp", "util/concurrency/thread_pool.cpp", @@ -278,6 +280,7 @@ env.StaticLibrary('mongocommon', commonFiles, 'stacktrace', 'stringutils', 'fail_point', + 'util/concurrency/thread_name', '$BUILD_DIR/third_party/shim_pcrecpp', '$BUILD_DIR/third_party/murmurhash3/murmurhash3', '$BUILD_DIR/third_party/shim_boost'] + diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err index c16a63767b1..bbcdd6ae25e 100644 --- a/src/mongo/base/error_codes.err +++ b/src/mongo/base/error_codes.err @@ -41,5 +41,7 @@ error_code("FileRenameFailed", 37) error_code("FileNotOpen", 38) error_code("FileStreamFailed", 39) error_code("ConflictingUpdateOperators", 40) +error_code("FileAlreadyOpen", 41) +error_code("LogWriteFailed", 42) error_class("NetworkError", ["HostUnreachable", "HostNotFound"]) diff --git a/src/mongo/client/dbclient_rs.cpp b/src/mongo/client/dbclient_rs.cpp index 773f2d6fdf3..360101b70e0 100644 --- a/src/mongo/client/dbclient_rs.cpp +++ b/src/mongo/client/dbclient_rs.cpp @@ -1823,7 +1823,7 @@ namespace mongo { return _lazyState._lastClient->recv( m ); } catch( DBException& e ){ - log() << "could not receive data from " << _lazyState._lastClient << causedBy( e ) << endl; + log() << "could not receive data from " << _lazyState._lastClient->toString() << causedBy( e ) << endl; return false; } } diff --git a/src/mongo/client/dbclientcursor.h b/src/mongo/client/dbclientcursor.h index 20c28df0a0d..46ae49c46b1 100644 --- a/src/mongo/client/dbclientcursor.h +++ b/src/mongo/client/dbclientcursor.h @@ -78,8 +78,7 @@ namespace mongo { BSONObj o = next(); if( strcmp(o.firstElementFieldName(), "$err") == 0 ) { string s = "nextSafe(): " + o.toString(); - if( logLevel >= 5 ) - log() << s << endl; + LOG(5) << s; uasserted(13106, s); } return o; diff --git a/src/mongo/client/distlock.cpp b/src/mongo/client/distlock.cpp index 31f0e0c18d0..3e091a55352 100644 --- a/src/mongo/client/distlock.cpp +++ b/src/mongo/client/distlock.cpp @@ -24,6 +24,7 @@ #include "mongo/client/dbclientcursor.h" #include "mongo/s/type_locks.h" #include "mongo/s/type_lockpings.h" +#include "mongo/util/concurrency/thread_name.h" #include "mongo/util/timer.h" namespace mongo { @@ -173,8 +174,9 @@ namespace mongo { scoped_lock lk( _mutex ); int numOldLocks = _oldLockOIDs.size(); - if( numOldLocks > 0 ) + if( numOldLocks > 0 ) { LOG( DistributedLock::logLvl - 1 ) << "trying to delete " << _oldLockOIDs.size() << " old lock entries for process " << process << endl; + } bool removed = false; for( list<OID>::iterator i = _oldLockOIDs.begin(); i != _oldLockOIDs.end(); @@ -532,6 +534,20 @@ namespace mongo { return true; } + static void logErrMsgOrWarn(const StringData& messagePrefix, + const StringData& lockName, + const StringData& errMsg, + const StringData& altErrMsg) { + + if (errMsg.empty()) { + LOG(DistributedLock::logLvl - 1) << messagePrefix << " '" << lockName << "' " << + altErrMsg << std::endl; + } + else { + warning() << messagePrefix << " '" << lockName << "' " << causedBy(errMsg.toString()); + } + } + // Semantics of this method are basically that if the lock cannot be acquired, returns false, can be retried. // If the lock should not be tried again (some unexpected error) a LockException is thrown. // If we are only trying to re-enter a currently held lock, reenter should be true. @@ -704,8 +720,7 @@ namespace mongo { // TODO: Clean up all the extra code to exit this method, probably with a refactor if ( !errMsg.empty() || !err["n"].type() || err["n"].numberInt() < 1 ) { - ( errMsg.empty() ? LOG( logLvl - 1 ) : warning() ) << "Could not force lock '" << lockName << "' " - << ( !errMsg.empty() ? causedBy(errMsg) : string("(another force won)") ) << endl; + logErrMsgOrWarn("Could not force lock", lockName, errMsg, "(another force won"); *other = o; other->getOwned(); conn.done(); return false; } @@ -749,10 +764,7 @@ namespace mongo { // TODO: Clean up all the extra code to exit this method, probably with a refactor if ( ! errMsg.empty() || ! err["n"].type() || err["n"].numberInt() < 1 ) { - ( errMsg.empty() ? LOG( logLvl - 1 ) : warning() ) << "Could not re-enter lock '" << lockName << "' " - << ( !errMsg.empty() ? causedBy(errMsg) : string("(not sure lock is held)") ) - << " gle: " << err - << endl; + logErrMsgOrWarn("Could not re-enter lock", lockName, errMsg, "(not sure lock is held"); *other = o; other->getOwned(); conn.done(); return false; } @@ -822,8 +834,7 @@ namespace mongo { currLock = conn->findOne( LocksType::ConfigNS , BSON( LocksType::name(_name) ) ); if ( !errMsg.empty() || !err["n"].type() || err["n"].numberInt() < 1 ) { - ( errMsg.empty() ? LOG( logLvl - 1 ) : warning() ) << "could not acquire lock '" << lockName << "' " - << ( !errMsg.empty() ? causedBy( errMsg ) : string("(another update won)") ) << endl; + logErrMsgOrWarn("could not acquire lock", lockName, errMsg, "(another update won"); *other = currLock; other->getOwned(); gotLock = false; diff --git a/src/mongo/client/distlock_test.cpp b/src/mongo/client/distlock_test.cpp index 60c3e5fcc79..aa5ba21069a 100644 --- a/src/mongo/client/distlock_test.cpp +++ b/src/mongo/client/distlock_test.cpp @@ -29,6 +29,7 @@ #include "mongo/db/auth/privilege.h" #include "mongo/db/commands.h" #include "mongo/util/bson_util.h" +#include "mongo/util/concurrency/thread_name.h" #include "mongo/util/timer.h" // Modify some config options for the RNG, since they cause MSVC to fail diff --git a/src/mongo/client/parallel.cpp b/src/mongo/client/parallel.cpp index 8afbfdbbe95..a55eb9785a2 100644 --- a/src/mongo/client/parallel.cpp +++ b/src/mongo/client/parallel.cpp @@ -776,7 +776,7 @@ namespace mongo { ShardPtr primary; string prefix; - if (MONGO_unlikely(logLevel >= pc)) { + if (MONGO_unlikely(logger::globalLogDomain()->shouldLog(pc))) { if( _totalTries > 0 ) { prefix = str::stream() << "retrying (" << _totalTries << " tries)"; } @@ -798,7 +798,7 @@ namespace mongo { // Try to get either the chunk manager or the primary shard config->getChunkManagerOrPrimary( ns, manager, primary ); - if (MONGO_unlikely(logLevel >= pc)) { + if (MONGO_unlikely(logger::globalLogDomain()->shouldLog(pc))) { if (manager) { vinfo = str::stream() << "[" << manager->getns() << " @ " << manager->getVersion().toString() << "]"; @@ -826,7 +826,7 @@ namespace mongo { // Don't use version to get shards here todo = _qShards; - if (MONGO_unlikely(logLevel >= pc)) { + if (MONGO_unlikely(logger::globalLogDomain()->shouldLog(pc))) { vinfo = str::stream() << "[" << _qShards.size() << " shards specified]"; } } diff --git a/src/mongo/db/btree.cpp b/src/mongo/db/btree.cpp index 0a5e8d905d4..c74d71a6884 100644 --- a/src/mongo/db/btree.cpp +++ b/src/mongo/db/btree.cpp @@ -146,7 +146,7 @@ namespace mongo { this->assertValid(order, true); if ( bt_dmp ) { - _log() << thisLoc.toString() << ' '; + log() << thisLoc.toString() << ' '; ((BtreeBucket *) this)->dump(depth); } @@ -1761,18 +1761,19 @@ namespace mongo { template< class V > void BtreeBucket<V>::dump(unsigned depth) const { string indent = string(depth, ' '); - _log() << "BUCKET n:" << this->n; - _log() << " parent:" << hex << this->parent.getOfs() << dec; + LogstreamBuilder l = log(); + l << "BUCKET n:" << this->n; + l << " parent:" << hex << this->parent.getOfs() << dec; for ( int i = 0; i < this->n; i++ ) { - _log() << '\n' << indent; + l << '\n' << indent; KeyNode k = keyNode(i); string ks = k.key.toString(); - _log() << " " << hex << k.prevChildBucket.getOfs() << '\n'; - _log() << indent << " " << i << ' ' << ks.substr(0, 30) << " Loc:" << k.recordLoc.toString() << dec; + l << " " << hex << k.prevChildBucket.getOfs() << '\n'; + l << indent << " " << i << ' ' << ks.substr(0, 30) << " Loc:" << k.recordLoc.toString() << dec; if ( this->k(i).isUnused() ) - _log() << " UNUSED"; + l << " UNUSED"; } - _log() << "\n" << indent << " " << hex << this->nextChild.getOfs() << dec << endl; + l << "\n" << indent << " " << hex << this->nextChild.getOfs() << dec << endl; } /** todo: meaning of return code unclear clean up */ diff --git a/src/mongo/db/btreebuilder.cpp b/src/mongo/db/btreebuilder.cpp index 26c8e27c4b4..a97882b510a 100644 --- a/src/mongo/db/btreebuilder.cpp +++ b/src/mongo/db/btreebuilder.cpp @@ -151,8 +151,9 @@ namespace mongo { mayCommitProgressDurably(); } - if( levels > 1 ) + if( levels > 1 ) { LOG(2) << "btree levels: " << levels << endl; + } } /** when all addKeys are done, we then build the higher levels of the tree */ diff --git a/src/mongo/db/client.cpp b/src/mongo/db/client.cpp index 6e098264367..974d072fe93 100644 --- a/src/mongo/db/client.cpp +++ b/src/mongo/db/client.cpp @@ -48,6 +48,7 @@ #include "mongo/s/d_logic.h" #include "mongo/s/stale_exception.h" // for SendStaleConfigException #include "mongo/scripting/engine.h" +#include "mongo/util/concurrency/thread_name.h" #include "mongo/util/file_allocator.h" #include "mongo/util/mongoutils/checksum.h" #include "mongo/util/mongoutils/str.h" diff --git a/src/mongo/db/cmdline.cpp b/src/mongo/db/cmdline.cpp index f1c6d8661de..5b271439f15 100644 --- a/src/mongo/db/cmdline.cpp +++ b/src/mongo/db/cmdline.cpp @@ -296,12 +296,13 @@ namespace { } if (params.count("verbose")) { - logLevel = 1; + logger::globalLogDomain()->setMinimumLoggedSeverity(logger::LogSeverity::Debug(1)); } for (string s = "vv"; s.length() <= 12; s.append("v")) { if (params.count(s)) { - logLevel = s.length(); + logger::globalLogDomain()->setMinimumLoggedSeverity( + logger::LogSeverity::Debug(s.length())); } } diff --git a/src/mongo/db/commands/index_stats.cpp b/src/mongo/db/commands/index_stats.cpp index 1309e078ee9..6a5a6f0cf6a 100644 --- a/src/mongo/db/commands/index_stats.cpp +++ b/src/mongo/db/commands/index_stats.cpp @@ -501,7 +501,7 @@ namespace mongo { string ns = dbname + "." + cmdObj.firstElement().valuestrsafe(); const NamespaceDetails* nsd = nsdetails(ns); if (!cmdLine.quiet) { - tlog() << "CMD: indexStats " << ns << endl; + MONGO_TLOG(0) << "CMD: indexStats " << ns << endl; } if (!nsd) { errmsg = "ns not found"; diff --git a/src/mongo/db/commands/isself.cpp b/src/mongo/db/commands/isself.cpp index 055f966c3fe..b6f7e8d6d85 100644 --- a/src/mongo/db/commands/isself.cpp +++ b/src/mongo/db/commands/isself.cpp @@ -97,12 +97,15 @@ namespace mongo { freeifaddrs( addrs ); addrs = NULL; - if (logLevel >= 1) { - LOG(1) << "getMyAddrs():"; + if (logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1))) { + LogstreamBuilder builder(logger::globalLogDomain(), + getThreadName(), + logger::LogSeverity::Debug(1)); + builder << "getMyAddrs():"; for (vector<string>::const_iterator it=out.begin(), end=out.end(); it!=end; ++it) { - LOG(1) << " [" << *it << ']'; + builder << " [" << *it << ']'; } - LOG(1) << endl; + builder << endl; } return out; @@ -141,12 +144,15 @@ namespace mongo { freeaddrinfo(addrs); - if (logLevel >= 1) { - LOG(1) << "getallIPs(\"" << iporhost << "\"):"; + if (logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1))) { + LogstreamBuilder builder(logger::globalLogDomain(), + getThreadName(), + logger::LogSeverity::Debug(1)); + builder << "getallIPs(\"" << iporhost << "\"):"; for (vector<string>::const_iterator it=out.begin(), end=out.end(); it!=end; ++it) { - LOG(1) << " [" << *it << ']'; + builder << " [" << *it << ']'; } - LOG(1) << endl; + builder << endl; } return out; diff --git a/src/mongo/db/commands/mr.cpp b/src/mongo/db/commands/mr.cpp index be11a40b995..05cbdb1dd31 100644 --- a/src/mongo/db/commands/mr.cpp +++ b/src/mongo/db/commands/mr.cpp @@ -910,7 +910,7 @@ namespace mongo { { dbtempreleasecond tl; if ( ! tl.unlocked() ) - LOG( LL_WARNING ) << "map/reduce can't temp release" << endl; + warning() << "map/reduce can't temp release" << endl; // reduce and finalize last array finalReduce( all ); } diff --git a/src/mongo/db/commands/parameters.cpp b/src/mongo/db/commands/parameters.cpp index b23f6c49665..ddb6bb4a217 100644 --- a/src/mongo/db/commands/parameters.cpp +++ b/src/mongo/db/commands/parameters.cpp @@ -23,6 +23,7 @@ #include "mongo/db/commands.h" #include "mongo/db/cmdline.h" #include "mongo/db/server_parameters.h" +#include "mongo/util/mongoutils/str.h" namespace mongo { @@ -162,11 +163,41 @@ namespace mongo { } cmdSet; namespace { - ExportedServerParameter<int> LogLevelSetting( ServerParameterSet::getGlobal(), - "logLevel", - &logLevel, - true, - true ); + class LogLevelSetting : public ServerParameter { + public: + LogLevelSetting() : ServerParameter(ServerParameterSet::getGlobal(), "logLevel") {} + + virtual void append(BSONObjBuilder& b, const std::string& name) { + b << name << logger::globalLogDomain()->getMinimumLogSeverity().toInt(); + } + + virtual Status set(const BSONElement& newValueElement) { + typedef logger::LogSeverity LogSeverity; + int newValue; + if (!newValueElement.coerce(&newValue) || newValue < 0) + return Status(ErrorCodes::BadValue, mongoutils::str::stream() << + "Invalid value for logLevel: " << newValueElement); + LogSeverity newSeverity = (newValue > 0) ? LogSeverity::Debug(newValue) : + LogSeverity::Log(); + logger::globalLogDomain()->setMinimumLoggedSeverity(newSeverity); + return Status::OK(); + } + + virtual Status setFromString(const std::string& str) { + typedef logger::LogSeverity LogSeverity; + int newValue; + Status status = parseNumberFromString(str, &newValue); + if (!status.isOK()) + return status; + if (newValue < 0) + return Status(ErrorCodes::BadValue, mongoutils::str::stream() << + "Invalid value for logLevel: " << newValue); + LogSeverity newSeverity = (newValue > 0) ? LogSeverity::Debug(newValue) : + LogSeverity::Log(); + logger::globalLogDomain()->setMinimumLoggedSeverity(newSeverity); + return Status::OK(); + } + } logLevelSetting; ExportedServerParameter<bool> NoTableScanSetting( ServerParameterSet::getGlobal(), "notablescan", diff --git a/src/mongo/db/commands/pipeline_command.cpp b/src/mongo/db/commands/pipeline_command.cpp index d5905325bd2..56df3369d24 100644 --- a/src/mongo/db/commands/pipeline_command.cpp +++ b/src/mongo/db/commands/pipeline_command.cpp @@ -127,7 +127,7 @@ namespace mongo { BSONObj shardBson(shardBuilder.done()); DEV (log() << "\n---- shardBson\n" << - shardBson.jsonString(Strict, 1) << "\n----\n").flush(); + shardBson.jsonString(Strict, 1) << "\n----\n"); /* for debugging purposes, show what the pipeline now looks like */ DEV { @@ -135,7 +135,7 @@ namespace mongo { pPipeline->toBson(&pipelineBuilder); BSONObj pipelineBson(pipelineBuilder.done()); (log() << "\n---- pipelineBson\n" << - pipelineBson.jsonString(Strict, 1) << "\n----\n").flush(); + pipelineBson.jsonString(Strict, 1) << "\n----\n"); } /* on the shard servers, create the local pipeline */ diff --git a/src/mongo/db/commands/storage_details.cpp b/src/mongo/db/commands/storage_details.cpp index b86ec1dff5e..4ea51f0ebd9 100644 --- a/src/mongo/db/commands/storage_details.cpp +++ b/src/mongo/db/commands/storage_details.cpp @@ -777,7 +777,7 @@ namespace { const string ns = dbname + "." + cmdObj.firstElement().valuestrsafe(); const NamespaceDetails* nsd = nsdetails(ns); if (!cmdLine.quiet) { - tlog() << "CMD: storageDetails " << ns << ", analyze " << subCommandStr << endl; + MONGO_TLOG(0) << "CMD: storageDetails " << ns << ", analyze " << subCommandStr << endl; } if (!nsd) { errmsg = "ns not found"; diff --git a/src/mongo/db/commands/write_commands/batch_executor.cpp b/src/mongo/db/commands/write_commands/batch_executor.cpp index 23aca5cc1c6..89096429f91 100644 --- a/src/mongo/db/commands/write_commands/batch_executor.cpp +++ b/src/mongo/db/commands/write_commands/batch_executor.cpp @@ -157,9 +157,10 @@ namespace mongo { opDebug.recordStats(); // Log operation if running with at least "-v", or if exceeds slow threshold. - if (logLevel >= 1 + if (logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1)) || opDebug.executionTime > cmdLine.slowMS + childOp.getExpectedLatencyMs()) { - tlog() << opDebug.report(childOp) << endl; + + MONGO_TLOG(1) << opDebug.report(childOp) << endl; } // TODO Log operation if logLevel >= 3 and assertion thrown (as assembleResponse() diff --git a/src/mongo/db/database.cpp b/src/mongo/db/database.cpp index 1d151241830..2f69f217e24 100644 --- a/src/mongo/db/database.cpp +++ b/src/mongo/db/database.cpp @@ -194,7 +194,7 @@ namespace mongo { { if( n < (int) _files.size() && _files[n] ) { - dlog(2) << "openExistingFile " << n << " is already open" << endl; + MONGO_DLOG(2) << "openExistingFile " << n << " is already open" << endl; return true; } } @@ -474,7 +474,7 @@ namespace mongo { // that would make the DBs map very large. not clear what to do to handle though, // perhaps just log it, which is what we do here with the "> 40" : bool cant = !Lock::isWriteLocked(ns); - if( logLevel >= 1 || m.size() > 40 || cant || DEBUG_BUILD ) { + if( logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1)) || m.size() > 40 || cant || DEBUG_BUILD ) { log() << "opening db: " << (path==dbpath?"":path) << ' ' << dbname << endl; } massert(15927, "can't open database in a read lock. if db was just closed, consider retrying the query. might otherwise indicate an internal error", !cant); diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index 8ac02271f06..51a1c61fb4b 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -60,6 +60,7 @@ #include "mongo/scripting/engine.h" #include "mongo/util/background.h" #include "mongo/util/concurrency/task.h" +#include "mongo/util/concurrency/thread_name.h" #include "mongo/util/exception_filter_win32.h" #include "mongo/util/file_allocator.h" #include "mongo/util/net/message_server.h" @@ -446,12 +447,15 @@ namespace mongo { void run() { Client::initThread( name().c_str() ); - if( cmdLine.syncdelay == 0 ) + if( cmdLine.syncdelay == 0 ) { log() << "warning: --syncdelay 0 is not recommended and can have strange performance" << endl; - else if( cmdLine.syncdelay == 1 ) + } + else if( cmdLine.syncdelay == 1 ) { log() << "--syncdelay 1" << endl; - else if( cmdLine.syncdelay != 60 ) + } + else if( cmdLine.syncdelay != 60 ) { LOG(1) << "--syncdelay " << cmdLine.syncdelay << endl; + } int time_flushing = 0; while ( ! inShutdown() ) { _diaglog.flush(); @@ -474,7 +478,7 @@ namespace mongo { _flushed(time_flushing); - if( logLevel >= 1 || time_flushing >= 10000 ) { + if( logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1)) || time_flushing >= 10000 ) { log() << "flushing mmaps took " << time_flushing << "ms " << " for " << numFiles << " files" << endl; } } @@ -577,13 +581,15 @@ namespace mongo { Client::initThread("initandlisten"); - Logstream::get().addGlobalTee( new RamLog("global") ); + logger::globalLogDomain()->attachAppender( + logger::MessageLogDomain::AppenderAutoPtr( + new RamLogAppender(new RamLog("global")))); bool is32bit = sizeof(int*) == 4; { ProcessId pid = ProcessId::getCurrent(); - Nullstream& l = log(); + LogstreamBuilder l = log(); l << "MongoDB starting : pid=" << pid << " port=" << cmdLine.port << " dbpath=" << dbpath; if( replSettings.master ) l << " master=" << replSettings.master; if( replSettings.slave ) l << " slave=" << (int) replSettings.slave; diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp index 8ab4b4a0d14..3a2a9036f62 100644 --- a/src/mongo/db/dbcommands.cpp +++ b/src/mongo/db/dbcommands.cpp @@ -599,8 +599,9 @@ namespace mongo { bool run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool) { int was = _diaglog.setLevel( cmdObj.firstElement().numberInt() ); _diaglog.flush(); - if ( !cmdLine.quiet ) - tlog() << "CMD: diagLogging set to " << _diaglog.getLevel() << " from: " << was << endl; + if ( !cmdLine.quiet ) { + MONGO_TLOG(0) << "CMD: diagLogging set to " << _diaglog.getLevel() << " from: " << was << endl; + } result.append( "was" , was ); return true; } @@ -643,8 +644,9 @@ namespace mongo { virtual bool run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool) { string nsToDrop = dbname + '.' + cmdObj.firstElement().valuestr(); NamespaceDetails *d = nsdetails(nsToDrop); - if ( !cmdLine.quiet ) - tlog() << "CMD: drop " << nsToDrop << endl; + if ( !cmdLine.quiet ) { + MONGO_TLOG(0) << "CMD: drop " << nsToDrop << endl; + } if ( d == 0 ) { errmsg = "ns not found"; return false; @@ -817,8 +819,9 @@ namespace mongo { BSONElement e = jsobj.firstElement(); string toDeleteNs = dbname + '.' + e.valuestr(); NamespaceDetails *d = nsdetails(toDeleteNs); - if ( !cmdLine.quiet ) - tlog() << "CMD: dropIndexes " << toDeleteNs << endl; + if ( !cmdLine.quiet ) { + MONGO_TLOG(0) << "CMD: dropIndexes " << toDeleteNs << endl; + } if ( d ) { stopIndexBuilds(dbname, jsobj); @@ -883,7 +886,7 @@ namespace mongo { BSONElement e = jsobj.firstElement(); string toDeleteNs = dbname + '.' + e.valuestr(); NamespaceDetails *d = nsdetails(toDeleteNs); - tlog() << "CMD: reIndex " << toDeleteNs << endl; + MONGO_TLOG(0) << "CMD: reIndex " << toDeleteNs << endl; BackgroundOperation::assertNoBgOpInProgForNs(toDeleteNs.c_str()); if ( ! d ) { @@ -2165,8 +2168,9 @@ namespace mongo { return; } - if ( c->adminOnly() ) + if ( c->adminOnly() ) { LOG( 2 ) << "command: " << cmdObj << endl; + } if (c->maintenanceMode() && theReplSet) { mmSetter.reset(new MaintenanceModeSetter()); @@ -2241,8 +2245,7 @@ namespace mongo { bool _runCommands(const char *ns, BSONObj& _cmdobj, BufBuilder &b, BSONObjBuilder& anObjBuilder, bool fromRepl, int queryOptions) { string dbname = nsToDatabase( ns ); - if( logLevel >= 1 ) - log() << "run command " << ns << ' ' << _cmdobj << endl; + LOG(1) << "run command " << ns << ' ' << _cmdobj << endl; const char *p = strchr(ns, '.'); if ( !p ) return false; diff --git a/src/mongo/db/dbcommands_admin.cpp b/src/mongo/db/dbcommands_admin.cpp index d2d68df8011..48bf66cd477 100644 --- a/src/mongo/db/dbcommands_admin.cpp +++ b/src/mongo/db/dbcommands_admin.cpp @@ -162,8 +162,9 @@ namespace mongo { bool run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl ) { string ns = dbname + "." + cmdObj.firstElement().valuestrsafe(); NamespaceDetails * d = nsdetails( ns ); - if ( !cmdLine.quiet ) - tlog() << "CMD: validate " << ns << endl; + if ( !cmdLine.quiet ) { + MONGO_TLOG(0) << "CMD: validate " << ns << endl; + } if ( ! d ) { errmsg = "ns not found"; diff --git a/src/mongo/db/dbwebserver.cpp b/src/mongo/db/dbwebserver.cpp index bab3fa99f3f..54d4c323a42 100644 --- a/src/mongo/db/dbwebserver.cpp +++ b/src/mongo/db/dbwebserver.cpp @@ -26,6 +26,7 @@ #include <boost/date_time/posix_time/posix_time.hpp> #include <pcrecpp.h> +#include "mongo/base/init.h" #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/authorization_manager_global.h" #include "mongo/db/auth/authorization_session.h" @@ -334,7 +335,8 @@ namespace mongo { _log = RamLog::get( "global" ); if ( ! _log ) { _log = new RamLog("global"); - Logstream::get().addGlobalTee( _log ); + logger::globalLogDomain()->attachAppender(logger::MessageLogDomain::AppenderAutoPtr( + new RamLogAppender(_log))); } } @@ -344,7 +346,12 @@ namespace mongo { RamLog * _log; }; - LogPlugin * logPlugin = new LogPlugin(); + MONGO_INITIALIZER_GENERAL(WebStatusLogPlugin, ("ServerLogRedirection"), ("default"))( + InitializerContext*) { + + new LogPlugin; + return Status::OK(); + } // -- handler framework --- diff --git a/src/mongo/db/index.cpp b/src/mongo/db/index.cpp index 8d3a46c0f2b..9f6da5803be 100644 --- a/src/mongo/db/index.cpp +++ b/src/mongo/db/index.cpp @@ -243,7 +243,7 @@ namespace mongo { return false; } sourceCollection = nsdetails(sourceNS); - tlog() << "info: creating collection " << sourceNS << " on add index" << endl; + MONGO_TLOG(0) << "info: creating collection " << sourceNS << " on add index" << endl; verify( sourceCollection ); } diff --git a/src/mongo/db/index/btree_based_builder.cpp b/src/mongo/db/index/btree_based_builder.cpp index 42618de14e9..735cb292b9b 100644 --- a/src/mongo/db/index/btree_based_builder.cpp +++ b/src/mongo/db/index/btree_based_builder.cpp @@ -162,7 +162,7 @@ namespace mongo { phaseOne->addKeys(keys, loc, mayInterrupt); cursor->advance(); progressMeter->hit(); - if ( logLevel > 1 && phaseOne->n % 10000 == 0 ) { + if ( logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(2) ) && phaseOne->n % 10000 == 0 ) { printMemInfo( "\t iterating objects" ); } } @@ -175,7 +175,7 @@ namespace mongo { Timer t; - tlog(1) << "fastBuildIndex " << ns << ' ' << idx.info.obj().toString() << endl; + MONGO_TLOG(1) << "fastBuildIndex " << ns << ' ' << idx.info.obj().toString() << endl; bool dupsAllowed = !idx.unique() || ignoreUniqueIndex(idx); bool dropDups = idx.dropDups() || inDBRepair; @@ -183,7 +183,8 @@ namespace mongo { getDur().writingDiskLoc(idx.head).Null(); - if ( logLevel > 1 ) printMemInfo( "before index start" ); + if ( logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(2) ) ) + printMemInfo( "before index start" ); /* get and sort all the keys ----- */ ProgressMeterHolder pm(op->setMessage("index: (1/3) external sort", @@ -201,9 +202,11 @@ namespace mongo { d->setIndexIsMultikey(ns, idxNo); } - if ( logLevel > 1 ) printMemInfo( "before final sort" ); + if ( logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(2) ) ) + printMemInfo( "before final sort" ); phase1.sorter->sort( mayInterrupt ); - if ( logLevel > 1 ) printMemInfo( "after final sort" ); + if ( logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(2) ) ) + printMemInfo( "after final sort" ); LOG(t.seconds() > 5 ? 0 : 1) << "\t external sort used : " << sorter.numFiles() << " files " << " in " << t.seconds() << " secs" << endl; diff --git a/src/mongo/db/index_update.cpp b/src/mongo/db/index_update.cpp index daaa955f84f..390f1794de5 100644 --- a/src/mongo/db/index_update.cpp +++ b/src/mongo/db/index_update.cpp @@ -269,7 +269,7 @@ namespace mongo { bool background = idx.info.obj()["background"].trueValue(); - tlog() << "build index " << ns << ' ' << idx.keyPattern() << ( background ? " background" : "" ) << endl; + MONGO_TLOG(0) << "build index " << ns << ' ' << idx.keyPattern() << ( background ? " background" : "" ) << endl; Timer t; unsigned long long n; @@ -284,7 +284,7 @@ namespace mongo { BackgroundIndexBuildJob j(ns.c_str()); n = j.go(ns, d, idx); } - tlog() << "build index done. scanned " << n << " total records. " << t.millis() / 1000.0 << " secs" << endl; + MONGO_TLOG(0) << "build index done. scanned " << n << " total records. " << t.millis() / 1000.0 << " secs" << endl; } extern BSONObj id_obj; // { _id : 1 } diff --git a/src/mongo/db/initialize_server_global_state.cpp b/src/mongo/db/initialize_server_global_state.cpp index 9d20834d5b2..125ab6fccf1 100644 --- a/src/mongo/db/initialize_server_global_state.cpp +++ b/src/mongo/db/initialize_server_global_state.cpp @@ -19,15 +19,25 @@ #include "mongo/db/initialize_server_global_state.h" #include <boost/filesystem/operations.hpp> +#include <memory> #ifndef _WIN32 +#include <syslog.h> #include <sys/types.h> #include <sys/wait.h> #endif +#include "mongo/base/init.h" #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/security_key.h" #include "mongo/db/cmdline.h" +#include "mongo/logger/logger.h" +#include "mongo/logger/message_event.h" +#include "mongo/logger/message_event_utf8_encoder.h" +#include "mongo/logger/rotatable_file_appender.h" +#include "mongo/logger/rotatable_file_manager.h" +#include "mongo/logger/rotatable_file_writer.h" +#include "mongo/logger/syslog_appender.h" #include "mongo/platform/process_id.h" #include "mongo/util/log.h" #include "mongo/util/net/listen.h" @@ -132,14 +142,13 @@ namespace mongo { // this is run in the final child process (the server) - // stdout handled in initLogging - //fclose(stdout); - //freopen("/dev/null", "w", stdout); - - fclose(stderr); - fclose(stdin); + FILE* f = freopen("/dev/null", "w", stdout); + if ( f == NULL ) { + cout << "Cant reassign stdout while forking server process: " << strerror(errno) << endl; + return false; + } - FILE* f = freopen("/dev/null", "w", stderr); + f = freopen("/dev/null", "w", stderr); if ( f == NULL ) { cout << "Cant reassign stderr while forking server process: " << strerror(errno) << endl; return false; @@ -160,32 +169,74 @@ namespace mongo { _exit(EXIT_FAILURE); } - bool initializeServerGlobalState() { + MONGO_INITIALIZER_GENERAL(ServerLogRedirection, + ("GlobalLogManager", "globalVariablesConfigured"), + ("default"))( + InitializerContext*) { - Listener::globalTicketHolder.resize( cmdLine.maxConns ); + using logger::LogManager; + using logger::MessageEventEphemeral; + using logger::MessageEventDetailsEncoder; + using logger::MessageEventWithContextEncoder; + using logger::MessageLogDomain; + using logger::RotatableFileAppender; + using logger::StatusWithRotatableFileWriter; #ifndef _WIN32 - if (!fs::is_directory(cmdLine.socket)) { - cout << cmdLine.socket << " must be a directory" << endl; - return false; - } + using logger::SyslogAppender; if (cmdLine.logWithSyslog) { StringBuilder sb; sb << cmdLine.binaryName << "." << cmdLine.port; - Logstream::useSyslog( sb.str().c_str() ); + openlog(strdup(sb.str().c_str()), LOG_PID | LOG_CONS, LOG_USER); + LogManager* manager = logger::globalLogManager(); + manager->getGlobalDomain()->clearAppenders(); + manager->getGlobalDomain()->attachAppender( + MessageLogDomain::AppenderAutoPtr( + new SyslogAppender<MessageEventEphemeral>( + new logger::MessageEventWithContextEncoder))); + manager->getNamedDomain("javascriptOutput")->attachAppender( + MessageLogDomain::AppenderAutoPtr( + new SyslogAppender<MessageEventEphemeral>( + new logger::MessageEventWithContextEncoder))); } -#endif +#endif // defined(_WIN32) + if (!cmdLine.logpath.empty()) { fassert(16448, !cmdLine.logWithSyslog); string absoluteLogpath = boost::filesystem::absolute( cmdLine.logpath, cmdLine.cwd).string(); - if (!initLogging(absoluteLogpath, cmdLine.logAppend)) { - cout << "Bad logpath value: \"" << absoluteLogpath << "\"; terminating." << endl; - return false; + StatusWithRotatableFileWriter writer = + logger::globalRotatableFileManager()->openFile(absoluteLogpath, cmdLine.logAppend); + if (!writer.isOK()) { + return writer.getStatus(); } + LogManager* manager = logger::globalLogManager(); + manager->getGlobalDomain()->clearAppenders(); + manager->getGlobalDomain()->attachAppender( + MessageLogDomain::AppenderAutoPtr( + new RotatableFileAppender<MessageEventEphemeral>( + new MessageEventDetailsEncoder, writer.getValue()))); + manager->getNamedDomain("javascriptOutput")->attachAppender( + MessageLogDomain::AppenderAutoPtr( + new RotatableFileAppender<MessageEventEphemeral>( + new MessageEventDetailsEncoder, writer.getValue()))); } + return Status::OK(); + } + + bool initializeServerGlobalState() { + + Listener::globalTicketHolder.resize( cmdLine.maxConns ); + +#ifndef _WIN32 + if (!fs::is_directory(cmdLine.socket)) { + cout << cmdLine.socket << " must be a directory" << endl; + return false; + } +#endif + if (!cmdLine.pidFile.empty()) { writePidFile(cmdLine.pidFile); } diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp index cfcbb0480c4..9b260663b93 100644 --- a/src/mongo/db/instance.cpp +++ b/src/mongo/db/instance.cpp @@ -318,7 +318,7 @@ namespace mongo { void mongoAbort(const char *msg) { if( reportEventToSystem ) reportEventToSystem(msg); - rawOut(msg); + severe() << msg; ::abort(); } @@ -406,7 +406,7 @@ namespace mongo { debug.op = op; long long logThreshold = cmdLine.slowMS; - bool shouldLog = logLevel >= 1; + bool shouldLog = logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1)); if ( op == dbQuery ) { if ( handlePossibleShardedMessage( m , &dbresponse ) ) @@ -466,12 +466,12 @@ namespace mongo { } } catch ( UserException& ue ) { - tlog(3) << " Caught Assertion in " << opToString(op) << ", continuing " + MONGO_TLOG(3) << " Caught Assertion in " << opToString(op) << ", continuing " << ue.toString() << endl; debug.exceptionInfo = ue.getInfo(); } catch ( AssertionException& e ) { - tlog(3) << " Caught Assertion in " << opToString(op) << ", continuing " + MONGO_TLOG(3) << " Caught Assertion in " << opToString(op) << ", continuing " << e.toString() << endl; debug.exceptionInfo = e.getInfo(); shouldLog = true; @@ -484,7 +484,7 @@ namespace mongo { logThreshold += currentOp.getExpectedLatencyMs(); if ( shouldLog || debug.executionTime > logThreshold ) { - mongo::tlog() << debug.report( currentOp ) << endl; + MONGO_TLOG(0) << debug.report( currentOp ) << endl; } if ( currentOp.shouldDBProfile( debug.executionTime ) ) { @@ -514,13 +514,13 @@ namespace mongo { uassert( 13004 , str::stream() << "sent negative cursors to kill: " << n , n >= 1 ); if ( n > 2000 ) { - LOG( n < 30000 ? LL_WARNING : LL_ERROR ) << "receivedKillCursors, n=" << n << endl; + ( n < 30000 ? warning() : error() ) << "receivedKillCursors, n=" << n << endl; verify( n < 30000 ); } int found = ClientCursor::eraseIfAuthorized(n, (long long *) x); - if ( logLevel > 0 || found != n ) { + if ( logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1)) || found != n ) { LOG( found == n ? 1 : 0 ) << "killcursors: found " << found << " of " << n << endl; } diff --git a/src/mongo/db/jsobj.cpp b/src/mongo/db/jsobj.cpp index 55c484720ed..bc488d07aea 100644 --- a/src/mongo/db/jsobj.cpp +++ b/src/mongo/db/jsobj.cpp @@ -922,13 +922,14 @@ namespace mongo { } void BSONObj::dump() const { - out() << hex; + LogstreamBuilder builder = out(); + builder << hex; const char *p = objdata(); for ( int i = 0; i < objsize(); i++ ) { - out() << i << '\t' << ( 0xff & ( (unsigned) *p ) ); + builder << i << '\t' << ( 0xff & ( (unsigned) *p ) ); if ( *p >= 'A' && *p <= 'z' ) - out() << '\t' << *p; - out() << endl; + builder << '\t' << *p; + builder << endl; p++; } } diff --git a/src/mongo/db/ops/query.cpp b/src/mongo/db/ops/query.cpp index b7156d305ae..9f6822fd51a 100644 --- a/src/mongo/db/ops/query.cpp +++ b/src/mongo/db/ops/query.cpp @@ -800,7 +800,7 @@ namespace mongo { ccPointer.reset( new ClientCursor( queryOptions, cursor, ns, jsobj.getOwned() ) ); cursorid = ccPointer->cursorid(); - DEV tlog(2) << "query has more, cursorid: " << cursorid << endl; + DEV { MONGO_TLOG(2) << "query has more, cursorid: " << cursorid << endl; } if ( cursor->supportYields() ) { ClientCursor::YieldData data; ccPointer->prepareToYield( data ); @@ -815,7 +815,10 @@ namespace mongo { } if ( !ccPointer->ok() && ccPointer->c()->tailable() ) { - DEV tlog() << "query has no more but tailable, cursorid: " << cursorid << endl; + DEV { + MONGO_TLOG(0) << "query has no more but tailable, cursorid: " << cursorid << + endl; + } } if( queryOptions & QueryOption_Exhaust ) { @@ -941,8 +944,7 @@ namespace mongo { uassert( 16332 , "can't have an empty ns" , ns[0] ); - if( logLevel >= 2 ) - log() << "runQuery called " << ns << " " << jsobj << endl; + LOG(2) << "runQuery called " << ns << " " << jsobj << endl; curop.debug().ns = ns; curop.debug().ntoreturn = pq.getNumToReturn(); diff --git a/src/mongo/db/pagefault.cpp b/src/mongo/db/pagefault.cpp index 74d4e486d53..3a738e55ecc 100644 --- a/src/mongo/db/pagefault.cpp +++ b/src/mongo/db/pagefault.cpp @@ -45,7 +45,7 @@ namespace mongo { if( LockMongoFilesShared::getEra() != era ) { // files opened and closed. we don't try to handle but just bail out; this is much simpler // and less error prone and saves us from taking a dbmutex readlock. - dlog(2) << "era changed" << endl; + MONGO_DLOG(2) << "era changed" << endl; return; } r->touch(); @@ -59,7 +59,7 @@ namespace mongo { verify( cc()._pageFaultRetryableSection == 0 ); if( Lock::isLocked() ) { cc()._pageFaultRetryableSection = 0; - if( debug || logLevel > 2 ) { + if( debug || logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(3)) ) { LOGSOME << "info PageFaultRetryableSection will not yield, already locked upon reaching" << endl; } } diff --git a/src/mongo/db/pdfile.cpp b/src/mongo/db/pdfile.cpp index db6d1351308..e6ba5e9d104 100644 --- a/src/mongo/db/pdfile.cpp +++ b/src/mongo/db/pdfile.cpp @@ -399,7 +399,7 @@ namespace mongo { if( !boost::filesystem::exists(filename) ) return false; if( !mmf.open(filename,false) ) { - dlog(2) << "info couldn't open " << filename << " probably end of datafile list" << endl; + MONGO_DLOG(2) << "info couldn't open " << filename << " probably end of datafile list" << endl; return false; } _mb = mmf.getView(); verify(_mb); @@ -518,8 +518,11 @@ namespace mongo { addNewExtentToNamespace(ns, e, loc, emptyLoc, newCapped); - DEV tlog(1) << "new extent " << ns << " size: 0x" << hex << ExtentSize << " loc: 0x" << hex << offset - << " emptyLoc:" << hex << emptyLoc.getOfs() << dec << endl; + DEV { + MONGO_TLOG(1) << "new extent " << ns << " size: 0x" << hex << ExtentSize << " loc: 0x" + << hex << offset << " emptyLoc:" << hex << emptyLoc.getOfs() << dec + << endl; + } return e; } @@ -586,7 +589,7 @@ namespace mongo { } } - if( n > 128 ) LOG( n < 512 ? 1 : 0 ) << "warning: newExtent " << n << " scanned\n"; + if( n > 128 ) { LOG( n < 512 ? 1 : 0 ) << "warning: newExtent " << n << " scanned\n"; } if( best ) { Extent *e = best; @@ -1953,8 +1956,9 @@ namespace mongo { q = p / (c+"ns"); bool ok = false; MONGO_ASSERT_ON_EXCEPTION( ok = fo.apply( q ) ); - if ( ok ) + if ( ok ) { LOG(2) << fo.op() << " file " << q.string() << endl; + } int i = 0; int extra = 10; // should not be necessary, this is defensive in case there are missing files while ( 1 ) { diff --git a/src/mongo/db/pipeline/pipeline_d.cpp b/src/mongo/db/pipeline/pipeline_d.cpp index 5cfb53d148b..d6dcb22732e 100644 --- a/src/mongo/db/pipeline/pipeline_d.cpp +++ b/src/mongo/db/pipeline/pipeline_d.cpp @@ -120,11 +120,11 @@ namespace mongo { /* for debugging purposes, show what the query and sort are */ DEV { (log() << "\n---- query BSON\n" << - queryObj.jsonString(Strict, 1) << "\n----\n").flush(); + queryObj.jsonString(Strict, 1) << "\n----\n"); (log() << "\n---- sort BSON\n" << - sortObj.jsonString(Strict, 1) << "\n----\n").flush(); + sortObj.jsonString(Strict, 1) << "\n----\n"); (log() << "\n---- fullName\n" << - fullName << "\n----\n").flush(); + fullName << "\n----\n"); } // Create the necessary context to use a Cursor, including taking a namespace read lock, diff --git a/src/mongo/db/query_optimizer_internal.cpp b/src/mongo/db/query_optimizer_internal.cpp index 3191772ec51..6d21d03bec6 100644 --- a/src/mongo/db/query_optimizer_internal.cpp +++ b/src/mongo/db/query_optimizer_internal.cpp @@ -946,8 +946,9 @@ namespace mongo { shared_ptr<QueryPlanRunner> QueryPlanRunnerQueue::init() { massert( 10369, "no plans", _plans.plans().size() > 0 ); - if ( _plans.plans().size() > 1 ) + if ( _plans.plans().size() > 1 ) { LOG(1) << " running multiple plans" << endl; + } for( QueryPlanSet::PlanVector::const_iterator i = _plans.plans().begin(); i != _plans.plans().end(); ++i ) { shared_ptr<QueryPlanRunner> runner( _prototypeRunner.createChild() ); diff --git a/src/mongo/db/record.cpp b/src/mongo/db/record.cpp index d55d6d40f68..46c36537e42 100644 --- a/src/mongo/db/record.cpp +++ b/src/mongo/db/record.cpp @@ -190,7 +190,7 @@ namespace mongo { if ( rarely_count++ % ( 2048 / BigHashSize ) == 0 ) { long long now = Listener::getElapsedTimeMillis(); RARELY if ( now == 0 ) { - tlog() << "warning Listener::getElapsedTimeMillis returning 0ms" << endl; + MONGO_TLOG(0) << "warning Listener::getElapsedTimeMillis returning 0ms" << endl; } if ( now - _lastRotate > ( 1000 * RotateTimeSecs ) ) { diff --git a/src/mongo/db/repl/master_slave.cpp b/src/mongo/db/repl/master_slave.cpp index cb4a4ce3388..9fd6cb721f8 100644 --- a/src/mongo/db/repl/master_slave.cpp +++ b/src/mongo/db/repl/master_slave.cpp @@ -527,8 +527,7 @@ namespace mongo { @param alreadyLocked caller already put us in write lock if true */ void ReplSource::sync_pullOpLog_applyOperation(BSONObj& op, bool alreadyLocked) { - if( logLevel >= 6 ) // op.tostring is expensive so doing this check explicitly - LOG(6) << "processing op: " << op << endl; + LOG(6) << "processing op: " << op << endl; if( op.getStringField("op")[0] == 'n' ) return; @@ -609,8 +608,7 @@ namespace mongo { bool empty = ctx.db()->isEmpty(); bool incompleteClone = incompleteCloneDbs.count( clientName ) != 0; - if( logLevel >= 6 ) - LOG(6) << "ns: " << ns << ", justCreated: " << ctx.justCreated() << ", empty: " << empty << ", incompleteClone: " << incompleteClone << endl; + LOG(6) << "ns: " << ns << ", justCreated: " << ctx.justCreated() << ", empty: " << empty << ", incompleteClone: " << incompleteClone << endl; // always apply admin command command // this is a bit hacky -- the semantics of replication/commands aren't well specified @@ -844,17 +842,14 @@ namespace mongo { nextOpTime = OpTime(); // will reread the op below } else if ( nextOpTime != syncedTo ) { // didn't get what we queried for - error - Nullstream& l = log(); - l << "repl: nextOpTime " << nextOpTime.toStringLong() << ' '; - if ( nextOpTime < syncedTo ) - l << "<??"; - else - l << ">"; - - l << " syncedTo " << syncedTo.toStringLong() << '\n'; - log() << "repl: time diff: " << (nextOpTime.getSecs() - syncedTo.getSecs()) << "sec\n"; - log() << "repl: tailing: " << tailing << '\n'; - log() << "repl: data too stale, halting replication" << endl; + log() + << "repl: nextOpTime " << nextOpTime.toStringLong() << ' ' + << ((nextOpTime < syncedTo) ? "<??" : ">") + << " syncedTo " << syncedTo.toStringLong() << '\n' + << "repl: time diff: " << (nextOpTime.getSecs() - syncedTo.getSecs()) + << "sec\n" + << "repl: tailing: " << tailing << '\n' + << "repl: data too stale, halting replication" << endl; replInfo = replAllDead = "data too stale halted replication"; verify( syncedTo < nextOpTime ); throw SyncException(); @@ -977,7 +972,7 @@ namespace mongo { _sleepAdviceTime = 0; ReplInfo r("sync"); if ( !cmdLine.quiet ) { - Nullstream& l = log(); + LogstreamBuilder l = log(); l << "repl: syncing from "; if( sourceName() != "main" ) { l << "source:" << sourceName() << ' '; diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp index 9917f10b982..1697641ace7 100644 --- a/src/mongo/db/repl/oplog.cpp +++ b/src/mongo/db/repl/oplog.cpp @@ -229,9 +229,7 @@ namespace mongo { append_O_Obj(r->data(), partial, obj); - if ( logLevel >= 6 ) { - LOG( 6 ) << "logOp:" << BSONObj::make(r) << endl; - } + LOG( 6 ) << "logOp:" << BSONObj::make(r) << endl; } static void _logOpOld(const char *opstr, const char *ns, const char *logNS, const BSONObj& obj, BSONObj *o2, bool *bb, bool fromMigrate ) { diff --git a/src/mongo/db/repl/rs.cpp b/src/mongo/db/repl/rs.cpp index 7290e24e7d6..e0c58423611 100644 --- a/src/mongo/db/repl/rs.cpp +++ b/src/mongo/db/repl/rs.cpp @@ -62,7 +62,7 @@ namespace mongo { void replset::sethbmsg(const string& s, const int level) { if (theReplSet) { - theReplSet->sethbmsg(s, logLevel); + theReplSet->sethbmsg(s, level); } } @@ -686,7 +686,7 @@ namespace mongo { int n = 0; for( vector<ReplSetConfig*>::iterator i = cfgs.begin(); i != cfgs.end(); i++ ) { ReplSetConfig* cfg = *i; - DEV LOG(1) << n+1 << " config shows version " << cfg->version << rsLog; + DEV { LOG(1) << n+1 << " config shows version " << cfg->version << rsLog; } if( ++n == 1 ) myVersion = cfg->version; if( cfg->ok() && cfg->version > v ) { highest = cfg; diff --git a/src/mongo/db/server_extra_log_context.cpp b/src/mongo/db/server_extra_log_context.cpp index b183c202751..86c446505ca 100644 --- a/src/mongo/db/server_extra_log_context.cpp +++ b/src/mongo/db/server_extra_log_context.cpp @@ -61,7 +61,7 @@ namespace { if (!logUserIds) return Status::OK(); - return Logstream::registerExtraLogContextFn(appendServerExtraLogContext); + return logger::registerExtraLogContextFn(appendServerExtraLogContext); } } // namespace diff --git a/src/mongo/db/storage/namespace_index.cpp b/src/mongo/db/storage/namespace_index.cpp index 715300d6ef1..07cb3767161 100644 --- a/src/mongo/db/storage/namespace_index.cpp +++ b/src/mongo/db/storage/namespace_index.cpp @@ -70,7 +70,7 @@ namespace mongo { _ht->kill(extra); } catch(DBException&) { - dlog(3) << "caught exception in kill_ns" << endl; + MONGO_DLOG(3) << "caught exception in kill_ns" << endl; } } } diff --git a/src/mongo/dbtests/basictests.cpp b/src/mongo/dbtests/basictests.cpp index ad3e5db00b8..ccc81202170 100644 --- a/src/mongo/dbtests/basictests.cpp +++ b/src/mongo/dbtests/basictests.cpp @@ -203,12 +203,12 @@ namespace BasicTests { if( sec == 1 ) matches++; else - log() << "temp millis: " << t.millis() << endl; + mongo::unittest::log() << "temp millis: " << t.millis() << endl; ASSERT( sec >= 0 && sec <= 2 ); t.reset(); } if ( matches < 2 ) - log() << "matches:" << matches << endl; + mongo::unittest::log() << "matches:" << matches << endl; ASSERT( matches >= 2 ); sleepmicros( 1527123 ); @@ -283,7 +283,7 @@ namespace BasicTests { int elapsedMillis = t.millis(); - log() << "Slept for " << elapsedMillis << endl; + mongo::unittest::log() << "Slept for " << elapsedMillis << endl; ASSERT( almostGTE( elapsedMillis, lastSleepTimeMillis, epsMillis ) ); lastSleepTimeMillis = elapsedMillis; @@ -601,50 +601,6 @@ namespace BasicTests { } } ctest1; - /** Simple tests for log tees. */ - class LogTee { - public: - ~LogTee() { - // Clean global tees on test failure. - Logstream::get().removeGlobalTee( &_tee ); - } - void run() { - // Attempting to remove a tee before any tees are added is safe. - Logstream::get().removeGlobalTee( &_tee ); - - // A log is not written to a non global tee. - log() << "LogTee test" << endl; - assertNumLogs( 0 ); - - // A log is written to a global tee. - Logstream::get().addGlobalTee( &_tee ); - log() << "LogTee test" << endl; - assertNumLogs( 1 ); - - // A log is not written to a tee removed from the global tee list. - Logstream::get().removeGlobalTee( &_tee ); - log() << "LogTee test" << endl; - assertNumLogs( 1 ); - } - private: - void assertNumLogs( int expected ) const { - ASSERT_EQUALS( expected, _tee.numLogs() ); - } - class Tee : public mongo::Tee { - public: - Tee() : - _numLogs() { - } - virtual void write( LogLevel level, const string &str ) { - ++_numLogs; - } - int numLogs() const { return _numLogs; } - private: - int _numLogs; - }; - Tee _tee; - }; - class All : public Suite { public: All() : Suite( "basic" ) { @@ -684,7 +640,6 @@ namespace BasicTests { add< CompressionTest1 >(); - add< LogTee >(); } } myall; diff --git a/src/mongo/dbtests/documenttests.cpp b/src/mongo/dbtests/documenttests.cpp index 073127550bd..73075d039a3 100644 --- a/src/mongo/dbtests/documenttests.cpp +++ b/src/mongo/dbtests/documenttests.cpp @@ -1285,7 +1285,8 @@ namespace DocumentTests { assertComparison(expectedResult, fromBson(a), fromBson(b)); } void assertComparison(int expectedResult, const Value& a, const Value& b) { - log() << "testing " << a.toString() << " and " << b.toString() << endl; + mongo::unittest::log() << + "testing " << a.toString() << " and " << b.toString() << endl; // reflexivity ASSERT_EQUALS(0, cmp(a, a)); ASSERT_EQUALS(0, cmp(b, b)); diff --git a/src/mongo/dbtests/extsorttests.cpp b/src/mongo/dbtests/extsorttests.cpp index 072bce89c5e..27c71517068 100644 --- a/src/mongo/dbtests/extsorttests.cpp +++ b/src/mongo/dbtests/extsorttests.cpp @@ -488,7 +488,8 @@ namespace ExtSortTests { } } catch (...) { - log() << "Failure from line " << line << " on iteration " << iteration << endl; + mongo::unittest::log() << + "Failure from line " << line << " on iteration " << iteration << endl; throw; } } diff --git a/src/mongo/dbtests/framework.cpp b/src/mongo/dbtests/framework.cpp index 826579da552..665cfe7a002 100644 --- a/src/mongo/dbtests/framework.cpp +++ b/src/mongo/dbtests/framework.cpp @@ -167,7 +167,7 @@ namespace mongo { } if (params.count("debug") || params.count("verbose") ) { - logLevel = 1; + logger::globalLogDomain()->setMinimumLoggedSeverity(logger::LogSeverity::Debug(1)); } if (params.count("list")) { @@ -254,7 +254,7 @@ namespace mongo { TestWatchDog twd; twd.go(); - // set tlogLevel to -1 to suppress tlog() output in a test program + // set tlogLevel to -1 to suppress MONGO_TLOG(0) output in a test program tlogLevel = -1; int ret = ::mongo::unittest::Suite::run(suites,filter,runsPerTest); diff --git a/src/mongo/dbtests/jstests.cpp b/src/mongo/dbtests/jstests.cpp index 1542ecf0b20..fb201d3aba7 100644 --- a/src/mongo/dbtests/jstests.cpp +++ b/src/mongo/dbtests/jstests.cpp @@ -132,35 +132,39 @@ namespace JSTests { } }; - /** Installs a tee for auditing log messages, including those logged with tlog(). */ + /** Installs a tee for auditing log messages, including those logged with MONGO_TLOG(0)(). */ class LogRecordingScope { public: LogRecordingScope() : - _oldTLogLevel( tlogLevel ) { + _oldTLogLevel(tlogLevel), + _logged(false), + _handle(mongo::logger::globalLogDomain()->attachAppender( + mongo::logger::MessageLogDomain::AppenderAutoPtr(new Tee(this)))) { tlogLevel = 0; - Logstream::get().addGlobalTee( &_tee ); } ~LogRecordingScope() { - Logstream::get().removeGlobalTee( &_tee ); + mongo::logger::globalLogDomain()->detachAppender(_handle); tlogLevel = _oldTLogLevel; } /** @return most recent log entry. */ - bool logged() const { return _tee.logged(); } + bool logged() const { return _logged; } private: - class Tee : public mongo::Tee { + class Tee : public mongo::logger::MessageLogDomain::EventAppender { public: - Tee() : - _logged() { + Tee(LogRecordingScope* scope) : _scope(scope) {} + virtual ~Tee() {} + virtual Status append(const logger::MessageEventEphemeral& event) { + _scope->_logged = true; + return Status::OK(); } - virtual void write( LogLevel level, const string &str ) { _logged = true; } - bool logged() const { return _logged; } private: - bool _logged; + LogRecordingScope* _scope; }; int _oldTLogLevel; - Tee _tee; + bool _logged; + mongo::logger::MessageLogDomain::AppenderHandle _handle; }; - + /** Error logging in Scope::exec(). */ class ExecLogError { public: @@ -870,7 +874,7 @@ namespace JSTests { ~Utf8Check() { reset(); } void run() { if( !globalScriptEngine->utf8Ok() ) { - log() << "warning: utf8 not supported" << endl; + mongo::unittest::log() << "warning: utf8 not supported" << endl; return; } string utf8ObjSpec = "{'_id':'\\u0001\\u007f\\u07ff\\uffff'}"; diff --git a/src/mongo/dbtests/mmaptests.cpp b/src/mongo/dbtests/mmaptests.cpp index 9f710b89b36..cb69f6b6bbf 100644 --- a/src/mongo/dbtests/mmaptests.cpp +++ b/src/mongo/dbtests/mmaptests.cpp @@ -97,7 +97,8 @@ namespace MMapTests { } } if( t.millis() > 10000 ) { - log() << "warning: MMap LeakTest is unusually slow N:" << N << ' ' << t.millis() << "ms" << endl; + mongo::unittest::log() << "warning: MMap LeakTest is unusually slow N:" << N << + ' ' << t.millis() << "ms" << endl; } } diff --git a/src/mongo/dbtests/perf/perftest.cpp b/src/mongo/dbtests/perf/perftest.cpp index d0a04a39ef9..290b371150d 100644 --- a/src/mongo/dbtests/perf/perftest.cpp +++ b/src/mongo/dbtests/perf/perftest.cpp @@ -762,7 +762,7 @@ namespace Misc { int main( int argc, char **argv, char** envp ) { mongo::runGlobalInitializersOrDie(argc, argv, envp); - logLevel = -1; + mongo::logger::globalLogDomain()->setMinimumLoggedSeverity(mongo::logger::LogSeverity::Log()); client_ = new DBDirectClient(); return mongo::dbtests::runDbTests(argc, argv, "/data/db/perftest"); diff --git a/src/mongo/dbtests/perftests.cpp b/src/mongo/dbtests/perftests.cpp index b0302ac2878..e1f82bfe496 100644 --- a/src/mongo/dbtests/perftests.cpp +++ b/src/mongo/dbtests/perftests.cpp @@ -825,14 +825,14 @@ namespace PerfTests { if( dontOptimizeOutHopefully ) { throw TestException(); } - log() << "hmmm" << endl; + mongo::unittest::log() << "hmmm" << endl; } void thr2(int n) { if( --n <= 0 ) { if( dontOptimizeOutHopefully ) { throw TestException(); } - log() << "hmmm" << endl; + mongo::unittest::log() << "hmmm" << endl; } Z z; try { @@ -846,7 +846,7 @@ namespace PerfTests { if( dontOptimizeOutHopefully ) { throw TestException(); } - log() << "hmmm" << endl; + mongo::unittest::log() << "hmmm" << endl; } try { Z z; @@ -860,7 +860,7 @@ namespace PerfTests { if( dontOptimizeOutHopefully ) { throw TestException(); } - log() << "hmmm" << endl; + mongo::unittest::log() << "hmmm" << endl; } Z z; thr4(n-1); diff --git a/src/mongo/dbtests/queryoptimizertests2.cpp b/src/mongo/dbtests/queryoptimizertests2.cpp index f671063d972..a82e46f8038 100644 --- a/src/mongo/dbtests/queryoptimizertests2.cpp +++ b/src/mongo/dbtests/queryoptimizertests2.cpp @@ -226,9 +226,9 @@ namespace { class QueryMissingNs : public Base { public: - QueryMissingNs() { log() << "querymissingns starts" << endl; } + QueryMissingNs() { mongo::unittest::log() << "querymissingns starts" << endl; } ~QueryMissingNs() { - log() << "end QueryMissingNs" << endl; + mongo::unittest::log() << "end QueryMissingNs" << endl; } void run() { Message m; diff --git a/src/mongo/dbtests/queryutiltests.cpp b/src/mongo/dbtests/queryutiltests.cpp index ad7b0f8f569..949f49e099c 100644 --- a/src/mongo/dbtests/queryutiltests.cpp +++ b/src/mongo/dbtests/queryutiltests.cpp @@ -75,7 +75,7 @@ namespace QueryUtilTests { virtual bool isPointIntervalSet() { return false; } static void checkElt( BSONElement expected, BSONElement actual ) { if ( expected.woCompare( actual, false ) ) { - log() << "expected: " << expected << ", got: " << actual; + mongo::unittest::log() << "expected: " << expected << ", got: " << actual; ASSERT( false ); } } @@ -2123,7 +2123,7 @@ namespace QueryUtilTests { } static void assertEqualWithoutFieldNames( const BSONObj &one, const BSONObj &two ) { if ( !equalWithoutFieldNames( one, two ) ) { - log() << one << " != " << two << endl; + mongo::unittest::log() << one << " != " << two << endl; ASSERT( equalWithoutFieldNames( one, two ) ); } } diff --git a/src/mongo/dbtests/replsettests.cpp b/src/mongo/dbtests/replsettests.cpp index d25ca4315dd..82fe74b322e 100644 --- a/src/mongo/dbtests/replsettests.cpp +++ b/src/mongo/dbtests/replsettests.cpp @@ -209,7 +209,7 @@ namespace ReplSetTests { } } } - log() << "index build ending" << endl; + mongo::unittest::log() << "index build ending" << endl; killCurrentOp.notifyAllWaiters(); cc().shutdown(); diff --git a/src/mongo/dbtests/repltests.cpp b/src/mongo/dbtests/repltests.cpp index da1b6557e4c..7291ad6bde6 100644 --- a/src/mongo/dbtests/repltests.cpp +++ b/src/mongo/dbtests/repltests.cpp @@ -126,7 +126,7 @@ namespace ReplTests { ReplSource a(b.obj()); for( vector< BSONObj >::iterator i = ops.begin(); i != ops.end(); ++i ) { if ( 0 ) { - log() << "op: " << *i << endl; + mongo::unittest::log() << "op: " << *i << endl; } a.applyOperation( *i ); } diff --git a/src/mongo/dbtests/spin_lock_test.cpp b/src/mongo/dbtests/spin_lock_test.cpp index cd789c9fe09..408c51eb408 100644 --- a/src/mongo/dbtests/spin_lock_test.cpp +++ b/src/mongo/dbtests/spin_lock_test.cpp @@ -92,7 +92,7 @@ namespace { } int ms = timer.millis(); - log() << "spinlock ConcurrentIncs time: " << ms << endl; + mongo::unittest::log() << "spinlock ConcurrentIncs time: " << ms << endl; ASSERT_EQUALS( counter, threads*incs ); #if defined(__linux__) diff --git a/src/mongo/dbtests/threadedtests.cpp b/src/mongo/dbtests/threadedtests.cpp index 7005c1e2165..dbb000b4def 100644 --- a/src/mongo/dbtests/threadedtests.cpp +++ b/src/mongo/dbtests/threadedtests.cpp @@ -253,7 +253,7 @@ namespace ThreadedTests { cc().shutdown(); } virtual void validate() { - log() << "mongomutextest validate" << endl; + mongo::unittest::log() << "mongomutextest validate" << endl; ASSERT( ! Lock::isReadLocked() ); ASSERT( wToXSuccessfulUpgradeCount >= 39 * N / 2000 ); { @@ -645,10 +645,13 @@ namespace ThreadedTests { #endif DEV { // a _DEBUG buildbot might be slow, try to avoid false positives - log() << "warning lock upgrade was slow " << t.millis() << endl; + mongo::unittest::log() << + "warning lock upgrade was slow " << t.millis() << endl; } else { - log() << "assertion failure: lock upgrade was too slow: " << t.millis() << endl; + mongo::unittest::log() << + "assertion failure: lock upgrade was too slow: " << + t.millis() << endl; ASSERT( false ); } } @@ -946,7 +949,7 @@ namespace ThreadedTests { _hotel.checkOut(); if( ( i % ( checkIns / 10 ) ) == 0 ) - log() << "checked in " << i << " times..." << endl; + mongo::unittest::log() << "checked in " << i << " times..." << endl; } diff --git a/src/mongo/logger/SConscript b/src/mongo/logger/SConscript index 369486f3b43..e4d3163b203 100644 --- a/src/mongo/logger/SConscript +++ b/src/mongo/logger/SConscript @@ -4,11 +4,23 @@ Import("env") env.StaticLibrary('logger', [ + 'console.cpp', + 'log_manager.cpp', + 'log_severity.cpp', + 'logger.cpp', + 'logstream_builder.cpp', + 'message_event_utf8_encoder.cpp', + 'message_log_domain.cpp', + 'ramlog.cpp', + 'rotatable_file_manager.cpp', 'rotatable_file_writer.cpp', ], - LIBDEPS=['$BUILD_DIR/mongo/base/base']) + LIBDEPS=['$BUILD_DIR/mongo/base/base', + '$BUILD_DIR/mongo/util/concurrency/thread_name']) + +env.CppUnitTest('log_test', 'log_test.cpp', + LIBDEPS=['logger', '$BUILD_DIR/mongo/foundation']) env.CppUnitTest('rotatable_file_writer_test', 'rotatable_file_writer_test.cpp', LIBDEPS=['logger']) - diff --git a/src/mongo/logger/appender.h b/src/mongo/logger/appender.h new file mode 100644 index 00000000000..4248cf68a07 --- /dev/null +++ b/src/mongo/logger/appender.h @@ -0,0 +1,44 @@ +/* Copyright 2013 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 "mongo/base/status.h" + +namespace mongo { +namespace logger { + + /** + * Interface for sinks in a logging system. The core of logging is when events of type E are + * appended to instances of Appender<E>. + * + * Example concrete instances are ConsoleAppender<E>, SyslogAppender<E> and + * RotatableFileAppender<E>. + */ + template <typename E> + class Appender { + public: + typedef E Event; + + virtual ~Appender() {} + + /** + * Appends "event", returns Status::OK() on success. + */ + virtual Status append(const Event& event) = 0; + }; + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/console.cpp b/src/mongo/logger/console.cpp new file mode 100644 index 00000000000..cbff1c09b25 --- /dev/null +++ b/src/mongo/logger/console.cpp @@ -0,0 +1,50 @@ +/* Copyright 2013 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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/logger/console.h" + +#include <iostream> + +namespace mongo { +namespace { + + /* + * Theory of operation: + * + * At process start, the loader initializes "consoleMutex" to NULL. At some point during static + * initialization, the static initialization process, running in the one and only extant thread, + * allocates a new boost::mutex on the heap and assigns consoleMutex to point to it. While + * consoleMutex is still NULL, we know that there is only one thread extant, so it is safe to + * skip locking the consoleMutex in the Console constructor. Once the mutex is initialized, + * users of Console can start acquiring it. + */ + + boost::mutex *consoleMutex = new boost::mutex; + +} // namespace + + Console::Console() : _consoleLock() { + if (consoleMutex) { + boost::unique_lock<boost::mutex> lk(*consoleMutex); + lk.swap(_consoleLock); + } + } + + std::ostream& Console::out() { return std::cout; } + std::istream& Console::in() { return std::cin; } + +} // namespace mongo diff --git a/src/mongo/logger/console.h b/src/mongo/logger/console.h new file mode 100644 index 00000000000..b27799a6223 --- /dev/null +++ b/src/mongo/logger/console.h @@ -0,0 +1,48 @@ +/* Copyright 2013 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 <boost/thread/mutex.hpp> +#include <iosfwd> + +namespace mongo { + + /** + * Representation of the console. Use this in place of cout/cin, in applications that write to + * the console from multiple threads (such as those that use the logging subsystem). + * + * The Console type is synchronized such that only one instance may be in the fully constructed + * state at a time. Correct usage is to instantiate one, write or read from it as desired, and + * then destroy it. + * + * The console streams accept UTF-8 encoded data, and attempt to write it to the attached + * console faithfully. + * + * TODO(schwerin): If no console is attached on Windows (services), should writes here go to the + * event logger? + */ + class Console { + public: + Console(); + + std::ostream& out(); + std::istream& in(); + + private: + boost::unique_lock<boost::mutex> _consoleLock; + }; + +} // namespace mongo diff --git a/src/mongo/logger/console_appender.h b/src/mongo/logger/console_appender.h new file mode 100644 index 00000000000..214fc921148 --- /dev/null +++ b/src/mongo/logger/console_appender.h @@ -0,0 +1,54 @@ +/* Copyright 2013 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 <boost/scoped_ptr.hpp> +#include <iostream> + +#include "mongo/base/disallow_copying.h" +#include "mongo/base/status.h" +#include "mongo/logger/appender.h" +#include "mongo/logger/console.h" +#include "mongo/logger/encoder.h" + +namespace mongo { +namespace logger { + + /** + * Appender for writing to the console (stdout). + */ + template <typename Event> + class ConsoleAppender : public Appender<Event> { + MONGO_DISALLOW_COPYING(ConsoleAppender); + + public: + typedef Encoder<Event> EventEncoder; + + explicit ConsoleAppender(EventEncoder* encoder) : _encoder(encoder) {} + virtual Status append(const Event& event) { + Console console; + _encoder->encode(event, console.out()).flush(); + if (!console.out()) + return Status(ErrorCodes::LogWriteFailed, "Error writing log message to console."); + return Status::OK(); + } + + private: + boost::scoped_ptr<EventEncoder> _encoder; + }; + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/encoder.h b/src/mongo/logger/encoder.h new file mode 100644 index 00000000000..f79e0d3a74e --- /dev/null +++ b/src/mongo/logger/encoder.h @@ -0,0 +1,37 @@ +/* Copyright 2013 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 <iosfwd> + +namespace mongo { +namespace logger { + + /** + * Interface for objects that encode Events to std::ostreams. + * + * Most appender implementations write to streams, and Encoders represent the process of + * encoding events into streams. + */ + template <typename Event> + class Encoder { + public: + virtual ~Encoder() {} + virtual std::ostream& encode(const Event& event, std::ostream& os) = 0; + }; + +} // namespace logger +} // nnamspace mongo diff --git a/src/mongo/logger/labeled_level.h b/src/mongo/logger/labeled_level.h new file mode 100644 index 00000000000..03a90b6bab5 --- /dev/null +++ b/src/mongo/logger/labeled_level.h @@ -0,0 +1,64 @@ +/* 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> + +#include "mongo/logger/log_severity.h" + +namespace mongo { +namespace logger { + + /** + * Deprecated utility for associating a string and log level together. + */ + class LabeledLevel { + public: + LabeledLevel( int level ) : _level( level ) {} + LabeledLevel( const char* label, int level ) : _label( label ), _level( level ) {} + LabeledLevel( const std::string& label, int level ) : _label( label ), _level( level ) {} + + LabeledLevel operator+( int i ) const { + return LabeledLevel( _label, _level + i ); + } + + LabeledLevel operator-( int i ) const { + return LabeledLevel( _label, _level - i ); + } + + const std::string& getLabel() const { return _label; } + int getLevel() const { return _level; } + + operator LogSeverity () const { return logger::LogSeverity::cast(_level); } + + private: + std::string _label; + int _level; + }; + + inline bool operator<( const LabeledLevel& ll, const int i ) { return ll.getLevel() < i; } + inline bool operator<( const int i, const LabeledLevel& ll ) { return i < ll.getLevel(); } + inline bool operator>( const LabeledLevel& ll, const int i ) { return ll.getLevel() > i; } + inline bool operator>( const int i, const LabeledLevel& ll ) { return i > ll.getLevel(); } + inline bool operator<=( const LabeledLevel& ll, const int i ) { return ll.getLevel() <= i; } + inline bool operator<=( const int i, const LabeledLevel& ll ) { return i <= ll.getLevel(); } + inline bool operator>=( const LabeledLevel& ll, const int i ) { return ll.getLevel() >= i; } + inline bool operator>=( const int i, const LabeledLevel& ll ) { return i >= ll.getLevel(); } + inline bool operator==( const LabeledLevel& ll, const int i ) { return ll.getLevel() == i; } + inline bool operator==( const int i, const LabeledLevel& ll ) { return i == ll.getLevel(); } + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/log_domain-impl.h b/src/mongo/logger/log_domain-impl.h new file mode 100644 index 00000000000..cb9e0605f8d --- /dev/null +++ b/src/mongo/logger/log_domain-impl.h @@ -0,0 +1,90 @@ +/* Copyright 2013 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 "mongo/logger/message_log_domain.h" + +#include <algorithm> + +/* + * Implementation of LogDomain<E>. Include this in cpp files to instantiate new LogDomain types. + * See message_log_domain.h, e.g. + */ + +namespace mongo { +namespace logger { + + template <typename E> + LogDomain<E>::LogDomain() : _minimumLoggedSeverity(LogSeverity::Log()) {} + + template <typename E> + LogDomain<E>::~LogDomain() { + clearAppenders(); + } + + template <typename E> + void LogDomain<E>::append(const E& event) { + for (typename AppenderVector::const_iterator iter = _appenders.begin(); + iter != _appenders.end(); ++iter) { + + if (*iter) { + (*iter)->append(event); + } + } + } + + template <typename E> + typename LogDomain<E>::AppenderHandle LogDomain<E>::attachAppender( + typename LogDomain<E>::AppenderAutoPtr appender) { + + typename AppenderVector::iterator iter = std::find( + _appenders.begin(), + _appenders.end(), + static_cast<EventAppender*>(NULL)); + + if (iter == _appenders.end()) { + _appenders.push_back(appender.release()); + return AppenderHandle(_appenders.size() - 1); + } + else { + *iter = appender.release(); + return AppenderHandle(iter - _appenders.begin()); + } + } + + template <typename E> + typename LogDomain<E>::AppenderAutoPtr LogDomain<E>::detachAppender( + typename LogDomain<E>::AppenderHandle handle) { + + EventAppender*& appender = _appenders.at(handle._index); + AppenderAutoPtr result(appender); + appender = NULL; + return result; + } + + template <typename E> + void LogDomain<E>::clearAppenders() { + for(typename AppenderVector::const_iterator iter = _appenders.begin(); + iter != _appenders.end(); ++iter) { + + delete *iter; + } + + _appenders.clear(); + } + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/log_domain.h b/src/mongo/logger/log_domain.h new file mode 100644 index 00000000000..b1f6dcb81f3 --- /dev/null +++ b/src/mongo/logger/log_domain.h @@ -0,0 +1,133 @@ +/* Copyright 2013 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 <boost/scoped_ptr.hpp> +#include <memory> +#include <string> +#include <vector> + +#include "mongo/base/disallow_copying.h" +#include "mongo/logger/appender.h" +#include "mongo/logger/log_severity.h" + +namespace mongo { +namespace logger { + + /** + * Logging domain for events of type E. + * + * A logging domain consists of a set of Appenders and a minimum severity. + * + * TODO: The severity doesn't seem to apply for auditing, maybe it only belongs on the + * MessageLogManager? We don't really have multiple tunable logging domains, right now. Other + * than the global domain, shouldLog doesn't matter. + * + * Usage: Configure the log domain in a single threaded context, using attachAppender, + * detachAppender and clearAppenders(). The domain takes ownership of any attached appender, + * returning an AppenderHandle for each attached appender. That handle may be used later to + * detach the appender, causing the domain to release ownership of it. Mostly, this + * attach/detach behavior is useful in testing, since you do not want to change the appenders of + * a domain that is currently receiving append() calls. + * + * Once you've configured the domain, call append() from potentially many threads, to add log + * messages. + */ + template <typename E> + class LogDomain { + MONGO_DISALLOW_COPYING(LogDomain); + public: + typedef E Event; + typedef Appender<Event> EventAppender; + + /** + * Opaque handle returned by attachAppender(), which can be subsequently passed to + * detachAppender() to detach an appender from an instance of LogDomain. + */ + class AppenderHandle { + friend class LogDomain; + public: + AppenderHandle() {} + + private: + explicit AppenderHandle(size_t index) : _index(index) {} + + size_t _index; + }; + + // TODO(schwerin): Replace with unique_ptr in C++11. + typedef std::auto_ptr<EventAppender> AppenderAutoPtr; + + LogDomain(); + ~LogDomain(); + + /** + * Receives an event for logging, calling append(event) on all attached appenders. + * + * TODO(schwerin): Should we return failed statuses somehow? vector<AppenderHandle, Status> + * for failed appends, e.g.? + */ + void append(const Event& event); + + /** + * Predicate that answers the question, "Should I, the caller, append to you, the log + * domain, messages of the given severity?" True means yes. + */ + bool shouldLog(LogSeverity severity) { return severity >= _minimumLoggedSeverity; } + + /** + * Gets the minimum severity of messages that should be sent to this LogDomain. + */ + LogSeverity getMinimumLogSeverity() { return _minimumLoggedSeverity; } + + /** + * Sets the minimum severity of messages that should be sent to this LogDomain. + */ + void setMinimumLoggedSeverity(LogSeverity severity) { _minimumLoggedSeverity = severity; } + + + // + // Configuration methods. Must be synchronized with each other and calls to "append" by the + // caller. + // + + /** + * Attaches "appender" to this domain, taking ownership of it. Returns a handle that may be + * used later to detach this appender. + */ + AppenderHandle attachAppender(AppenderAutoPtr appender); + + /** + * Detaches the appender referenced by "handle" from this domain, releasing ownership of it. + * Returns an auto_ptr to the handler to the caller, who is now responsible for its + * deletion. Caller should consider "handle" is invalid after this call. + */ + AppenderAutoPtr detachAppender(AppenderHandle handle); + + /** + * Destroy all attached appenders, invalidating all handles. + */ + void clearAppenders(); + + private: + typedef std::vector<EventAppender*> AppenderVector; + + LogSeverity _minimumLoggedSeverity; + AppenderVector _appenders; + }; + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/log_manager.cpp b/src/mongo/logger/log_manager.cpp new file mode 100644 index 00000000000..7340ad1bc8b --- /dev/null +++ b/src/mongo/logger/log_manager.cpp @@ -0,0 +1,47 @@ +/* Copyright 2013 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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/logger/log_manager.h" + +#include "mongo/logger/console_appender.h" +#include "mongo/logger/message_event_utf8_encoder.h" + +namespace mongo { +namespace logger { + + LogManager::LogManager() { + // Should really fassert that the following status .isOK(), but it never fails. + _globalDomain.attachAppender(MessageLogDomain::AppenderAutoPtr( + new ConsoleAppender<MessageEventEphemeral>(new MessageEventDetailsEncoder))); + } + + LogManager::~LogManager() { + for (DomainsByNameMap::iterator iter = _domains.begin(); iter != _domains.end(); ++iter) { + delete iter->second; + } + } + + MessageLogDomain* LogManager::getNamedDomain(const std::string& name) { + MessageLogDomain*& domain = _domains[name]; + if (!domain) { + domain = new MessageLogDomain; + } + return domain; + } + +} // logger +} // mongo diff --git a/src/mongo/logger/log_manager.h b/src/mongo/logger/log_manager.h new file mode 100644 index 00000000000..626f1062781 --- /dev/null +++ b/src/mongo/logger/log_manager.h @@ -0,0 +1,57 @@ +/* Copyright 2013 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> + +#include "mongo/base/disallow_copying.h" +#include "mongo/logger/message_log_domain.h" +#include "mongo/logger/rotatable_file_writer.h" +#include "mongo/platform/unordered_map.h" + +namespace mongo { +namespace logger { + + /** + * Container for managing log domains. + * + * Use this while setting up the logging system, before launching any threads. + */ + class LogManager { + MONGO_DISALLOW_COPYING(LogManager); + public: + LogManager(); + ~LogManager(); + + /** + * Gets the global domain for this manager. It has no name. + */ + MessageLogDomain* getGlobalDomain() { return &_globalDomain; } + + /** + * Get the log domain with the given name, creating if needed. + */ + MessageLogDomain* getNamedDomain(const std::string& name); + + private: + typedef unordered_map<std::string, MessageLogDomain*> DomainsByNameMap; + + DomainsByNameMap _domains; + MessageLogDomain _globalDomain; + }; + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/log_severity-inl.h b/src/mongo/logger/log_severity-inl.h new file mode 100644 index 00000000000..26a1e92f417 --- /dev/null +++ b/src/mongo/logger/log_severity-inl.h @@ -0,0 +1,42 @@ +/* Copyright 2013 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 + +namespace mongo { +namespace logger { + + LogSeverity LogSeverity::Severe() { return LogSeverity(-4); } + LogSeverity LogSeverity::Error() { return LogSeverity(-3); } + LogSeverity LogSeverity::Warning() { return LogSeverity(-2); } + LogSeverity LogSeverity::Info() { return LogSeverity(-1); } + LogSeverity LogSeverity::Log() { return LogSeverity(0); } + LogSeverity LogSeverity::Debug(int debugLevel) { return LogSeverity(debugLevel); } + + LogSeverity LogSeverity::cast(int ll) { return LogSeverity(ll); } + + int LogSeverity::toInt() const { return _severity; } + LogSeverity LogSeverity::moreSevere() const { return LogSeverity(_severity - 1); } + LogSeverity LogSeverity::lessSevere() const { return LogSeverity(_severity + 1); } + + bool LogSeverity::operator==(LogSeverity other) const { return _severity == other._severity; } + bool LogSeverity::operator!=(LogSeverity other) const { return _severity != other._severity; } + bool LogSeverity::operator<(LogSeverity other) const { return _severity > other._severity; } + bool LogSeverity::operator<=(LogSeverity other) const { return _severity >= other._severity; } + bool LogSeverity::operator>(LogSeverity other) const { return _severity < other._severity; } + bool LogSeverity::operator>=(LogSeverity other) const { return _severity <= other._severity; } + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/log_severity.cpp b/src/mongo/logger/log_severity.cpp new file mode 100644 index 00000000000..62f92c47c20 --- /dev/null +++ b/src/mongo/logger/log_severity.cpp @@ -0,0 +1,55 @@ +/* Copyright 2013 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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/logger/log_severity.h" + +#include <iostream> + +namespace mongo { +namespace logger { + +namespace { + const char unknownSeverityString[] = "UNKNOWN"; + const char severeSeverityString[] = "SEVERE"; + const char errorSeverityString[] = "ERROR"; + const char warningSeverityString[] = "warning"; + const char infoSeverityString[] = "info"; + const char debugSeverityString[] = "debug"; +} // namespace + + StringData LogSeverity::toStringData() const { + if (_severity > 0) + return StringData(debugSeverityString, StringData::LiteralTag()); + if (*this == LogSeverity::Severe()) + return StringData(severeSeverityString, StringData::LiteralTag()); + if (*this == LogSeverity::Error()) + return StringData(errorSeverityString, StringData::LiteralTag()); + if (*this == LogSeverity::Warning()) + return StringData(warningSeverityString, StringData::LiteralTag()); + if (*this == LogSeverity::Info()) + return StringData(infoSeverityString, StringData::LiteralTag()); + if (*this == LogSeverity::Log()) + return StringData(infoSeverityString, StringData::LiteralTag()); + return StringData(unknownSeverityString, StringData::LiteralTag()); + } + + std::ostream& operator<<(std::ostream& os, LogSeverity severity) { + return os << severity.toStringData(); + } + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/log_severity.h b/src/mongo/logger/log_severity.h new file mode 100644 index 00000000000..e1e872bd6c0 --- /dev/null +++ b/src/mongo/logger/log_severity.h @@ -0,0 +1,123 @@ +/* Copyright 2013 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 <iosfwd> +#include <string> + +#include "mongo/base/string_data.h" + +namespace mongo { +namespace logger { + + /** + * Representation of the severity / priority of a log message. + * + * Severities are totally ordered, from most severe to least severe as follows: + * Severe, Error, Warning, Info, Log, Debug(1), Debug(2), ... + */ + class LogSeverity { + public: + // + // Static factory methods for getting LogSeverity objects of the various severity levels. + // + + static inline LogSeverity Severe(); + static inline LogSeverity Error(); + static inline LogSeverity Warning(); + static inline LogSeverity Info(); + static inline LogSeverity Log(); // === Debug(0) + static inline LogSeverity Debug(int debugLevel); + + /** + * Casts an integer to a severity. + * + * Do not use this. It exists to enable a handful of leftover uses of LOG(0) and the + * deprecated LabeledLevel. + */ + static inline LogSeverity cast(int); + + inline int toInt() const; + + /** + * Returns a LogSeverity object that is one unit "more severe" than this one. + */ + inline LogSeverity moreSevere() const; + + /** + * Returns a LogSeverity object that is one unit "less severe" than this one. + */ + inline LogSeverity lessSevere() const; + + /** + * Returns a string naming this severity level. + * + * See toStringData(), below. + */ + inline std::string toString() const; + + /** + * Returns a StringData naming this security level. + * + * Not all levels are uniquely named. Debug(N) is named "debug", regardless of "N", + * e.g. + */ + StringData toStringData() const; + + // + // Comparison operations. + // + + /// Returns true if this is exactly as severe as other. + inline bool operator==(const LogSeverity other) const; + + /// Returns true if this is not exactly as severe as other. + inline bool operator!=(const LogSeverity other) const; + + /// Returns true if this is less severe than other. + inline bool operator<(const LogSeverity other) const; + + /// Returns true if this is no more severe than other. + inline bool operator<=(const LogSeverity other) const; + + /// Returns true if this is more severe than other. + inline bool operator>(const LogSeverity other) const; + + /// Returns true if this is no less severe than other. + inline bool operator>=(const LogSeverity other) const; + + private: + explicit LogSeverity(int severity) : _severity(severity) {} + + /// The stored severity. More negative is more severe. NOTE: This means that the >, <, >= + /// and <= operators on LogSeverity have opposite sense of the same operators on the + /// underlying integer. That is, given severities S1 and S2, S1 > S2 means that S1.toInt() + /// < S2.toInt(). + /// + /// TODO(schwerin): Decide if we should change this so more positive is more severe. The + /// logLevel parameter in the database is more compatible with this sense, but it's not + /// totally intuitive. One could also remove the operator overloads in favor of named + /// methods, isNoMoreSevereThan, isLessSevereThan, isMoreSevereThan, isNoLessSevereThan, + /// isSameSeverity and isDifferentSeverity. + int _severity; + }; + + std::ostream& operator<<(std::ostream& os, LogSeverity severity); + +} // namespace logger +} // namespace mongo + +#include "mongo/logger/log_severity-inl.h" diff --git a/src/mongo/logger/log_test.cpp b/src/mongo/logger/log_test.cpp new file mode 100644 index 00000000000..e816c4c382a --- /dev/null +++ b/src/mongo/logger/log_test.cpp @@ -0,0 +1,147 @@ +/* Copyright 2013 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. + */ + +#include "mongo/platform/basic.h" + +#include <sstream> +#include <string> +#include <vector> + +#include "mongo/logger/appender.h" +#include "mongo/logger/encoder.h" +#include "mongo/logger/message_event_utf8_encoder.h" +#include "mongo/logger/message_log_domain.h" +#include "mongo/logger/rotatable_file_appender.h" +#include "mongo/logger/rotatable_file_writer.h" +#include "mongo/platform/compiler.h" +#include "mongo/unittest/unittest.h" +#include "mongo/util/concurrency/thread_name.h" +#include "mongo/util/log.h" + +using namespace mongo::logger; + +namespace mongo { +namespace { + + // TODO(schwerin): Have logger write to a different log from the global log, so that tests can + // redirect their global log output for examination. + class LogTest : public unittest::Test { + friend class LogTestAppender; + public: + LogTest() { + globalLogDomain()->clearAppenders(); + _appenderHandle = globalLogDomain()->attachAppender( + MessageLogDomain::AppenderAutoPtr(new LogTestAppender(this))); + } + + virtual ~LogTest() { globalLogDomain()->detachAppender(_appenderHandle); } + + protected: + std::vector<std::string> _logLines; + + private: + class LogTestAppender : public MessageLogDomain::EventAppender { + public: + explicit LogTestAppender(LogTest* ltest) : _ltest(ltest) {} + virtual ~LogTestAppender() {} + virtual Status append(const MessageLogDomain::Event& event) { + std::ostringstream _os; + if (!_encoder.encode(event, _os)) + return Status(ErrorCodes::LogWriteFailed, "Failed to append to LogTestAppender."); + _ltest->_logLines.push_back(_os.str()); + return Status::OK(); + } + + private: + LogTest *_ltest; + MessageEventUnadornedEncoder _encoder; + }; + + MessageLogDomain::AppenderHandle _appenderHandle; + }; + + TEST_F(LogTest, logContext) { + logContext("WHA!"); + ASSERT_GREATER_THAN(_logLines.size(), 1U); + ASSERT_NOT_EQUALS(_logLines[0].find("WHA!"), std::string::npos); + + // TODO(schwerin): Ensure that logContext rights a proper context to the log stream, + // including the address of the logContext() function. + //void const* logContextFn = reinterpret_cast<void const*>(logContext); + } + + class CountAppender : public Appender<MessageEventEphemeral> { + public: + CountAppender() : _count(0) {} + virtual ~CountAppender() {} + + virtual Status append(const MessageEventEphemeral& event) { + ++_count; + return Status::OK(); + } + + int getCount() { return _count; } + + private: + int _count; + }; + + /** Simple tests for detaching appenders. */ + TEST_F(LogTest, DetachAppender) { + MessageLogDomain::AppenderAutoPtr countAppender(new CountAppender); + MessageLogDomain domain; + + // Appending to the domain before attaching the appender does not affect the appender. + domain.append(MessageEventEphemeral(LogSeverity::Log(), "", "1")); + ASSERT_EQUALS(0, dynamic_cast<CountAppender*>(countAppender.get())->getCount()); + + // Appending to the domain after attaching the appender does affect the appender. + MessageLogDomain::AppenderHandle handle = domain.attachAppender(countAppender); + domain.append(MessageEventEphemeral(LogSeverity::Log(), "", "2")); + countAppender = domain.detachAppender(handle); + ASSERT_EQUALS(1, dynamic_cast<CountAppender*>(countAppender.get())->getCount()); + + // Appending to the domain after detaching the appender does not affect the appender. + domain.append(MessageEventEphemeral(LogSeverity::Log(), "", "3")); + ASSERT_EQUALS(1, dynamic_cast<CountAppender*>(countAppender.get())->getCount()); + } + + class A { + public: + std::string toString() const { + log() << "Golly!\n"; + return "Golly!"; + } + }; + + // Tests that logging while in the midst of logging produces two distinct log messages, with the + // inner log message appearing before the outer. + TEST_F(LogTest, LogstreamBuilderReentrance) { + log() << "Logging A() -- " << A() << " -- done!" << std::endl; + ASSERT_EQUALS(2U, _logLines.size()); + ASSERT_EQUALS(std::string("Golly!\n"), _logLines[0]); + ASSERT_EQUALS(std::string("Logging A() -- Golly! -- done!\n"), _logLines[1]); + } + + // + // Instantiating this object is a basic test of static-initializer-time logging. + // + class B { + public: + B() { log() << "Exercising initializer time logging."; } + } b; + +} // namespace +} // namespace mongo diff --git a/src/mongo/logger/logger.cpp b/src/mongo/logger/logger.cpp new file mode 100644 index 00000000000..27d28509b56 --- /dev/null +++ b/src/mongo/logger/logger.cpp @@ -0,0 +1,54 @@ +/* Copyright 2013 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. + */ + +#include "mongo/logger/logger.h" + +#include "mongo/base/init.h" +#include "mongo/base/status.h" +#include "mongo/bson/inline_decls.h" // For MONGO_unlikely, which should really be in + // mongo/platform/compiler.h + +namespace mongo { +namespace logger { + + static LogManager* theGlobalLogManager; // NULL at program start, before even static + // initialization. + + static RotatableFileManager theGlobalRotatableFileManager; + + LogManager* globalLogManager() { + if (MONGO_unlikely(!theGlobalLogManager)) { + theGlobalLogManager = new LogManager; + } + return theGlobalLogManager; + } + + RotatableFileManager* globalRotatableFileManager() { + return &theGlobalRotatableFileManager; + } + + /** + * Just in case no static initializer called globalLogManager, make sure that the global log + * manager is instantiated while we're still in a single-threaded context. + */ + MONGO_INITIALIZER_GENERAL(GlobalLogManager, MONGO_NO_PREREQUISITES, ("default"))( + InitializerContext*) { + + globalLogManager(); + return Status::OK(); + } + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/logger.h b/src/mongo/logger/logger.h new file mode 100644 index 00000000000..11a739d7721 --- /dev/null +++ b/src/mongo/logger/logger.h @@ -0,0 +1,42 @@ +/* Copyright 2013 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 "mongo/logger/message_log_domain.h" +#include "mongo/logger/log_manager.h" +#include "mongo/logger/rotatable_file_manager.h" + +namespace mongo { +namespace logger { + + /** + * Gets a global singleton instance of RotatableFileManager. + */ + RotatableFileManager* globalRotatableFileManager(); + + /** + * Gets a global singleton instance of LogManager. + */ + LogManager* globalLogManager(); + + /** + * Gets the global MessageLogDomain associated for the global log manager. + */ + inline MessageLogDomain* globalLogDomain() { return globalLogManager()->getGlobalDomain(); } + +} // namespace logger +} // namespace mongo + diff --git a/src/mongo/logger/logstream_builder.cpp b/src/mongo/logger/logstream_builder.cpp new file mode 100644 index 00000000000..3083e693b92 --- /dev/null +++ b/src/mongo/logger/logstream_builder.cpp @@ -0,0 +1,110 @@ +/* Copyright 2013 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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/logger/logstream_builder.h" + +#include "mongo/base/owned_pointer_vector.h" +#include "mongo/logger/tee.h" +#include "mongo/util/assert_util.h" // TODO: remove apple dep for this in threadlocal.h +#include "mongo/util/concurrency/threadlocal.h" + +namespace mongo { + +namespace { + + /// Type of per-thread cache for storing pre-constructed ostringstreams. While its type is + /// vector, it should only ever contain 0 or 1 item. It is a vector rather than just a + /// thread_specific_ptr<> because of the high cost of thread_specific_ptr<>::reset(). + typedef OwnedPointerVector<std::ostringstream> OwnedOstreamVector; + +} // namespace + + TSP_DECLARE(OwnedOstreamVector, threadOstreamCache); + TSP_DEFINE(OwnedOstreamVector, threadOstreamCache); + +namespace logger { + + LogstreamBuilder::LogstreamBuilder(MessageLogDomain* domain, + const std::string& contextName, + LogSeverity severity) + : _domain(domain), + _contextName(contextName), + _severity(severity), + _os(NULL), + _tee(NULL) { + } + + LogstreamBuilder::LogstreamBuilder(logger::MessageLogDomain* domain, + const std::string& contextName, + LabeledLevel labeledLevel) + : _domain(domain), + _contextName(contextName), + _severity(labeledLevel), + _os(NULL), + _tee(NULL) { + + setBaseMessage(labeledLevel.getLabel()); + } + + LogstreamBuilder::LogstreamBuilder(const LogstreamBuilder& other) + : _domain(other._domain), + _contextName(other._contextName), + _severity(other._severity), + _baseMessage(other._baseMessage), + _os(NULL), + _tee(NULL) { + + if (other._os || other._tee) + abort(); + } + + + LogstreamBuilder::~LogstreamBuilder() { + if (_os) { + _baseMessage += _os->str(); + _domain->append(MessageEventEphemeral(_severity, _contextName, _baseMessage)); + if (_tee) + _tee->write(_baseMessage); + _os->str(""); + std::vector<std::ostringstream*>& cache = threadOstreamCache.getMake()->mutableVector(); + if (!cache.empty()) { + cache.push_back(_os); + } + } + } + + void LogstreamBuilder::operator<<(Tee* tee) { + makeStream(); // Adding a Tee counts for purposes of deciding to make a log message. + // TODO: dassert(!_tee); + _tee = tee; + } + + void LogstreamBuilder::makeStream() { + if (!_os) { + std::vector<std::ostringstream*>& oses = threadOstreamCache.getMake()->mutableVector(); + if (oses.empty()) { + _os = new std::ostringstream; + } + else { + _os = oses.back(); + oses.pop_back(); + } + } + } + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/logstream_builder.h b/src/mongo/logger/logstream_builder.h new file mode 100644 index 00000000000..befd7bd19b3 --- /dev/null +++ b/src/mongo/logger/logstream_builder.h @@ -0,0 +1,140 @@ +/* Copyright 2013 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 <boost/scoped_ptr.hpp> +#include <iostream> +#include <sstream> +#include <string> + +#include "mongo/logger/labeled_level.h" +#include "mongo/logger/log_severity.h" +#include "mongo/logger/message_log_domain.h" +#include "mongo/util/exit_code.h" + +namespace mongo { +namespace logger { + + class Tee; + + /** + * Stream-ish object used to build and append log messages. + */ + class LogstreamBuilder { + public: + static LogSeverity severityCast(int ll) { return LogSeverity::cast(ll); } + static LogSeverity severityCast(LogSeverity ls) { return ls; } + static LabeledLevel severityCast(const LabeledLevel &labeled) { return labeled; } + + /** + * Construct a LogstreamBuilder that writes to "domain" on destruction. + * + * "contextName" is a short name of the thread or other context. + * "severity" is the logging priority/severity of the message. + */ + LogstreamBuilder(MessageLogDomain* domain, + const std::string& contextName, + LogSeverity severity); + + /** + * Deprecated. + */ + LogstreamBuilder(MessageLogDomain* domain, + const std::string& contextName, + LabeledLevel labeledLevel); + + /** + * Copies a LogstreamBuilder. LogstreamBuilder instances are copyable only until the first + * call to stream() or operator<<. + * + * TODO(schwerin): After C++11 transition, replace with a move-constructor, and make + * LogstreamBuilder movable. + */ + LogstreamBuilder(const LogstreamBuilder& other); + + /** + * Destroys a LogstreamBuilder(). If anything was written to it via stream() or operator<<, + * constructs a MessageLogDomain::Event and appends it to the associated domain. + */ + ~LogstreamBuilder(); + + + /** + * Sets an optional prefix for the message. + */ + LogstreamBuilder& setBaseMessage(const std::string& baseMessage) { + _baseMessage = baseMessage; + return *this; + } + + std::ostream& stream() { makeStream(); return *_os; } + + LogstreamBuilder& operator<<(const char *x) { stream() << x; return *this; } + LogstreamBuilder& operator<<(const std::string& x) { stream() << x; return *this; } + LogstreamBuilder& operator<<(const StringData& x) { stream() << x; return *this; } + LogstreamBuilder& operator<<(char *x) { stream() << x; return *this; } + LogstreamBuilder& operator<<(char x) { stream() << x; return *this; } + LogstreamBuilder& operator<<(int x) { stream() << x; return *this; } + LogstreamBuilder& operator<<(ExitCode x) { stream() << x; return *this; } + LogstreamBuilder& operator<<(long x) { stream() << x; return *this; } + LogstreamBuilder& operator<<(unsigned long x) { stream() << x; return *this; } + LogstreamBuilder& operator<<(unsigned x) { stream() << x; return *this; } + LogstreamBuilder& operator<<(unsigned short x) { stream() << x; return *this; } + LogstreamBuilder& operator<<(double x) { stream() << x; return *this; } + LogstreamBuilder& operator<<(void *x) { stream() << x; return *this; } + LogstreamBuilder& operator<<(const void *x) { stream() << x; return *this; } + LogstreamBuilder& operator<<(long long x) { stream() << x; return *this; } + LogstreamBuilder& operator<<(unsigned long long x) { stream() << x; return *this; } + LogstreamBuilder& operator<<(bool x) { stream() << x; return *this; } + + template <typename T> + LogstreamBuilder& operator<<(const T& x) { + stream() << x.toString(); + return *this; + } + + LogstreamBuilder& operator<< (std::ostream& ( *manip )(std::ostream&)) { + stream() << manip; + return *this; + } + LogstreamBuilder& operator<< (std::ios_base& (*manip)(std::ios_base&)) { + stream() << manip; + return *this; + } + + /** + * In addition to appending the message to _domain, write it to the given tee. May only + * be called once per instance of LogstreamBuilder. + */ + void operator<<(Tee* tee); + + private: + LogstreamBuilder& operator=(const LogstreamBuilder& other); + + void makeStream(); + + MessageLogDomain* _domain; + std::string _contextName; + LogSeverity _severity; + std::string _baseMessage; + std::ostringstream* _os; + Tee* _tee; + + }; + + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/message_event.h b/src/mongo/logger/message_event.h new file mode 100644 index 00000000000..35afc2b03e2 --- /dev/null +++ b/src/mongo/logger/message_event.h @@ -0,0 +1,46 @@ +/* Copyright 2013 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 "mongo/base/string_data.h" +#include "mongo/logger/log_severity.h" + +namespace mongo { +namespace logger { + + /** + * Free form text log message object that does not own the storage behind its message and + * contextName. + * + * Used and owned by one thread. This is the message type used by MessageLogDomain. + */ + class MessageEventEphemeral { + public: + MessageEventEphemeral(LogSeverity severity, StringData contextName, StringData message) : + _severity(severity), _contextName(contextName), _message(message) {} + + LogSeverity getSeverity() const { return _severity; } + StringData getContextName() const { return _contextName; } + StringData getMessage() const { return _message; } + + private: + LogSeverity _severity; + StringData _contextName; + StringData _message; + }; + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/message_event_utf8_encoder.cpp b/src/mongo/logger/message_event_utf8_encoder.cpp new file mode 100644 index 00000000000..b53c000e1e2 --- /dev/null +++ b/src/mongo/logger/message_event_utf8_encoder.cpp @@ -0,0 +1,87 @@ +/* Copyright 2013 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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/logger/message_event_utf8_encoder.h" + +#include <iostream> + +#include "mongo/util/time_support.h" + +namespace mongo { +namespace logger { + + MessageEventDetailsEncoder::~MessageEventDetailsEncoder() {} + std::ostream& MessageEventDetailsEncoder::encode(const MessageEventEphemeral& event, + std::ostream &os) { + + static const size_t maxLogLine = 10 * 1024; + + char dateString[64]; + curTimeString(dateString); + os << dateString << ' '; + StringData contextName = event.getContextName(); + if (!contextName.empty()) { + os << '[' << contextName << "] "; + } + + LogSeverity severity = event.getSeverity(); + if (severity >= LogSeverity::Info()) { + os << severity << ": "; + } + + StringData msg = event.getMessage(); + if (msg.size() > maxLogLine) { + os << "warning: log line attempted (" << msg.size() / 1024 << "k) over max size (" << + maxLogLine / 1024 << "k), printing beginning and end ... "; + os << msg.substr(0, maxLogLine / 3); + os << " .......... "; + os << msg.substr(msg.size() - (maxLogLine / 3)); + } + else { + os << msg; + } + if (!msg.endsWith(StringData("\n", StringData::LiteralTag()))) + os << '\n'; + return os; + } + + MessageEventWithContextEncoder::~MessageEventWithContextEncoder() {} + std::ostream& MessageEventWithContextEncoder::encode(const MessageEventEphemeral& event, + std::ostream& os) { + StringData contextName = event.getContextName(); + if (!contextName.empty()) { + os << '[' << contextName << "] "; + } + StringData message = event.getMessage(); + os << message; + if (!message.endsWith("\n")) + os << '\n'; + return os; + } + + MessageEventUnadornedEncoder::~MessageEventUnadornedEncoder() {} + std::ostream& MessageEventUnadornedEncoder::encode(const MessageEventEphemeral& event, + std::ostream& os) { + StringData message = event.getMessage(); + os << message; + if (!message.endsWith("\n")) + os << '\n'; + return os; + } + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/message_event_utf8_encoder.h b/src/mongo/logger/message_event_utf8_encoder.h new file mode 100644 index 00000000000..948f39508bd --- /dev/null +++ b/src/mongo/logger/message_event_utf8_encoder.h @@ -0,0 +1,53 @@ +/* Copyright 2013 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 "mongo/logger/encoder.h" +#include "mongo/logger/message_event.h" + +namespace mongo { +namespace logger { + + /** + * Encoder that writes log messages of the style that MongoDB writes to console and files. + */ + class MessageEventDetailsEncoder : public Encoder<MessageEventEphemeral> { + public: + virtual ~MessageEventDetailsEncoder(); + virtual std::ostream& encode(const MessageEventEphemeral& event, std::ostream& os); + }; + + /** + * Encoder that generates log messages suitable for syslog. + */ + class MessageEventWithContextEncoder : public Encoder<MessageEventEphemeral> { + public: + virtual ~MessageEventWithContextEncoder(); + virtual std::ostream& encode(const MessageEventEphemeral& event, std::ostream& os); + }; + + + /** + * Encoder that generates log messages containing only the raw text of the message. + */ + class MessageEventUnadornedEncoder : public Encoder<MessageEventEphemeral> { + public: + virtual ~MessageEventUnadornedEncoder(); + virtual std::ostream& encode(const MessageEventEphemeral& event, std::ostream& os); + }; + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/message_log_domain.cpp b/src/mongo/logger/message_log_domain.cpp new file mode 100644 index 00000000000..40d11c65f22 --- /dev/null +++ b/src/mongo/logger/message_log_domain.cpp @@ -0,0 +1,28 @@ +/* Copyright 2013 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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/logger/message_log_domain.h" + +#include "mongo/logger/log_domain-impl.h" + +namespace mongo { +namespace logger { + + template class LogDomain<MessageEventEphemeral>; + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/message_log_domain.h b/src/mongo/logger/message_log_domain.h new file mode 100644 index 00000000000..faf97d61342 --- /dev/null +++ b/src/mongo/logger/message_log_domain.h @@ -0,0 +1,32 @@ +/* Copyright 2013 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 <boost/scoped_ptr.hpp> +#include <memory> +#include <string> +#include <vector> + +#include "mongo/logger/log_domain.h" +#include "mongo/logger/message_event.h" + +namespace mongo { +namespace logger { + + typedef LogDomain<MessageEventEphemeral> MessageLogDomain; + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/util/ramlog.cpp b/src/mongo/logger/ramlog.cpp index 6501570bff3..6bb8e9df40d 100644 --- a/src/mongo/util/ramlog.cpp +++ b/src/mongo/logger/ramlog.cpp @@ -15,11 +15,13 @@ * limitations under the License. */ -#include "pch.h" -#include "log.h" -#include "ramlog.h" -#include "mongoutils/html.h" -#include "mongoutils/str.h" +#include "mongo/platform/basic.h" + +#include "mongo/logger/ramlog.h" + +#include "mongo/logger/message_event_utf8_encoder.h" +#include "mongo/util/mongoutils/html.h" +#include "mongo/util/mongoutils/str.h" namespace mongo { @@ -47,15 +49,17 @@ namespace mongo { } - void RamLog::write(LogLevel ll, const std::string& str) { + void RamLog::write(const std::string& str) { + boost::unique_lock<boost::mutex> lk(_mutex); _lastWrite = time(0); _totalLinesWritten++; char *p = lines[(h+n)%N]; unsigned sz = str.size(); + if (0 == sz) return; if( sz < C ) { - if ( str.c_str()[sz-1] == '\n' ) { + if (str.c_str()[sz-1] == '\n' ) { memcpy(p, str.c_str(), sz-1); p[sz-1] = 0; } @@ -70,12 +74,23 @@ namespace mongo { else h = (h+1) % N; } - void RamLog::get( vector<const char*>& v) const { + time_t RamLog::lastWrite() { + boost::unique_lock<boost::mutex> lk(_mutex); + return _lastWrite; + } + + long long RamLog::getTotalLinesWritten() { + boost::unique_lock<boost::mutex> lk(_mutex); + return _totalLinesWritten; + } + + void RamLog::get( std::vector<const char*>& v) { + boost::unique_lock<boost::mutex> lk(_mutex); for( unsigned x=0, i=h; x++ < n; i=(i+1)%N ) v.push_back(lines[i]); } - int RamLog::repeats(const vector<const char *>& v, int i) { + int RamLog::repeats(const std::vector<const char *>& v, int i) { for( int j = i-1; j >= 0 && j+8 > i; j-- ) { if( strcmp(v[i]+20,v[j]+20) == 0 ) { for( int x = 1; ; x++ ) { @@ -90,7 +105,7 @@ namespace mongo { } - string RamLog::clean(const vector<const char *>& v, int i, string line ) { + string RamLog::clean(const std::vector<const char *>& v, int i, string line ) { if( line.empty() ) line = v[i]; if( i > 0 && strncmp(v[i], v[i-1], 11) == 0 ) return string(" ") + line.substr(11); @@ -98,8 +113,8 @@ namespace mongo { } string RamLog::color(const std::string& line) { - string s = str::after(line, "replSet "); - if( str::startsWith(s, "warning") || startsWith(s, "error") ) + std::string s = str::after(line, "replSet "); + if( str::startsWith(s, "warning") || str::startsWith(s, "error") ) return html::red(line); if( str::startsWith(s, "info") ) { if( str::endsWith(s, " up\n") ) @@ -122,13 +137,13 @@ namespace mongo { while( *sp && *sp != ' ' ) sp++; string url(h, sp-h); - stringstream ss; + std::stringstream ss; ss << string(s, h-s) << "<a href=\"" << url << "\">" << url << "</a>" << sp; return ss.str(); } - void RamLog::toHTML(stringstream& s) { - vector<const char*> v; + void RamLog::toHTML(std::stringstream& s) { + std::vector<const char*> v; get( v ); s << "<pre>\n"; @@ -139,13 +154,13 @@ namespace mongo { s << color( linkify( html::escape( clean(v, i) ).c_str() ) ) << '\n'; } else { - stringstream x; + std::stringstream x; x << string(v[i], 0, 24); int nr = (i-r); int last = i+nr-1; for( ; r < i ; r++ ) x << '.'; if( 1 ) { - stringstream r; + std::stringstream r; if( nr == 1 ) r << "repeat last line"; else r << "repeats last " << nr << " lines; ends " << string(v[last]+4,0,15); s << html::a("", r.str(), html::escape( clean(v, i,x.str() ) ) ); @@ -158,6 +173,17 @@ namespace mongo { s << "</pre>\n"; } + RamLogAppender::RamLogAppender(RamLog* ramlog) : _ramlog(ramlog) {} + RamLogAppender::~RamLogAppender() {} + + Status RamLogAppender::append(const logger::MessageEventEphemeral& event) { + std::ostringstream ss; + logger::MessageEventDetailsEncoder encoder; + encoder.encode(event, ss); + _ramlog->write(ss.str()); + return Status::OK(); + } + // --------------- // static things // --------------- @@ -172,8 +198,8 @@ namespace mongo { return 0; return i->second; } - - void RamLog::getNames( vector<string>& names ) { + + void RamLog::getNames( std::vector<string>& names ) { if ( ! _named ) return; @@ -186,6 +212,4 @@ namespace mongo { mongo::mutex* RamLog::_namedLock; RamLog::RM* RamLog::_named = 0; - - Tee* const warnings = new RamLog("warnings"); // Things put here go in serverStatus } diff --git a/src/mongo/logger/ramlog.h b/src/mongo/logger/ramlog.h new file mode 100644 index 00000000000..2b78d91daaa --- /dev/null +++ b/src/mongo/logger/ramlog.h @@ -0,0 +1,90 @@ +// ramlog.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 <map> +#include <sstream> +#include <string> +#include <vector> + +#include "mongo/base/disallow_copying.h" +#include "mongo/base/string_data.h" +#include "mongo/base/status.h" +#include "mongo/util/concurrency/mutex.h" +#include "mongo/logger/appender.h" +#include "mongo/logger/message_event.h" +#include "mongo/logger/tee.h" + +namespace mongo { + + class RamLog : public logger::Tee { + MONGO_DISALLOW_COPYING(RamLog); + public: + RamLog( const std::string& name ); + + void write(const std::string& str); + + void get( std::vector<const char*>& v); + + void toHTML(std::stringstream& s); + + static RamLog* get( const std::string& name ); + static void getNames( std::vector<std::string>& names ); + + time_t lastWrite(); + long long getTotalLinesWritten(); + + private: + static int repeats(const std::vector<const char *>& v, int i); + static string clean(const std::vector<const char *>& v, int i, string line=""); + static string color(const std::string& line); + + /* turn http:... into an anchor */ + static string linkify(const char *s); + + ~RamLog(); // want this private as we want to leak so we can use them till the very end + + enum { + N = 1024, // number of lines + C = 512 // max size of line + }; + + boost::mutex _mutex; // Guards all non-static data. + char lines[N][C]; + unsigned h; // current position + unsigned n; // number of lines stores 0 o N + string _name; + long long _totalLinesWritten; + + typedef std::map<string,RamLog*> RM; + static mongo::mutex* _namedLock; + static RM* _named; + time_t _lastWrite; + }; + + class RamLogAppender : public logger::Appender<logger::MessageEventEphemeral> { + public: + explicit RamLogAppender(RamLog* ramlog); + virtual ~RamLogAppender(); + + virtual Status append(const logger::MessageEventEphemeral& event); + + private: + RamLog* _ramlog; + }; +} diff --git a/src/mongo/logger/rotatable_file_appender.h b/src/mongo/logger/rotatable_file_appender.h new file mode 100644 index 00000000000..32384011280 --- /dev/null +++ b/src/mongo/logger/rotatable_file_appender.h @@ -0,0 +1,61 @@ +/* Copyright 2013 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 "mongo/base/disallow_copying.h" +#include "mongo/base/status.h" +#include "mongo/logger/appender.h" +#include "mongo/logger/encoder.h" +#include "mongo/logger/rotatable_file_writer.h" + +namespace mongo { +namespace logger { + + /** + * Appender for writing to instances of RotatableFileWriter. + */ + template <typename Event> + class RotatableFileAppender : public Appender<Event> { + MONGO_DISALLOW_COPYING(RotatableFileAppender); + + public: + typedef Encoder<Event> EventEncoder; + + /** + * Constructs an appender, that owns "encoder", but not "writer." Caller must + * keep "writer" in scope at least as long as the constructed appender. + */ + RotatableFileAppender(EventEncoder* encoder, RotatableFileWriter* writer) : + _encoder(encoder), + _writer(writer) { + } + + virtual Status append(const Event& event) { + RotatableFileWriter::Use useWriter(_writer); + Status status = useWriter.status(); + if (!status.isOK()) + return status; + _encoder->encode(event, useWriter.stream()).flush(); + return useWriter.status(); + } + + private: + boost::scoped_ptr<EventEncoder> _encoder; + RotatableFileWriter* _writer; + }; + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/rotatable_file_manager.cpp b/src/mongo/logger/rotatable_file_manager.cpp new file mode 100644 index 00000000000..acf31ed90ac --- /dev/null +++ b/src/mongo/logger/rotatable_file_manager.cpp @@ -0,0 +1,70 @@ +/* Copyright 2013 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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/logger/rotatable_file_manager.h" + +#include "mongo/logger/rotatable_file_writer.h" +#include "mongo/util/map_util.h" + +namespace mongo { +namespace logger { + + RotatableFileManager::RotatableFileManager() {} + + RotatableFileManager::~RotatableFileManager() { + for (WriterByNameMap::iterator iter = _writers.begin(); iter != _writers.end(); ++iter) { + delete iter->second; + } + } + + StatusWithRotatableFileWriter RotatableFileManager::openFile(const std::string& fileName, + bool append) { + if (_writers.count(fileName) > 0) { + return StatusWithRotatableFileWriter(ErrorCodes::FileAlreadyOpen, + "File \"" + fileName + "\" already opened."); + } + std::auto_ptr<RotatableFileWriter> writer(new RotatableFileWriter); + RotatableFileWriter::Use writerUse(writer.get()); + Status status = writerUse.setFileName(fileName, append); + if (!status.isOK()) + return StatusWithRotatableFileWriter(status); + _writers.insert(std::make_pair(fileName, writer.get())); + return StatusWith<RotatableFileWriter*>(writer.release()); + } + + RotatableFileWriter* RotatableFileManager::getFile(const std::string& name) { + return mapFindWithDefault(_writers, name, static_cast<RotatableFileWriter*>(NULL)); + } + + RotatableFileManager::FileNameStatusPairVector RotatableFileManager::rotateAll( + const std::string& renameTargetSuffix) { + + FileNameStatusPairVector badStatuses; + for (WriterByNameMap::const_iterator iter = _writers.begin(); + iter != _writers.end(); ++iter) { + + Status status = RotatableFileWriter::Use(iter->second).rotate( + iter->first + renameTargetSuffix); + if (!status.isOK()) { + badStatuses.push_back(std::make_pair(iter->first, status)); + } + } + return badStatuses; + } + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/rotatable_file_manager.h b/src/mongo/logger/rotatable_file_manager.h new file mode 100644 index 00000000000..5ab377a5e33 --- /dev/null +++ b/src/mongo/logger/rotatable_file_manager.h @@ -0,0 +1,77 @@ +/* Copyright 2013 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> +#include <utility> +#include <vector> + +#include "mongo/base/disallow_copying.h" +#include "mongo/base/status.h" +#include "mongo/base/status_with.h" +#include "mongo/logger/rotatable_file_writer.h" +#include "mongo/platform/unordered_map.h" + +namespace mongo { +namespace logger { + + typedef StatusWith<RotatableFileWriter*> StatusWithRotatableFileWriter; + + /** + * Utility object that owns and manages rotation for RotatableFileWriters. + * + * Unlike RotatableFileWriter, this type leaves synchronization to its consumers. + */ + class RotatableFileManager { + MONGO_DISALLOW_COPYING(RotatableFileManager); + public: + typedef std::pair<std::string, Status> FileNameStatusPair; + typedef std::vector<FileNameStatusPair> FileNameStatusPairVector; + + RotatableFileManager(); + ~RotatableFileManager(); + + /** + * Opens "name" in mode "append" and returns a new RotatableFileWriter set to + * operate on the file. + * + * If the manager already has opened "name", returns ErrorCodes::FileAlreadyOpen. + * May also return failure codes issued by RotatableFileWriter::Use::setFileName(). + */ + StatusWithRotatableFileWriter openFile(const std::string& name, bool append); + + /** + * Gets a RotatableFileWriter for writing to "name", if the manager owns one, or NULL if + * not. + */ + RotatableFileWriter* getFile(const std::string& name); + + /** + * Rotates all managed files, renaming each file by appending "renameTargetSuffix". + * + * Returns a vector of <filename, Status> pairs for filenames with non-OK rotate status. + * An empty vector indicates that all files were rotated successfully. + */ + FileNameStatusPairVector rotateAll(const std::string& renameTargetSuffix); + + private: + typedef unordered_map<std::string, RotatableFileWriter*> WriterByNameMap; + + WriterByNameMap _writers; + }; + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/rotatable_file_writer.h b/src/mongo/logger/rotatable_file_writer.h index 7cc2b3df0c4..00cf8d65c24 100644 --- a/src/mongo/logger/rotatable_file_writer.h +++ b/src/mongo/logger/rotatable_file_writer.h @@ -34,6 +34,9 @@ namespace logger { * manipulation methods for the stream. For any instance of RotatableFileWriter, at most one * fully constructed instance of RotatableFileWriter::Use exists at a time, providing mutual * exclusion. + * + * Behavior is undefined if two instances of RotatableFileWriter should simultaneously have the + * same value for their fileName. */ class RotatableFileWriter { MONGO_DISALLOW_COPYING(RotatableFileWriter); diff --git a/src/mongo/logger/rotatable_file_writer_test.cpp b/src/mongo/logger/rotatable_file_writer_test.cpp index 767533498dc..bc690396443 100644 --- a/src/mongo/logger/rotatable_file_writer_test.cpp +++ b/src/mongo/logger/rotatable_file_writer_test.cpp @@ -45,9 +45,6 @@ namespace { TEST_F(RotatableFileWriterTest, RotationTest) { using namespace logger; - const std::string logFileName("LogTest_RotatableFileAppender.txt"); - const std::string logFileNameRotated("LogTest_RotatableFileAppender_Rotated.txt"); - { RotatableFileWriter writer; RotatableFileWriter::Use writerUse(&writer); diff --git a/src/mongo/logger/syslog_appender.h b/src/mongo/logger/syslog_appender.h new file mode 100644 index 00000000000..6ec5fcaf4e0 --- /dev/null +++ b/src/mongo/logger/syslog_appender.h @@ -0,0 +1,71 @@ +/* Copyright 2013 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 + +#ifndef _WIN32 // TODO(schwerin): Should be #if MONGO_CONFIG_HAVE_SYSLOG_H? + +#include <boost/scoped_ptr.hpp> +#include <sstream> +#include <syslog.h> + +#include "mongo/base/disallow_copying.h" +#include "mongo/base/status.h" +#include "mongo/logger/appender.h" +#include "mongo/logger/encoder.h" + +namespace mongo { +namespace logger { + + /** + * Appender for writing to syslog. Users must have separately called openlog(). + */ + template <typename Event> + class SyslogAppender : public Appender<Event> { + MONGO_DISALLOW_COPYING(SyslogAppender); + + public: + typedef Encoder<Event> EventEncoder; + + explicit SyslogAppender(EventEncoder* encoder) : _encoder(encoder) {} + virtual Status append(const Event& event) { + std::ostringstream os; + _encoder->encode(event, os); + if (!os) + return Status(ErrorCodes::LogWriteFailed, "Error writing log message to syslog."); + syslog(getSyslogPriority(event.getSeverity()), "%s", os.str().c_str()); + return Status::OK(); + } + + private: + int getSyslogPriority(LogSeverity severity) { + if (severity <= LogSeverity::Debug(1)) + return LOG_DEBUG; + if (severity == LogSeverity::Warning()) + return LOG_WARNING; + if (severity == LogSeverity::Error()) + return LOG_ERR; + if (severity >= LogSeverity::Severe()) + return LOG_CRIT; + // Info() and Log(). + return LOG_INFO; + } + boost::scoped_ptr<EventEncoder> _encoder; + }; + +} // namespace logger +} // namespace mongo + +#endif diff --git a/src/mongo/logger/tee.h b/src/mongo/logger/tee.h new file mode 100644 index 00000000000..d07addf931b --- /dev/null +++ b/src/mongo/logger/tee.h @@ -0,0 +1,35 @@ +/* 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> + +namespace mongo { +namespace logger { + + class Tee { + public: + virtual ~Tee() {} + + /** + * Implementations of Tee::write must handle their own synchronization. Callers may assume + * it is safe to call this method at any time from any thread. + */ + virtual void write(const std::string& str) = 0; + }; + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/s/chunk.cpp b/src/mongo/s/chunk.cpp index e0e8e9510e5..513ab618bea 100644 --- a/src/mongo/s/chunk.cpp +++ b/src/mongo/s/chunk.cpp @@ -417,11 +417,11 @@ namespace mongo { ChunkPtr toMove = cm->findIntersectingChunk(min); if ( ! (toMove->getMin() == min && toMove->getMax() == max) ){ - LOG(1) << "recently split chunk: " << range << " modified before we could migrate " << toMove << endl; + LOG(1).stream() << "recently split chunk: " << range << " modified before we could migrate " << toMove << endl; return true; } - log() << "moving chunk (auto): " << toMove << " to: " << newLocation.toString() << endl; + log().stream() << "moving chunk (auto): " << toMove << " to: " << newLocation.toString() << endl; BSONObj res; massert( 10412 , @@ -1346,7 +1346,7 @@ namespace mongo { } catch (...) { - LOG( LL_ERROR ) << "\t invalid ChunkRangeMap! printing ranges:" << endl; + error() << "\t invalid ChunkRangeMap! printing ranges:" << endl; for (ChunkRangeMap::const_iterator it=_ranges.begin(), end=_ranges.end(); it != end; ++it) cout << it->first << ": " << *it->second << endl; @@ -1456,7 +1456,8 @@ namespace mongo { cmdBuilder.append( "shardHost" , s.getConnString() ); BSONObj cmd = cmdBuilder.obj(); - LOG(1) << " setShardVersion " << s.getName() + LOG(1).stream() + << " setShardVersion " << s.getName() << " " << conn.getServerAddress() << " " << ns << " " << cmd diff --git a/src/mongo/s/commands_admin.cpp b/src/mongo/s/commands_admin.cpp index cdc0431238c..1d91b12cd40 100644 --- a/src/mongo/s/commands_admin.cpp +++ b/src/mongo/s/commands_admin.cpp @@ -667,7 +667,7 @@ namespace mongo { } } - tlog() << "CMD: shardcollection: " << cmdObj << endl; + MONGO_TLOG(0) << "CMD: shardcollection: " << cmdObj << endl; config->shardCollection( ns , proposedKey , careAboutUnique , &initSplits ); @@ -693,7 +693,8 @@ namespace mongo { BSONObj moveResult; if (!chunk->moveAndCommit(to, Chunk::MaxChunkSize, false, true, moveResult)) { - warning() << "Couldn't move chunk " << chunk << " to shard " << to + warning().stream() + << "Couldn't move chunk " << chunk << " to shard " << to << " while sharding collection " << ns << ". Reason: " << moveResult << endl; } @@ -715,9 +716,10 @@ namespace mongo { if ( ! subSplits.empty() ){ BSONObj splitResult; if ( ! currentChunk->multiSplit( subSplits , splitResult ) ){ - warning() << "Couldn't split chunk " << currentChunk - << " while sharding collection " << ns << ". Reason: " - << splitResult << endl; + warning().stream() + << "Couldn't split chunk " << currentChunk + << " while sharding collection " << ns << ". Reason: " + << splitResult << endl; } subSplits.clear(); } @@ -890,7 +892,7 @@ namespace mongo { } verify(chunk.get()); - log() << "splitting: " << ns << " shard: " << chunk << endl; + log().stream() << "splitting: " << ns << " shard: " << chunk << endl; BSONObj res; bool worked; @@ -1005,7 +1007,7 @@ namespace mongo { return false; } - tlog() << "CMD: movechunk: " << cmdObj << endl; + MONGO_TLOG(0) << "CMD: movechunk: " << cmdObj << endl; BSONObj res; if (!c->moveAndCommit(to, diff --git a/src/mongo/s/config.cpp b/src/mongo/s/config.cpp index 155bbc95c33..c811374233a 100644 --- a/src/mongo/s/config.cpp +++ b/src/mongo/s/config.cpp @@ -850,7 +850,7 @@ namespace mongo { } if ( up == 1 ) { - LOG( LL_WARNING ) << "only 1 config server reachable, continuing" << endl; + warning() << "only 1 config server reachable, continuing" << endl; return true; } @@ -870,7 +870,7 @@ namespace mongo { stringstream ss; ss << "config servers " << _config[firstGood] << " and " << _config[i] << " differ"; - LOG( LL_WARNING ) << ss.str() << endl; + warning() << ss.str() << endl; if ( tries <= 1 ) { ss << "\n" << c1 << "\t" << c2 << "\n" << d1 << "\t" << d2; errmsg = ss.str(); @@ -890,7 +890,7 @@ namespace mongo { if ( checkConsistency ) { string errmsg; if ( ! checkConfigServersConsistent( errmsg ) ) { - LOG( LL_ERROR ) << "could not verify that config servers are in sync" << causedBy(errmsg) << warnings; + error() << "could not verify that config servers are in sync" << causedBy(errmsg) << warnings; return false; } } diff --git a/src/mongo/s/cursors.cpp b/src/mongo/s/cursors.cpp index 446c8ae4f48..5bda5766fcc 100644 --- a/src/mongo/s/cursors.cpp +++ b/src/mongo/s/cursors.cpp @@ -167,16 +167,16 @@ namespace mongo { CursorCache::~CursorCache() { // TODO: delete old cursors? - bool print = logLevel > 0; + bool print = logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1)); if ( _cursors.size() || _refs.size() ) print = true; verify(_refs.size() == _refsNS.size()); if ( print ) - cout << " CursorCache at shutdown - " - << " sharded: " << _cursors.size() - << " passthrough: " << _refs.size() - << endl; + log() << " CursorCache at shutdown - " + << " sharded: " << _cursors.size() + << " passthrough: " << _refs.size() + << endl; } ShardedClientCursorPtr CursorCache::get( long long id ) const { @@ -276,7 +276,7 @@ namespace mongo { int n = *x++; if ( n > 2000 ) { - LOG( n < 30000 ? LL_WARNING : LL_ERROR ) << "receivedKillCursors, n=" << n << endl; + ( n < 30000 ? warning() : error() ) << "receivedKillCursors, n=" << n << endl; } @@ -291,7 +291,7 @@ namespace mongo { LOG(_myLogLevel) << "CursorCache::gotKillCursors id: " << id << endl; if ( ! id ) { - LOG( LL_WARNING ) << " got cursor id of 0 to kill" << endl; + warning() << " got cursor id of 0 to kill" << endl; continue; } @@ -311,7 +311,7 @@ namespace mongo { MapNormal::iterator refsIt = _refs.find(id); MapNormal::iterator refsNSIt = _refsNS.find(id); if (refsIt == _refs.end()) { - LOG( LL_WARNING ) << "can't find cursor: " << id << endl; + warning() << "can't find cursor: " << id << endl; continue; } verify(refsNSIt != _refsNS.end()); diff --git a/src/mongo/s/d_split.cpp b/src/mongo/s/d_split.cpp index 464b870f107..65427fec359 100644 --- a/src/mongo/s/d_split.cpp +++ b/src/mongo/s/d_split.cpp @@ -624,8 +624,8 @@ namespace mongo { result.append( "requestedMin" , min ); result.append( "requestedMax" , max ); - LOG( LL_WARNING ) << "aborted split because " << errmsg << ": " << min << "->" << max - << " is now " << currMin << "->" << currMax << endl; + warning() << "aborted split because " << errmsg << ": " << min << "->" << max + << " is now " << currMin << "->" << currMax << endl; return false; } @@ -634,8 +634,8 @@ namespace mongo { result.append( "from" , myShard.getName() ); result.append( "official" , shard ); - LOG( LL_WARNING ) << "aborted split because " << errmsg << ": chunk is at " << shard - << " and not at " << myShard.getName() << endl; + warning() << "aborted split because " << errmsg << ": chunk is at " << shard + << " and not at " << myShard.getName() << endl; return false; } @@ -644,8 +644,8 @@ namespace mongo { maxVersion.addToBSON( result, "officialVersion" ); shardingState.getVersion( ns ).addToBSON( result, "myVersion" ); - LOG( LL_WARNING ) << "aborted split because " << errmsg << ": official " << maxVersion - << " mine: " << shardingState.getVersion(ns) << endl; + warning() << "aborted split because " << errmsg << ": official " << maxVersion + << " mine: " << shardingState.getVersion(ns) << endl; return false; } diff --git a/src/mongo/s/grid.cpp b/src/mongo/s/grid.cpp index fa1abb2d151..15dd930ad0a 100644 --- a/src/mongo/s/grid.cpp +++ b/src/mongo/s/grid.cpp @@ -549,13 +549,10 @@ namespace mongo { return true; } - if ( logLevel ) { - stringstream ss; - ss << " now: " << now - << " startTime: " << startTime - << " stopTime: " << stopTime; - log() << "_inBalancingWindow: " << ss.str() << endl; - } + LOG(1).stream() << "_inBalancingWindow: " + << " now: " << now + << " startTime: " << startTime + << " stopTime: " << stopTime; // allow balancing if during the activeWindow // note that a window may be open during the night diff --git a/src/mongo/s/s_only.cpp b/src/mongo/s/s_only.cpp index 10b9eb43366..48824ab7da3 100644 --- a/src/mongo/s/s_only.cpp +++ b/src/mongo/s/s_only.cpp @@ -15,20 +15,21 @@ * limitations under the License. */ -#include "pch.h" +#include "mongo/pch.h" #include "mongo/client/connpool.h" #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/authorization_manager_global.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/auth/authz_session_external_state_s.h" -#include "mongo/s/shard.h" +#include "mongo/db/commands.h" +#include "mongo/db/dbhelpers.h" +#include "mongo/db/matcher.h" +#include "mongo/s/client_info.h" #include "mongo/s/grid.h" -#include "request.h" -#include "client_info.h" -#include "../db/dbhelpers.h" -#include "../db/matcher.h" -#include "../db/commands.h" +#include "mongo/s/request.h" +#include "mongo/s/shard.h" +#include "mongo/util/concurrency/thread_name.h" /* most a pile of hacks to make linking nicer diff --git a/src/mongo/s/server.cpp b/src/mongo/s/server.cpp index e5fb69bbe00..5d6d18c7c8d 100644 --- a/src/mongo/s/server.cpp +++ b/src/mongo/s/server.cpp @@ -44,6 +44,7 @@ #include "mongo/scripting/engine.h" #include "mongo/util/admin_access.h" #include "mongo/util/concurrency/task.h" +#include "mongo/util/concurrency/thread_name.h" #include "mongo/util/exception_filter_win32.h" #include "mongo/util/log.h" #include "mongo/util/net/message.h" @@ -228,7 +229,8 @@ namespace mongo { void init() { serverID.init(); - Logstream::get().addGlobalTee( new RamLog("global") ); + logger::globalLogDomain()->attachAppender( + logger::MessageLogDomain::AppenderAutoPtr(new RamLogAppender(new RamLog("global")))); } void start( const MessageServer::Options& opts ) { @@ -445,7 +447,7 @@ static void processCommandLineOptions(const std::vector<std::string>& argv) { } if ( params.count( "test" ) ) { - logLevel = 5; + ::mongo::logger::globalLogDomain()->setMinimumLoggedSeverity(::mongo::logger::LogSeverity::Debug(5)); StartupTest::runTests(); cout << "tests passed" << endl; ::_exit(EXIT_SUCCESS); diff --git a/src/mongo/s/strategy_shard.cpp b/src/mongo/s/strategy_shard.cpp index 62af3cc7f4b..2cd4df22900 100644 --- a/src/mongo/s/strategy_shard.cpp +++ b/src/mongo/s/strategy_shard.cpp @@ -587,7 +587,7 @@ namespace mongo { << "inserting " << group.inserts.size() << " documents to shard " - << group.shard + << group.shard->toString() << " at version " << (group.manager.get() ? group.manager->getVersion().toString() : diff --git a/src/mongo/s/strategy_single.cpp b/src/mongo/s/strategy_single.cpp index cc95e9cf890..7a3590936b5 100644 --- a/src/mongo/s/strategy_single.cpp +++ b/src/mongo/s/strategy_single.cpp @@ -210,7 +210,7 @@ namespace mongo { b.append( "err" , "can't do unlock through mongos" ); } else { - LOG( LL_WARNING ) << "unknown sys command [" << ns << "]" << endl; + warning() << "unknown sys command [" << ns << "]" << endl; return false; } diff --git a/src/mongo/s/version_manager.cpp b/src/mongo/s/version_manager.cpp index 1496ebc1407..bc146e49074 100644 --- a/src/mongo/s/version_manager.cpp +++ b/src/mongo/s/version_manager.cpp @@ -222,10 +222,11 @@ namespace mongo { "version is zero" ) ) << endl; } - LOG(2) << " have to set shard version for conn: " << conn->getServerAddress() << " ns:" << ns - << " my last seq: " << sequenceNumber << " current: " << officialSequenceNumber - << " version: " << version << " manager: " << manager.get() - << endl; + LOG(2).stream() + << " have to set shard version for conn: " << conn->getServerAddress() << " ns:" << ns + << " my last seq: " << sequenceNumber << " current: " << officialSequenceNumber + << " version: " << version << " manager: " << manager.get() + << endl; const string versionableServerAddress(conn->getServerAddress()); diff --git a/src/mongo/scripting/engine_v8.cpp b/src/mongo/scripting/engine_v8.cpp index 3e61236416a..527c9b0fd5c 100644 --- a/src/mongo/scripting/engine_v8.cpp +++ b/src/mongo/scripting/engine_v8.cpp @@ -17,6 +17,7 @@ #include "mongo/scripting/engine_v8.h" +#include "mongo/base/init.h" #include "mongo/platform/unordered_set.h" #include "mongo/scripting/v8_db.h" #include "mongo/scripting/v8_utils.h" @@ -303,78 +304,77 @@ namespace mongo { template <typename _GCState> void gcCallback(v8::GCType type, v8::GCCallbackFlags flags) { - const int verbosity = 1; // log level for stat collection - if (logLevel < verbosity) - // don't collect stats unless verbose - return; - - v8::HeapStatistics stats; - v8::V8::GetHeapStatistics(&stats); - log() << "V8 GC " << _GCState::name - << " heap stats - " - << " total: " << stats.total_heap_size() - << " exec: " << stats.total_heap_size_executable() - << " used: " << stats.used_heap_size()<< " limit: " - << stats.heap_size_limit() - << endl; - } - - V8ScriptEngine::V8ScriptEngine() : - _globalInterruptLock("GlobalV8InterruptLock"), - _opToScopeMap(), - _deadlineMonitor() { - } - - V8ScriptEngine::~V8ScriptEngine() { - } - - void ScriptEngine::setup() { - if (!globalScriptEngine) { - globalScriptEngine = new V8ScriptEngine(); - } - } - - std::string ScriptEngine::getInterpreterVersionString() { - return "V8 3.12.19"; - } - - void V8ScriptEngine::interrupt(unsigned opId) { - mongo::mutex::scoped_lock intLock(_globalInterruptLock); - OpIdToScopeMap::iterator iScope = _opToScopeMap.find(opId); - if (iScope == _opToScopeMap.end()) { - // got interrupt request for a scope that no longer exists - LOG(1) << "received interrupt request for unknown op: " << opId - << printKnownOps_inlock() << endl; - return; - } - LOG(1) << "interrupting op: " << opId << printKnownOps_inlock() << endl; - iScope->second->kill(); - } - - void V8ScriptEngine::interruptAll() { - mongo::mutex::scoped_lock interruptLock(_globalInterruptLock); - for (OpIdToScopeMap::iterator iScope = _opToScopeMap.begin(); - iScope != _opToScopeMap.end(); ++iScope) { - iScope->second->kill(); - } - } - - void V8Scope::registerOpId() { - scoped_lock giLock(_engine->_globalInterruptLock); - if (_engine->haveGetCurrentOpIdCallback()) { - // this scope has an associated operation - _opId = _engine->getCurrentOpId(); - _engine->_opToScopeMap[_opId] = this; - } - else - // no associated op id (e.g. running from shell) - _opId = 0; - LOG(2) << "V8Scope " << this << " registered for op " << _opId << endl; - } - - void V8Scope::unregisterOpId() { - scoped_lock giLock(_engine->_globalInterruptLock); - LOG(2) << "V8Scope " << this << " unregistered for op " << _opId << endl; + if (!logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1))) + // don't collect stats unless verbose + return; + + v8::HeapStatistics stats; + v8::V8::GetHeapStatistics(&stats); + log() << "V8 GC " << _GCState::name + << " heap stats - " + << " total: " << stats.total_heap_size() + << " exec: " << stats.total_heap_size_executable() + << " used: " << stats.used_heap_size()<< " limit: " + << stats.heap_size_limit() + << endl; + } + + V8ScriptEngine::V8ScriptEngine() : + _globalInterruptLock("GlobalV8InterruptLock"), + _opToScopeMap(), + _deadlineMonitor() { + } + + V8ScriptEngine::~V8ScriptEngine() { + } + + void ScriptEngine::setup() { + if (!globalScriptEngine) { + globalScriptEngine = new V8ScriptEngine(); + } + } + + std::string ScriptEngine::getInterpreterVersionString() { + return "V8 3.12.19"; + } + + void V8ScriptEngine::interrupt(unsigned opId) { + mongo::mutex::scoped_lock intLock(_globalInterruptLock); + OpIdToScopeMap::iterator iScope = _opToScopeMap.find(opId); + if (iScope == _opToScopeMap.end()) { + // got interrupt request for a scope that no longer exists + LOG(1) << "received interrupt request for unknown op: " << opId + << printKnownOps_inlock() << endl; + return; + } + LOG(1) << "interrupting op: " << opId << printKnownOps_inlock() << endl; + iScope->second->kill(); + } + + void V8ScriptEngine::interruptAll() { + mongo::mutex::scoped_lock interruptLock(_globalInterruptLock); + for (OpIdToScopeMap::iterator iScope = _opToScopeMap.begin(); + iScope != _opToScopeMap.end(); ++iScope) { + iScope->second->kill(); + } + } + + void V8Scope::registerOpId() { + scoped_lock giLock(_engine->_globalInterruptLock); + if (_engine->haveGetCurrentOpIdCallback()) { + // this scope has an associated operation + _opId = _engine->getCurrentOpId(); + _engine->_opToScopeMap[_opId] = this; + } + else + // no associated op id (e.g. running from shell) + _opId = 0; + LOG(2) << "V8Scope " << static_cast<const void*>(this) << " registered for op " << _opId << endl; + } + + void V8Scope::unregisterOpId() { + scoped_lock giLock(_engine->_globalInterruptLock); + LOG(2) << "V8Scope " << static_cast<const void*>(this) << " unregistered for op " << _opId << endl; if (_engine->haveGetCurrentOpIdCallback() || _opId != 0) { // scope is currently associated with an operation id V8ScriptEngine::OpIdToScopeMap::iterator it = _engine->_opToScopeMap.find(_opId); @@ -387,12 +387,12 @@ namespace mongo { v8::Locker l(_isolate); mongo::mutex::scoped_lock cbEnterLock(_interruptLock); if (v8::V8::IsExecutionTerminating(_isolate)) { - LOG(2) << "v8 execution interrupted. isolate: " << _isolate << endl; + LOG(2) << "v8 execution interrupted. isolate: " << static_cast<const void*>(_isolate) << endl; return false; } if (_pendingKill || globalScriptEngine->interrupted()) { // kill flag was set before entering our callback - LOG(2) << "marked for death while leaving callback. isolate: " << _isolate << endl; + LOG(2) << "marked for death while leaving callback. isolate: " << static_cast<const void*>(_isolate) << endl; v8::V8::TerminateExecution(_isolate); return false; } @@ -405,11 +405,11 @@ namespace mongo { mongo::mutex::scoped_lock cbLeaveLock(_interruptLock); _inNativeExecution = false; if (v8::V8::IsExecutionTerminating(_isolate)) { - LOG(2) << "v8 execution interrupted. isolate: " << _isolate << endl; + LOG(2) << "v8 execution interrupted. isolate: " << static_cast<const void*>(_isolate) << endl; return false; } if (_pendingKill || globalScriptEngine->interrupted()) { - LOG(2) << "marked for death while leaving callback. isolate: " << _isolate << endl; + LOG(2) << "marked for death while leaving callback. isolate: " << static_cast<const void*>(_isolate) << endl; v8::V8::TerminateExecution(_isolate); return false; } @@ -422,9 +422,9 @@ namespace mongo { // Set the TERMINATE flag on the stack guard for this isolate. // This won't happen between calls to nativePrologue and nativeEpilogue(). v8::V8::TerminateExecution(_isolate); - LOG(1) << "killing v8 scope. isolate: " << _isolate << endl; + LOG(1) << "killing v8 scope. isolate: " << static_cast<const void*>(_isolate) << endl; } - LOG(1) << "marking v8 scope for death. isolate: " << _isolate << endl; + LOG(1) << "marking v8 scope for death. isolate: " << static_cast<const void*>(_isolate) << endl; _pendingKill = true; } @@ -438,7 +438,7 @@ namespace mongo { */ std::string V8ScriptEngine::printKnownOps_inlock() { stringstream out; - if (logLevel > 1) { + if (logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(2))) { out << " known ops: " << endl; for(OpIdToScopeMap::iterator iSc = _opToScopeMap.begin(); iSc != _opToScopeMap.end(); ++iSc) { @@ -1665,8 +1665,10 @@ namespace mongo { // --- random utils ---- + static logger::MessageLogDomain* jsPrintLogDomain; v8::Handle<v8::Value> V8Scope::Print(V8Scope* scope, const v8::Arguments& args) { - stringstream ss; + LogstreamBuilder builder(jsPrintLogDomain, getThreadName(), logger::LogSeverity::Log()); + std::ostream& ss = builder.stream(); v8::HandleScope handle_scope; bool first = true; for (int i = 0; i < args.Length(); i++) { @@ -1690,7 +1692,6 @@ namespace mongo { ss << *str; } ss << "\n"; - Logstream::logLockless(ss.str()); return handle_scope.Close(v8::Undefined()); } @@ -1730,4 +1731,9 @@ namespace mongo { *v8::String::Utf8Value(args[0]->ToString()))); } + MONGO_INITIALIZER(JavascriptPrintDomain)(InitializerContext*) { + jsPrintLogDomain = logger::globalLogManager()->getNamedDomain("javascriptOutput"); + return Status::OK(); + } + } // namespace mongo diff --git a/src/mongo/scripting/engine_v8.h b/src/mongo/scripting/engine_v8.h index 7a005e7f05f..5b22c64df38 100644 --- a/src/mongo/scripting/engine_v8.h +++ b/src/mongo/scripting/engine_v8.h @@ -73,9 +73,10 @@ namespace mongo { * V8Scope is destructed. */ ~ObjTracker() { - if (!_container.empty()) + if (!_container.empty()) { LOG(1) << "freeing " << _container.size() << " uncollected " << typeid(_ObjType).name() << " objects" << endl; + } typename set<TrackedPtr*>::iterator it = _container.begin(); while (it != _container.end()) { delete *it; diff --git a/src/mongo/shell/dbshell.cpp b/src/mongo/shell/dbshell.cpp index c77ebb463c2..f396b399a22 100644 --- a/src/mongo/shell/dbshell.cpp +++ b/src/mongo/shell/dbshell.cpp @@ -28,6 +28,9 @@ #include "mongo/client/sasl_client_authenticate.h" #include "mongo/db/cmdline.h" #include "mongo/db/repl/rs_member.h" +#include "mongo/logger/console_appender.h" +#include "mongo/logger/logger.h" +#include "mongo/logger/message_event_utf8_encoder.h" #include "mongo/scripting/engine.h" #include "mongo/shell/linenoise.h" #include "mongo/shell/shell_utils.h" @@ -844,7 +847,7 @@ int _main( int argc, char* argv[], char **envp ) { mongo::enableIPv6(); } if ( params.count( "verbose" ) ) { - logLevel = 1; + logger::globalLogDomain()->setMinimumLoggedSeverity(logger::LogSeverity::Debug(1)); } if ( url == "*" ) { @@ -859,6 +862,11 @@ int _main( int argc, char* argv[], char **envp ) { mongo::runGlobalInitializersOrDie(argc, argv, envp); mongo::StartupTest::runTests(); + logger::globalLogManager()->getNamedDomain("javascriptOutput")->attachAppender( + logger::MessageLogDomain::AppenderAutoPtr( + new logger::ConsoleAppender<logger::MessageEventEphemeral>( + new logger::MessageEventUnadornedEncoder))); + if ( !nodb ) { // connect to db //if ( ! mongo::cmdLine.quiet ) cout << "url: " << url << endl; diff --git a/src/mongo/tools/bsondump.cpp b/src/mongo/tools/bsondump.cpp index 31019f26d0c..4c87460b0b3 100644 --- a/src/mongo/tools/bsondump.cpp +++ b/src/mongo/tools/bsondump.cpp @@ -109,7 +109,7 @@ public: else if ( e.type() == String && ! isValidUTF8( e.valuestr() ) ) { cout << prefix << "\t\t\t" << "bad utf8 String!" << endl; } - else if ( logLevel > 0 ) { + else if ( logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1)) ) { cout << prefix << "\t\t\t" << e << endl; } diff --git a/src/mongo/tools/tool.cpp b/src/mongo/tools/tool.cpp index ed94e860cc9..8ca34f7d8d2 100644 --- a/src/mongo/tools/tool.cpp +++ b/src/mongo/tools/tool.cpp @@ -176,13 +176,13 @@ namespace mongo { ::_exit(0); } - if ( _params.count( "verbose" ) ) { - logLevel = 1; + if (_params.count("verbose")) { + logger::globalLogDomain()->setMinimumLoggedSeverity(logger::LogSeverity::Debug(1)); } - for (string s = "vv"; s.length() <= 10; s.append("v")) { + for (string s = "vv"; s.length() <= 12; s.append("v")) { if (_params.count(s)) { - logLevel = s.length(); + logger::globalLogDomain()->setMinimumLoggedSeverity(logger::LogSeverity::Debug(s.length())); } } @@ -498,7 +498,7 @@ namespace mongo { posix_fadvise(fileno(file), 0, fileLength, POSIX_FADV_SEQUENTIAL); #endif - if (!_quiet && logLevel >= 1) { + if (!_quiet && logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1))) { (_usesstdout ? cout : cerr ) << "\t file size: " << fileLength << endl; } diff --git a/src/mongo/unittest/crutch.cpp b/src/mongo/unittest/crutch.cpp index bae6f6a30a9..232086527b2 100644 --- a/src/mongo/unittest/crutch.cpp +++ b/src/mongo/unittest/crutch.cpp @@ -32,7 +32,6 @@ namespace mongo { StartupTest::StartupTest() {} StartupTest::~StartupTest() {} bool inShutdown() { return false; } - std::string getThreadName() { return "UNKNOWN"; } void setLastError(int code, const char* msg) {} bool StaticObserver::_destroyingStatics = false; } // namespace mongo diff --git a/src/mongo/unittest/unittest.cpp b/src/mongo/unittest/unittest.cpp index 0f51c016232..a891b7a10f7 100644 --- a/src/mongo/unittest/unittest.cpp +++ b/src/mongo/unittest/unittest.cpp @@ -21,6 +21,11 @@ #include <iostream> #include <map> +#include "mongo/base/init.h" +#include "mongo/logger/console_appender.h" +#include "mongo/logger/log_manager.h" +#include "mongo/logger/message_event_utf8_encoder.h" +#include "mongo/logger/message_log_domain.h" #include "mongo/util/assert_util.h" #include "mongo/util/log.h" @@ -29,6 +34,8 @@ namespace mongo { namespace unittest { namespace { + logger::MessageLogDomain* unittestOutput = + logger::globalLogManager()->getNamedDomain("unittest"); typedef std::map<std::string, Suite*> SuiteMap; inline SuiteMap& _allSuites() { @@ -38,6 +45,20 @@ namespace mongo { } // namespace + logger::LogstreamBuilder log() { + return LogstreamBuilder(unittestOutput, getThreadName(), logger::LogSeverity::Log()); + } + + MONGO_INITIALIZER_WITH_PREREQUISITES(UnitTestOutput, ("GlobalLogManager", "default"))( + InitializerContext*) { + + unittestOutput->attachAppender( + logger::MessageLogDomain::AppenderAutoPtr( + new logger::ConsoleAppender<logger::MessageLogDomain::Event>( + new logger::MessageEventDetailsEncoder))); + return Status::OK(); + } + class Result { public: Result( const std::string& name ) : _name( name ) , _rc(0) , _tests(0) , _fails(0) , _asserts(0) {} diff --git a/src/mongo/unittest/unittest.h b/src/mongo/unittest/unittest.h index ef5faee4e73..3f35e2aba73 100644 --- a/src/mongo/unittest/unittest.h +++ b/src/mongo/unittest/unittest.h @@ -33,6 +33,7 @@ #include <boost/scoped_ptr.hpp> #include <boost/shared_ptr.hpp> +#include "mongo/logger/logstream_builder.h" #include "mongo/util/assert_util.h" #include "mongo/util/mongoutils/str.h" @@ -170,6 +171,12 @@ namespace mongo { class Result; /** + * Gets a LogstreamBuilder for logging to the unittest log domain, which may have + * different target from the global log domain. + */ + mongo::logger::LogstreamBuilder log(); + + /** * Type representing the function composing a test. */ typedef boost::function<void (void)> TestFunction; diff --git a/src/mongo/util/background.cpp b/src/mongo/util/background.cpp index 9c20829669d..347f8b1bdd5 100644 --- a/src/mongo/util/background.cpp +++ b/src/mongo/util/background.cpp @@ -24,6 +24,7 @@ #include "mongo/util/concurrency/mutex.h" #include "mongo/util/concurrency/spin_lock.h" +#include "mongo/util/concurrency/thread_name.h" #include "mongo/util/mongoutils/str.h" #include "mongo/util/net/ssl_manager.h" #include "mongo/util/time_support.h" @@ -65,10 +66,10 @@ namespace mongo { run(); } catch ( std::exception& e ) { - LOG( LL_ERROR ) << "backgroundjob " << name() << " exception: " << e.what() << endl; + error() << "backgroundjob " << name() << " exception: " << e.what() << endl; } catch(...) { - LOG( LL_ERROR ) << "uncaught exception in BackgroundJob " << name() << endl; + error() << "uncaught exception in BackgroundJob " << name() << endl; } { diff --git a/src/mongo/util/concurrency/SConscript b/src/mongo/util/concurrency/SConscript new file mode 100644 index 00000000000..60aaa5ef9e0 --- /dev/null +++ b/src/mongo/util/concurrency/SConscript @@ -0,0 +1,8 @@ +# -*- mode: python -*- + +Import("env") + +env.StaticLibrary('thread_name', + ['thread_name.cpp'], + LIBDEPS=['$BUILD_DIR/mongo/base/base', + '$BUILD_DIR/third_party/shim_boost']) diff --git a/src/mongo/util/concurrency/mutexdebugger.cpp b/src/mongo/util/concurrency/mutexdebugger.cpp index 873cedd3683..a9f0eb00d46 100644 --- a/src/mongo/util/concurrency/mutexdebugger.cpp +++ b/src/mongo/util/concurrency/mutexdebugger.cpp @@ -15,9 +15,11 @@ * limitations under the License. */ -#include "pch.h" -#include "mutex.h" -#include "value.h" +#include "mongo/pch.h" + +#include "mongo/logger/logger.h" +#include "mongo/util/concurrency/mutex.h" + namespace mongo { @@ -43,7 +45,8 @@ namespace mongo { } void MutexDebugger::programEnding() { - if( logLevel>=1 && followers.size() ) { + using logger::LogSeverity; + if(logger::globalLogDomain()->shouldLog(LogSeverity::Debug(1)) && followers.size()) { std::cout << followers.size() << " mutexes in program" << endl; for( map< mid, set<mid> >::iterator i = followers.begin(); i != followers.end(); i++ ) { cout << i->first; diff --git a/src/mongo/util/concurrency/thread_name.cpp b/src/mongo/util/concurrency/thread_name.cpp new file mode 100644 index 00000000000..f25cccd9874 --- /dev/null +++ b/src/mongo/util/concurrency/thread_name.cpp @@ -0,0 +1,77 @@ +/* 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. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/platform/compiler.h" +#include "mongo/util/concurrency/thread_name.h" + +#include <boost/thread/tss.hpp> + +namespace mongo { + +namespace { + boost::thread_specific_ptr<std::string> _threadName; + +#if defined(_WIN32) + +#define MS_VC_EXCEPTION 0x406D1388 +#pragma pack(push,8) + typedef struct tagTHREADNAME_INFO { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. + } THREADNAME_INFO; +#pragma pack(pop) + + void setWinThreadName(const char *name) { + /* is the sleep here necessary??? + Sleep(10); + */ + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = -1; + info.dwFlags = 0; + __try { + RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); + } + __except(EXCEPTION_EXECUTE_HANDLER) { + } + } +#endif + +} // namespace + + void setThreadName(StringData name) { + _threadName.reset(new string(name.rawData(), name.size())); + +#if defined( DEBUG ) && defined( _WIN32 ) + // naming might be expensive so don't do "conn*" over and over + setWinThreadName(_threadName.get()->c_str()); +#endif + + } + + const std::string& getThreadName() { + std::string* s; + while (!(s = _threadName.get())) { + setThreadName(""); + } + return *s; + } + +} // namespace mongo diff --git a/src/mongo/util/concurrency/thread_name.h b/src/mongo/util/concurrency/thread_name.h new file mode 100644 index 00000000000..4a32789dfef --- /dev/null +++ b/src/mongo/util/concurrency/thread_name.h @@ -0,0 +1,35 @@ +/* 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> + +#include "mongo/base/string_data.h" + +namespace mongo { + + /** + * Sets the name of the current thread to "name". + */ + void setThreadName(StringData name); + + /** + * Retrieves the name of the current thread, as previously set, or "" if no name was previously + * set. + */ + const std::string& getThreadName(); + +} // namespace mongo diff --git a/src/mongo/util/exception_filter_win32.cpp b/src/mongo/util/exception_filter_win32.cpp index 252f59bde96..9b11e1fb0aa 100644 --- a/src/mongo/util/exception_filter_win32.cpp +++ b/src/mongo/util/exception_filter_win32.cpp @@ -24,6 +24,7 @@ #include "mongo/util/exit_code.h" #include "mongo/util/log.h" #include "mongo/util/stacktrace.h" +#include "mongo/util/text.h" namespace mongo { diff --git a/src/mongo/util/file_allocator.cpp b/src/mongo/util/file_allocator.cpp index e59bce6934b..2e7091fc6fa 100644 --- a/src/mongo/util/file_allocator.cpp +++ b/src/mongo/util/file_allocator.cpp @@ -37,6 +37,7 @@ #endif #include "mongo/platform/posix_fadvise.h" +#include "mongo/util/concurrency/thread_name.h" #include "mongo/util/mongoutils/str.h" #include "mongo/util/paths.h" #include "mongo/util/time_support.h" diff --git a/src/mongo/util/goodies.h b/src/mongo/util/goodies.h index 8b1c6c1dfe7..de449b2ccba 100644 --- a/src/mongo/util/goodies.h +++ b/src/mongo/util/goodies.h @@ -29,9 +29,6 @@ namespace mongo { /* @return a dump of the buffer as hex byte ascii output */ string hexdump(const char *data, unsigned len); - void setThreadName(const char * name); - string getThreadName(); - template<class T> inline string ToString(const T& t) { stringstream s; diff --git a/src/mongo/util/log.cpp b/src/mongo/util/log.cpp index 9ed7c224f6e..34421f3aecb 100644 --- a/src/mongo/util/log.cpp +++ b/src/mongo/util/log.cpp @@ -1,6 +1,3 @@ -/** @file log.cpp - */ - /* Copyright 2009 10gen Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,37 +13,32 @@ * limitations under the License. */ -#include "mongo/pch.h" +#include "mongo/platform/basic.h" -#include "mongo/platform/posix_fadvise.h" +#include "mongo/util/log.h" + +#include "mongo/logger/ramlog.h" +#include "mongo/logger/rotatable_file_manager.h" #include "mongo/util/assert_util.h" #include "mongo/util/concurrency/threadlocal.h" +#include "mongo/util/concurrency/thread_name.h" #include "mongo/util/stacktrace.h" +#include "mongo/util/text.h" #include "mongo/util/time_support.h" using namespace std; -#ifdef _WIN32 -# include <io.h> -# include <fcntl.h> -# include "mongo/util/text.h" -#else -# include <cxxabi.h> -# include <sys/file.h> -#endif - -#ifdef _WIN32 -# define dup2 _dup2 // Microsoft headers use ISO C names -# define fileno _fileno -#endif - -#include <boost/filesystem/operations.hpp> +// TODO: Win32 unicode console writing (in logger/console_appender?). +// TODO: Extra log context appending, and re-enable log_user_*.js +// TODO: Eliminate cout/cerr. +// TODO: LogIndent (for mongodump). +// TODO: Eliminate rawOut. namespace mongo { - static Logstream::ExtraLogContextFn _appendExtraLogContext; + static logger::ExtraLogContextFn _appendExtraLogContext; - Status Logstream::registerExtraLogContextFn(ExtraLogContextFn contextFn) { + Status logger::registerExtraLogContextFn(logger::ExtraLogContextFn contextFn) { if (!contextFn) return Status(ErrorCodes::BadValue, "Cannot register a NULL log context function."); if (_appendExtraLogContext) { @@ -57,183 +49,19 @@ namespace mongo { return Status::OK(); } - int logLevel = 0; int tlogLevel = 0; // test log level. so we avoid overchattiness (somewhat) in the c++ unit tests - mongo::mutex Logstream::mutex("Logstream"); - int Logstream::doneSetup = Logstream::magicNumber(); const char *default_getcurns() { return ""; } const char * (*getcurns)() = default_getcurns; - Nullstream nullstream; - vector<Tee*>* Logstream::globalTees = 0; - - TSP_DECLARE(Logstream, Logstream_tsp); - TSP_DEFINE(Logstream, Logstream_tsp); - - Nullstream& tlog( int level ) { - if ( !debug && level > tlogLevel ) - return nullstream; - if ( level > logLevel ) - return nullstream; - return Logstream::get().prolog(); - } - - class LoggingManager { - public: - LoggingManager() - : _enabled(0) , _file(0) { - } - - bool start( const string& lp , bool append ) { - uassert( 10268 , "LoggingManager already started" , ! _enabled ); - _append = append; - - bool exists = boost::filesystem::exists(lp); - bool isdir = boost::filesystem::is_directory(lp); - bool isreg = boost::filesystem::is_regular(lp); - - if ( exists ) { - if ( isdir ) { - cout << "logpath [" << lp << "] should be a filename, not a directory" << endl; - return false; - } - - if ( ! append ) { - // only attempt rename if log is regular file - if ( isreg ) { - stringstream ss; - ss << lp << "." << terseCurrentTime( false ); - string s = ss.str(); - - if ( ! rename( lp.c_str() , s.c_str() ) ) { - cout << "log file [" << lp - << "] exists; copied to temporary file [" << s << "]" << endl; - } else { - cout << "log file [" << lp - << "] exists and couldn't make backup [" << s - << "]; run with --logappend or manually remove file: " - << errnoWithDescription() << endl; - return false; - } - } - } - } - // test path - FILE * test = fopen( lp.c_str() , _append ? "a" : "w" ); - if ( ! test ) { - cout << "can't open [" << lp << "] for log file: " << errnoWithDescription() << endl; - return false; - } - - if (append && exists){ - // two blank lines before and after - const string msg = "\n\n***** SERVER RESTARTED *****\n\n\n"; - massert(14036, errnoWithPrefix("couldn't write to log file"), - fwrite(msg.data(), 1, msg.size(), test) == msg.size()); - } - - fclose( test ); - - _path = lp; - _enabled = 1; - return rotate(); - } - - bool rotate() { - if ( ! _enabled ) { - cout << "logRotate is not possible: loggingManager not enabled" << endl; - return true; - } - - if ( _file ) { - -#ifdef POSIX_FADV_DONTNEED - posix_fadvise(fileno(_file), 0, 0, POSIX_FADV_DONTNEED); -#endif - - // Rename the (open) existing log file to a timestamped name - stringstream ss; - ss << _path << "." << terseCurrentTime( false ); - string s = ss.str(); - if (0 != rename(_path.c_str(), s.c_str())) { - error() << "failed to rename '" << _path - << "' to '" << s - << "': " << errnoWithDescription() << endl; - return false; - } - } - - FILE* tmp = 0; // The new file using the original logpath name - -#if _WIN32 - // We rename an open log file (above, on next rotation) and the trick to getting Windows to do that is - // to open the file with FILE_SHARE_DELETE. So, we can't use the freopen() call that non-Windows - // versions use because it would open the file without the FILE_SHARE_DELETE flag we need. - // - HANDLE newFileHandle = CreateFileA( - _path.c_str(), - GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL - ); - if ( INVALID_HANDLE_VALUE != newFileHandle ) { - int newFileDescriptor = _open_osfhandle( reinterpret_cast<intptr_t>(newFileHandle), _O_APPEND ); - tmp = _fdopen( newFileDescriptor, _append ? "a" : "w" ); - } -#else - tmp = freopen(_path.c_str(), _append ? "a" : "w", stdout); -#endif - if ( !tmp ) { - error() << "can't open: " << _path.c_str() << " for log file" << endl; - return false; - } - - // redirect stdout and stderr to log file - dup2( fileno( tmp ), 1 ); // stdout - dup2( fileno( tmp ), 2 ); // stderr - - Logstream::setLogFile(tmp); // after this point no thread will be using old file - -#if _WIN32 - if ( _file ) - fclose( _file ); // In Windows, we still have the old file open, close it now -#endif - -#if 0 // enable to test redirection - cout << "written to cout" << endl; - cerr << "written to cerr" << endl; - log() << "written to log()" << endl; -#endif - - _file = tmp; // Save new file for next rotation - return true; - } - - private: - bool _enabled; - string _path; - bool _append; - FILE * _file; - - } loggingManager; - - bool initLogging( const string& lp , bool append ) { - cout << "all output going to: " << lp << endl; - return loggingManager.start( lp , append ); - } - bool rotateLogs() { - return loggingManager.rotate(); + using logger::RotatableFileManager; + RotatableFileManager* manager = logger::globalRotatableFileManager(); + RotatableFileManager::FileNameStatusPairVector result( + manager->rotateAll("." + terseCurrentTime(false))); + return result.empty(); } - // done *before* static initialization - FILE* Logstream::logfile = stdout; - bool Logstream::isSyslog = false; - string errnoWithDescription(int x) { #if defined(_WIN32) if( x < 0 ) @@ -282,178 +110,34 @@ namespace mongo { return s.str(); } - void Logstream::logLockless( const StringData& s ) { - - if ( s.size() == 0 ) - return; - - if ( doneSetup == 1717 ) { - -#if defined(_WIN32) - int fd = fileno( logfile ); - if ( _isatty( fd ) ) { - fflush( logfile ); - writeUtf8ToWindowsConsole( s.rawData(), s.size() ); - return; - } + /* + * NOTE(schwerin): Called from signal handlers; should not be taking locks or allocating + * memory. + */ + void rawOut(const StringData &s) { + for (size_t i = 0; i < s.size(); ++i) { +#ifdef _WIN32 + putc(s[i], stdout); #else - if ( isSyslog ) { - syslog( LOG_INFO , "%s" , s.rawData() ); - return; - } + putc_unlocked(s[i], stdout); #endif - - if (fwrite(s.rawData(), s.size(), 1, logfile)) { - fflush(logfile); - } - else { - int x = errno; - cout << "Failed to write to logfile: " << errnoWithDescription(x) << endl; - } - } - else { - cout << s; - cout.flush(); } } - void Logstream::flush(Tee *t) { - const size_t MAX_LOG_LINE = 1024 * 10; - - // this ensures things are sane - if ( doneSetup == 1717 ) { - string msg = ss.str(); - - string threadName = getThreadName(); - const char * type = logLevelToString(logLevel); - - size_t msgLen = msg.size(); - if ( msgLen > MAX_LOG_LINE ) - msgLen = MAX_LOG_LINE; - - const int spaceNeeded = (int)( msgLen + 300 /* for extra info */ + threadName.size()); - - BufBuilder b(spaceNeeded); - char* dateStr = b.grow(24); - curTimeString(dateStr); - dateStr[23] = ' '; // change null char to space - - 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 ); - } - - if (_appendExtraLogContext) - _appendExtraLogContext(b); - - if ( msg.size() > MAX_LOG_LINE ) { - stringstream sss; - sss << "warning: log line attempted (" << msg.size() / 1024 << "k) over max size(" << MAX_LOG_LINE / 1024 << "k)"; - sss << ", printing beginning and end ... "; - b.appendStr( sss.str(), false ); - const char * xx = msg.c_str(); - b.appendBuf( xx , MAX_LOG_LINE / 3 ); - b.appendStr( " .......... ", false ); - b.appendStr( xx + msg.size() - ( MAX_LOG_LINE / 3 ) ); - } - else { - 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); - } -#if defined(_WIN32) - int fd = fileno( logfile ); - if ( _isatty( fd ) ) { - fflush( logfile ); - writeUtf8ToWindowsConsole( out.data(), out.size() ); - } -#else - if ( isSyslog ) { - syslog( logLevelToSysLogLevel(logLevel) , "%s" , out.data() ); - } -#endif - else 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(); - } - - void Logstream::removeGlobalTee( Tee * tee ) { - if ( !globalTees ) { - return; - } - for( std::vector<Tee*>::iterator i = globalTees->begin(); i != globalTees->end(); ++i ) { - if ( *i == tee ) { - globalTees->erase( i ); - return; - } + void logContext(const char *errmsg) { + if ( errmsg ) { + problem() << errmsg << endl; } + printStackTrace(problem().stream()); } - void Logstream::setLogFile(FILE* f) { - scoped_lock lk(mutex); - logfile = f; + LogIndentLevel::LogIndentLevel() { } - - Logstream& Logstream::get() { - if ( StaticObserver::_destroyingStatics ) { - cout << "Logstream::get called in uninitialized state" << endl; - } - Logstream *p = Logstream_tsp.get(); - if( p == 0 ) - Logstream_tsp.reset( p = new Logstream() ); - return *p; - } - - /* note: can't use malloc herein - may be in signal handler. - logLockless() likely does not comply and should still be fixed todo - likewise class string? - */ - void rawOut( const string &s ) { - if( s.empty() ) return; - char buf[64]; - curTimeString(buf); - buf[23] = ' '; - buf[24] = 0; - - Logstream::logLockless(buf); - Logstream::logLockless(s); - Logstream::logLockless("\n"); + LogIndentLevel::~LogIndentLevel() { } - void logContext(const char *errmsg) { - if ( errmsg ) { - problem() << errmsg << endl; - } - printStackTrace(); - } + Tee* const warnings = new RamLog("warnings"); // Things put here go in serverStatus + Tee* const startupWarningsLog = new RamLog("startupWarnings"); // intentionally leaked -} +} // namespace mongo diff --git a/src/mongo/util/log.h b/src/mongo/util/log.h index eb31d317e25..942e9cbffe0 100644 --- a/src/mongo/util/log.h +++ b/src/mongo/util/log.h @@ -17,461 +17,118 @@ #pragma once -#include <string.h> -#include <sstream> -#include <errno.h> -#include <vector> -#include <boost/shared_ptr.hpp> -#include <boost/scoped_ptr.hpp> -#include <boost/thread/tss.hpp> - #include "mongo/base/status.h" #include "mongo/bson/util/builder.h" -#include "mongo/util/concurrency/mutex.h" -#include "mongo/util/debug_util.h" -#include "mongo/util/exit_code.h" - -#ifndef _WIN32 -#include <syslog.h> -#endif +#include "mongo/logger/logger.h" +#include "mongo/logger/logstream_builder.h" +#include "mongo/logger/tee.h" +#include "mongo/util/concurrency/thread_name.h" namespace mongo { - enum ExitCode; - - // using negative numbers so these are always less than ::mongo::loglevel (see MONGO_LOG) - enum LogLevel { LL_DEBUG=-1000 , 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_EMERG; - 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+( const std::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; - }; - - inline bool operator<( const LabeledLevel& ll, const int i ) { return ll.getLevel() < i; } - inline bool operator<( const int i, const LabeledLevel& ll ) { return i < ll.getLevel(); } - inline bool operator>( const LabeledLevel& ll, const int i ) { return ll.getLevel() > i; } - inline bool operator>( const int i, const LabeledLevel& ll ) { return i > ll.getLevel(); } - inline bool operator<=( const LabeledLevel& ll, const int i ) { return ll.getLevel() <= i; } - inline bool operator<=( const int i, const LabeledLevel& ll ) { return i <= ll.getLevel(); } - inline bool operator>=( const LabeledLevel& ll, const int i ) { return ll.getLevel() >= i; } - inline bool operator>=( const int i, const LabeledLevel& ll ) { return i >= ll.getLevel(); } - inline bool operator==( const LabeledLevel& ll, const int i ) { return ll.getLevel() == i; } - inline bool operator==( const int i, const LabeledLevel& ll ) { return i == ll.getLevel(); } - - 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 boost::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<< (std::ostream& ( *endl )(std::ostream&)) { - return *this; - } - virtual Nullstream& operator<< (std::ios_base& (*hex)(std::ios_base&)) { - return *this; - } - - virtual void flush(Tee *t = 0) {} - }; - extern Nullstream nullstream; - - class Logstream : public Nullstream { - static mongo::mutex mutex; - static int doneSetup; - std::stringstream ss; - int indent; - LogLevel logLevel; - static FILE* logfile; - static boost::scoped_ptr<std::ostream> stream; - static std::vector<Tee*> * globalTees; - static bool isSyslog; - public: - // Type for optional function for inserting context information in log messages. See - // registerExtraLogContextFn, below. - typedef void (*ExtraLogContextFn)(BufBuilder& builder); - - static void logLockless( const StringData& s ); - - static void setLogFile(FILE* f); -#ifndef _WIN32 - static void useSyslog(const char * name) { - std::cout << "using syslog ident: " << name << std::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_PID | LOG_CONS | 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; - } - - void flush(Tee *t = 0); - - inline Nullstream& setLogLevel(LogLevel l) { - logLevel = l; - return *this; - } +namespace logger { + typedef void (*ExtraLogContextFn)(BufBuilder& builder); + Status registerExtraLogContextFn(ExtraLogContextFn contextFn); - /** 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; 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; } +} // namespace logger - Logstream& operator<<(const LazyString& x) { - ss << x.val(); - return *this; - } - Nullstream& operator<< (Tee* tee) { - ss << '\n'; - flush(tee); - return *this; - } - Logstream& operator<< (std::ostream& ( *_endl )(std::ostream&)) { - ss << '\n'; - flush(0); - return *this; - } - Logstream& operator<< (std::ios_base& (*_hex)(std::ios_base&)) { - ss << _hex; - return *this; - } + using logger::LogstreamBuilder; + using logger::LabeledLevel; + using logger::Tee; - Logstream& prolog() { - return *this; - } - - void addGlobalTee( Tee * t ) { - if ( ! globalTees ) - globalTees = new std::vector<Tee*>(); - globalTees->push_back( t ); - } - - void removeGlobalTee( Tee * tee ); - - void indentInc(){ indent++; } - void indentDec(){ indent--; } - int getIndent() const { return indent; } - - // Replace any pre-existing function for appending context information to log lines with - // "contextFn". Returns Status::OK on first call, and ErrorCodes::AlreadyInitialized if - // called more than once. Returns ErrorCodes::BadValue if contextFn is NULL. - static Status registerExtraLogContextFn(ExtraLogContextFn contextFn); - - private: - Logstream() { - indent = 0; - _init(); - } - void _init() { - ss.str(""); - logLevel = LL_INFO; - } - - public: - static Logstream& get(); - }; - - extern int logLevel; - extern int tlogLevel; - - inline Nullstream& out( int level = 0 ) { - if ( level > logLevel ) - return nullstream; - return Logstream::get(); + /** + * Returns a LogstreamBuilder for logging a message with LogSeverity::Severe(). + */ + inline LogstreamBuilder severe() { + return LogstreamBuilder(logger::globalLogDomain(), + getThreadName(), + logger::LogSeverity::Severe()); } - /* 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); + /** + * Returns a LogstreamBuilder for logging a message with LogSeverity::Error(). + */ + inline LogstreamBuilder error() { + return LogstreamBuilder(logger::globalLogDomain(), + getThreadName(), + logger::LogSeverity::Error()); } - /* without prolog */ - inline Nullstream& _log( int level = 0 ) { - if ( level > logLevel ) - return nullstream; - return Logstream::get(); + /** + * Returns a LogstreamBuilder for logging a message with LogSeverity::Warning(). + */ + inline LogstreamBuilder warning() { + return LogstreamBuilder(logger::globalLogDomain(), + getThreadName(), + logger::LogSeverity::Warning()); } - /** logging which we may not want during unit tests (dbtests) runs. - set tlogLevel to -1 to suppress tlog() output in a test program. */ - Nullstream& tlog( int level = 0 ); - - // 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; + /** + * Returns a LogstreamBuilder for logging a message with LogSeverity::Log(). + */ + inline LogstreamBuilder log() { + return LogstreamBuilder(logger::globalLogDomain(), + getThreadName(), + logger::LogSeverity::Log()); } -#define MONGO_LOG(level) \ - ( MONGO_likely( ::mongo::logLevel < (level) ) ) \ - ? ::mongo::nullstream : ::mongo::logWithLevel(level) -#define LOG MONGO_LOG - inline Nullstream& log() { - return Logstream::get().prolog(); - } + /** + * Synonym for log(). + */ + inline LogstreamBuilder out() { return log(); } - // Use MONGO_LOG() instead of this - inline Nullstream& logWithLevel( int level ) { - if ( level > logLevel ) - return nullstream; - return Logstream::get().prolog(); - } + /** + * For logging which we may not want during unit tests (dbtests) runs. Set tlogLevel to -1 to + * suppress MONGO_TLOG() output in a test program. + */ + extern int tlogLevel; - inline Nullstream& logWithLevel( LogLevel l ) { - return Logstream::get().prolog().setLogLevel( l ); - } +#define MONGO_LOG(DLEVEL) \ + if (!(::mongo::logger::globalLogDomain())->shouldLog(::mongo::LogstreamBuilder::severityCast(DLEVEL))) {} \ + else LogstreamBuilder(::mongo::logger::globalLogDomain(), getThreadName(), ::mongo::LogstreamBuilder::severityCast(DLEVEL)) - inline Nullstream& logWithLevel( const LabeledLevel& ll ) { - Nullstream& stream = logWithLevel( ll.getLevel() ); - if( ll.getLabel() != "" ) - stream << "[" << ll.getLabel() << "] "; - return stream; - } +#define LOG MONGO_LOG - inline Nullstream& error() { - return logWithLevel( LL_ERROR ); - } +#define MONGO_DLOG(DLEVEL) \ + if (!(DEBUG_BUILD) && !::mongo::logger::globalLogDomain()->shouldLog(::mongo::LogstreamBuilder::severityCast(DLEVEL))) {} \ + else LogstreamBuilder(::mongo::logger::globalLogDomain(), getThreadName(), ::mongo::LogstreamBuilder::severityCast(DLEVEL)) - inline Nullstream& warning() { - return logWithLevel( LL_WARNING ); - } +#define MONGO_TLOG(DLEVEL) \ + if ((!::mongo::debug && ((DLEVEL) > tlogLevel)) || !::mongo::logger::globalLogDomain()->shouldLog(::mongo::LogstreamBuilder::severityCast(DLEVEL))) {} \ + else LogstreamBuilder(::mongo::logger::globalLogDomain(), getThreadName(), ::mongo::LogstreamBuilder::severityCast(DLEVEL)) /* 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; + inline LogstreamBuilder problem() { + std::string curns = getcurns(); + curns.push_back(' '); + return log().setBaseMessage(curns); } /** - log to a file rather than stdout - defined in assert_util.cpp + * Rotates the log files. Returns true if all logs rotate successfully. */ - bool initLogging( const string& logpath , bool append ); bool rotateLogs(); - std::string toUtf8String(const std::wstring& wide); - /** output the error # and error message with prefix. handy for use as parm in uassert/massert. */ string errnoWithPrefix( const char * prefix ); + // Guard that alters the indentation level used by log messages on the current thread. + // Used only by mongodump (mongo/tools/dump.cpp). Do not introduce new uses. struct LogIndentLevel { - LogIndentLevel(){ - Logstream::get().indentInc(); - } - ~LogIndentLevel(){ - Logstream::get().indentDec(); - } + LogIndentLevel(); + ~LogIndentLevel(); }; extern Tee* const warnings; // Things put here go in serverStatus extern Tee* const startupWarningsLog; // Things put here get reported in MMS string errnoWithDescription(int errorcode = -1); - void rawOut( const string &s ); + void rawOut( const StringData &s ); /** * Write the current context (backtrace), along with the optional "msg". diff --git a/src/mongo/util/net/message_port.cpp b/src/mongo/util/net/message_port.cpp index 42f8b9dbd92..fa87dccb0e7 100644 --- a/src/mongo/util/net/message_port.cpp +++ b/src/mongo/util/net/message_port.cpp @@ -205,7 +205,10 @@ again: } catch ( const SocketException & e ) { - LOG(psock->getLogLevel() + (e.shouldPrint() ? 0 : 1) ) << "SocketException: remote: " << remote() << " error: " << e << endl; + logger::LogSeverity severity = psock->getLogLevel(); + if (!e.shouldPrint()) + severity = severity.lessSevere(); + LOG(severity) << "SocketException: remote: " << remote() << " error: " << e << endl; m.reset(); return false; } diff --git a/src/mongo/util/net/message_server_port.cpp b/src/mongo/util/net/message_server_port.cpp index a972fa495be..eeec3cc3263 100644 --- a/src/mongo/util/net/message_server_port.cpp +++ b/src/mongo/util/net/message_server_port.cpp @@ -26,6 +26,7 @@ #include "mongo/db/lasterror.h" #include "mongo/db/stats/counters.h" #include "mongo/util/concurrency/ticketholder.h" +#include "mongo/util/concurrency/thread_name.h" #include "mongo/util/net/listen.h" #include "mongo/util/net/message.h" #include "mongo/util/net/message_port.h" @@ -174,7 +175,7 @@ namespace mongo { } verify( inPort ); - inPort->psock->setLogLevel(1); + inPort->psock->setLogLevel(logger::LogSeverity::Debug(1)); scoped_ptr<MessagingPort> p( inPort ); string otherSide; diff --git a/src/mongo/util/net/sock.cpp b/src/mongo/util/net/sock.cpp index 1206e29f347..ab9a56703a6 100644 --- a/src/mongo/util/net/sock.cpp +++ b/src/mongo/util/net/sock.cpp @@ -56,7 +56,7 @@ namespace mongo { struct timeval tv; tv.tv_sec = (int)secs; tv.tv_usec = (int)((long long)(secs*1000*1000) % (1000*1000)); - bool report = logLevel > 3; // solaris doesn't provide these + bool report = logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(4)); DEV report = true; #if defined(_WIN32) tv.tv_sec *= 1000; // Windows timeout is a DWORD, in milliseconds. @@ -194,7 +194,7 @@ namespace mongo { if( target != "0.0.0.0" ) { // don't log if this as it is a // CRT construction and log() may not work yet. log() << "getaddrinfo(\"" << target << "\") failed: " << - gai_strerror(ret) << endl; + getAddrInfoStrError(ret) << endl; } *this = SockAddr(port); } @@ -392,13 +392,11 @@ namespace mongo { // ------------ Socket ----------------- Socket::Socket(int fd , const SockAddr& remote) : - _fd(fd), _remote(remote), _timeout(0), _lastValidityCheckAtSecs(time(0)) { - _logLevel = 0; + _fd(fd), _remote(remote), _timeout(0), _lastValidityCheckAtSecs(time(0)), _logLevel(logger::LogSeverity::Log()) { _init(); } - Socket::Socket( double timeout, int ll ) { - _logLevel = ll; + Socket::Socket( double timeout, logger::LogSeverity ll ) : _logLevel(ll) { _fd = -1; _timeout = timeout; _lastValidityCheckAtSecs = time(0); @@ -657,9 +655,10 @@ namespace mongo { continue; } - if ( len <= 4 && ret != len ) + if ( len <= 4 && ret != len ) { LOG(_logLevel) << "Socket recv() got " << ret << " bytes wanted len=" << len << endl; + } fassert(16508, ret <= len); len -= ret; buf += ret; diff --git a/src/mongo/util/net/sock.h b/src/mongo/util/net/sock.h index 6b59e03dc0f..b661c76ed0b 100644 --- a/src/mongo/util/net/sock.h +++ b/src/mongo/util/net/sock.h @@ -39,6 +39,7 @@ #include <openssl/ssl.h> #endif +#include "mongo/logger/log_severity.h" #include "mongo/platform/compiler.h" namespace mongo { @@ -188,7 +189,7 @@ namespace mongo { Generally you don't want a timeout, you should be very prepared for errors if you set one. */ - Socket(double so_timeout = 0, int logLevel = 0 ); + Socket(double so_timeout = 0, logger::LogSeverity logLevel = logger::LogSeverity::Log() ); ~Socket(); @@ -202,8 +203,8 @@ namespace mongo { void recv( char * data , int len ); int unsafe_recv( char *buf, int max ); - int getLogLevel() const { return _logLevel; } - void setLogLevel( int ll ) { _logLevel = ll; } + logger::LogSeverity getLogLevel() const { return _logLevel; } + void setLogLevel( logger::LogSeverity ll ) { _logLevel = ll; } SockAddr remoteAddr() const { return _remote; } string remoteString() const { return _remote.toString(); } @@ -267,7 +268,7 @@ namespace mongo { SSL* _ssl; SSLManagerInterface* _sslManager; #endif - int _logLevel; // passed to log() when logging errors + logger::LogSeverity _logLevel; // passed to log() when logging errors }; diff --git a/src/mongo/util/progress_meter.cpp b/src/mongo/util/progress_meter.cpp index 57ea9f728ec..360657f4a8e 100644 --- a/src/mongo/util/progress_meter.cpp +++ b/src/mongo/util/progress_meter.cpp @@ -53,13 +53,11 @@ namespace mongo { if ( _total > 0 ) { int per = (int)( ( (double)_done * 100.0 ) / (double)_total ); - Nullstream& out = log() << "\t\t" << _name << ": " << _done - << '/' << _total << '\t' << per << '%'; - + LogstreamBuilder out = log(); + out << "\t\t" << _name << ": " << _done << '/' << _total << '\t' << per << '%'; if ( ! _units.empty() ) { out << "\t(" << _units << ")"; } - out << endl; } _lastTime = t; diff --git a/src/mongo/util/ramlog.h b/src/mongo/util/ramlog.h index afa5e0390c1..fde33bb9b08 100644 --- a/src/mongo/util/ramlog.h +++ b/src/mongo/util/ramlog.h @@ -1,5 +1,3 @@ -// ramlog.h - /* Copyright 2009 10gen Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,52 +15,4 @@ #pragma once -#include "mongo/util/concurrency/mutex.h" -#include "mongo/util/log.h" - -namespace mongo { - - class RamLog : public Tee { - public: - RamLog( const std::string& name ); - - virtual void write(LogLevel ll, const string& str); - - void get( vector<const char*>& v) const; - - void toHTML(stringstream& s); - - static RamLog* get( const std::string& name ); - static void getNames( vector<string>& names ); - - time_t lastWrite() { return _lastWrite; } // 0 if no writes - long long getTotalLinesWritten() const { return _totalLinesWritten; } - - protected: - static int repeats(const vector<const char *>& v, int i); - static string clean(const vector<const char *>& v, int i, string line=""); - static string color(const std::string& line); - - /* turn http:... into an anchor */ - static string linkify(const char *s); - - private: - ~RamLog(); // want this private as we want to leak so we can use them till the very end - - enum { - N = 1024, // number of lines - C = 512 // max size of line - }; - char lines[N][C]; - unsigned h; // current position - unsigned n; // number of lines stores 0 o N - string _name; - long long _totalLinesWritten; - - typedef map<string,RamLog*> RM; - static mongo::mutex* _namedLock; - static RM* _named; - time_t _lastWrite; - }; - -} +#include "mongo/logger/ramlog.h" diff --git a/src/mongo/util/signal_handlers.cpp b/src/mongo/util/signal_handlers.cpp index 241d824b1a0..019fdb18935 100644 --- a/src/mongo/util/signal_handlers.cpp +++ b/src/mongo/util/signal_handlers.cpp @@ -104,14 +104,12 @@ namespace mongo { } void printStackAndExit( int signalNum ) { - int fd = Logstream::getLogDesc(); + const int fd = 0; - if ( fd >= 0 ) { - formattedWrite( fd , "Received signal %d\n" , signalNum ); - formattedWrite( fd , "Backtrace: " ); - formattedBacktrace( fd ); - formattedWrite( fd , "===\n" ); - } + formattedWrite( fd , "Received signal %d\n" , signalNum ); + formattedWrite( fd , "Backtrace: " ); + formattedBacktrace( fd ); + formattedWrite( fd , "===\n" ); ::_exit( EXIT_ABRUPT ); } diff --git a/src/mongo/util/stacktrace.cpp b/src/mongo/util/stacktrace.cpp index c1348a00240..eee96e52174 100644 --- a/src/mongo/util/stacktrace.cpp +++ b/src/mongo/util/stacktrace.cpp @@ -13,6 +13,8 @@ * limitations under the License. */ +#include "mongo/platform/basic.h" + #include "mongo/util/stacktrace.h" #include <cstdlib> @@ -22,13 +24,13 @@ #include <vector> #include "mongo/util/log.h" +#include "mongo/util/concurrency/mutex.h" #ifdef _WIN32 #include <boost/filesystem/operations.hpp> #include <boost/smart_ptr/scoped_array.hpp> #include <sstream> #include <stdio.h> -#include "mongo/platform/windows_basic.h" #include <DbgHelp.h> #include "mongo/util/assert_util.h" #else diff --git a/src/mongo/util/stacktrace.h b/src/mongo/util/stacktrace.h index f1cd6279107..289ab230c5e 100644 --- a/src/mongo/util/stacktrace.h +++ b/src/mongo/util/stacktrace.h @@ -21,16 +21,16 @@ #include <iostream> -#include "mongo/platform/basic.h" +#include "mongo/util/log.h" namespace mongo { - // Print stack trace information to "os", default to std::cout. - void printStackTrace(std::ostream &os=std::cout); + // Print stack trace information to "os", default to the log stream. + void printStackTrace(std::ostream &os=log().stream()); #if defined(_WIN32) - // Print stack trace (using a specified stack context) to "os", default to std::cout. - void printWindowsStackTrace(CONTEXT &context, std::ostream &os=std::cout); + // Print stack trace (using a specified stack context) to "os", default to the log stream. + void printWindowsStackTrace(CONTEXT &context, std::ostream &os=log().stream()); // Print error message from C runtime followed by stack trace int crtDebugCallback(int, char* originalMessage, int*); diff --git a/src/mongo/util/time_support.h b/src/mongo/util/time_support.h index b5e077412e8..f448628f0f0 100644 --- a/src/mongo/util/time_support.h +++ b/src/mongo/util/time_support.h @@ -32,8 +32,8 @@ namespace mongo { * * Wed Oct 31 13:34:47.996 * - * @param timeStr pointer to the buffer to set the string - should at least be - * 24 bytes big. + * @param timeStr pointer to the buffer to set the string - empirically, 64 bytes is enough for + * the buffer, 26 is not. */ void curTimeString(char* timeStr); diff --git a/src/mongo/util/util.cpp b/src/mongo/util/util.cpp index fbeedcc379c..da29c1f320f 100644 --- a/src/mongo/util/util.cpp +++ b/src/mongo/util/util.cpp @@ -43,57 +43,6 @@ namespace mongo { return s; } - boost::thread_specific_ptr<string> _threadName; - -#if defined(_WIN32) - -#define MS_VC_EXCEPTION 0x406D1388 -#pragma pack(push,8) - typedef struct tagTHREADNAME_INFO { - DWORD dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). - DWORD dwThreadID; // Thread ID (-1=caller thread). - DWORD dwFlags; // Reserved for future use, must be zero. - } THREADNAME_INFO; -#pragma pack(pop) - - void setWinThreadName(const char *name) { - /* is the sleep here necessary??? - Sleep(10); - */ - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = name; - info.dwThreadID = -1; - info.dwFlags = 0; - __try { - RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); - } - __except(EXCEPTION_EXECUTE_HANDLER) { - } - } -#endif - - void setThreadName(const char *name) { - if ( ! name ) - name = "NONE"; - - _threadName.reset( new string(name) ); - -#if defined( DEBUG ) && defined( _WIN32 ) - // naming might be expensive so don't do "conn*" over and over - setWinThreadName(name); -#endif - - } - - string getThreadName() { - string * s = _threadName.get(); - if ( s ) - return *s; - return ""; - } - bool isPrime(int n) { int z = 2; while ( 1 ) { diff --git a/src/mongo/util/version.cpp b/src/mongo/util/version.cpp index 4c62a41a468..12b8f0208de 100644 --- a/src/mongo/util/version.cpp +++ b/src/mongo/util/version.cpp @@ -166,9 +166,6 @@ namespace mongo { result.appendNumber("maxBsonObjectSize", BSONObjMaxUserSize); } - - Tee* const startupWarningsLog = new RamLog("startupWarnings"); //intentionally leaked - // // system warnings // |