diff options
-rw-r--r-- | src/mongo/logger/SConscript | 3 | ||||
-rw-r--r-- | src/mongo/logger/log_function_test.cpp | 119 | ||||
-rw-r--r-- | src/mongo/logger/log_test.cpp | 108 | ||||
-rw-r--r-- | src/mongo/logger/log_test.h | 88 | ||||
-rw-r--r-- | src/mongo/util/log.h | 96 |
5 files changed, 348 insertions, 66 deletions
diff --git a/src/mongo/logger/SConscript b/src/mongo/logger/SConscript index 482d753595e..4febd815244 100644 --- a/src/mongo/logger/SConscript +++ b/src/mongo/logger/SConscript @@ -32,6 +32,9 @@ env.Program('console_test', 'console_test.cpp', env.CppUnitTest('log_test', 'log_test.cpp', LIBDEPS=['logger', '$BUILD_DIR/mongo/foundation']) +env.CppUnitTest('log_function_test', 'log_function_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/log_function_test.cpp b/src/mongo/logger/log_function_test.cpp new file mode 100644 index 00000000000..112ac9892bd --- /dev/null +++ b/src/mongo/logger/log_function_test.cpp @@ -0,0 +1,119 @@ +/* Copyright 2013 10gen Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects + * for all of the code used other than as permitted herein. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you do not + * wish to do so, delete this exception statement from your version. If you + * delete this exception statement from all source files in the program, + * then also delete it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/logger/log_test.h" + +#include <sstream> +#include <string> +#include <vector> + +#include "mongo/logger/appender.h" +#include "mongo/logger/encoder.h" +#include "mongo/logger/log_component.h" +#include "mongo/logger/message_event_utf8_encoder.h" +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kReplication +#include "mongo/util/log.h" +#include "mongo/util/mongoutils/str.h" + +using namespace mongo::logger; + +namespace mongo { +namespace { + + typedef LogTest<MessageEventDetailsEncoder> LogTestDetailsEncoder; + + // Constants for log component test cases. + const LogComponent componentA = LogComponent::kCommands; + const LogComponent componentB = MONGO_LOG_DEFAULT_COMPONENT; + + // Tests pass through of log component: + // unconditional log functions -> LogStreamBuilder -> MessageEventEphemeral + // -> MessageEventDetailsEncoder + // MONGO_DEFAULT_LOG_COMPONENT is set to kReplication before including util/log.h + // so non-debug logging without explicit component will log with kReplication instead + // of kDefault. + TEST_F(LogTestDetailsEncoder, LogFunctionsOverrideGlobalComponent) { + // severe() - no component specified. + severe() << "This is logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_NOT_EQUALS(_logLines[0].find(str::stream() << " F " << componentB.getNameForLog()), + std::string::npos); + + // severe() - with component. + _logLines.clear(); + severe(componentA) << "This is logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_NOT_EQUALS(_logLines[0].find(str::stream() << " F " << componentA.getNameForLog()), + std::string::npos); + + // error() - no component specified. + _logLines.clear(); + error() << "This is logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_NOT_EQUALS(_logLines[0].find(str::stream() << " E " << componentB.getNameForLog()), + std::string::npos); + + // error() - with component. + _logLines.clear(); + error(componentA) << "This is logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_NOT_EQUALS(_logLines[0].find(str::stream() << " E " << componentA.getNameForLog()), + std::string::npos); + + // warning() - no component specified. + _logLines.clear(); + warning() << "This is logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_NOT_EQUALS(_logLines[0].find(str::stream() << " W " << componentB.getNameForLog()), + std::string::npos); + + // warning() - with component. + _logLines.clear(); + warning(componentA) << "This is logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_NOT_EQUALS(_logLines[0].find(str::stream() << " W " << componentA.getNameForLog()), + std::string::npos); + + // log() - no component specified. + _logLines.clear(); + log() << "This is logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_NOT_EQUALS(_logLines[0].find(str::stream() << " I " << componentB.getNameForLog()), + std::string::npos); + + // log() - with component. + _logLines.clear(); + log(componentA) << "This is logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_NOT_EQUALS(_logLines[0].find(str::stream() << " I " << componentA.getNameForLog()), + std::string::npos); + } + +} // namespace +} // namespace mongo diff --git a/src/mongo/logger/log_test.cpp b/src/mongo/logger/log_test.cpp index 0837b51b753..777e8d19b39 100644 --- a/src/mongo/logger/log_test.cpp +++ b/src/mongo/logger/log_test.cpp @@ -27,6 +27,8 @@ #include "mongo/platform/basic.h" +#include "mongo/logger/log_test.h" + #include <sstream> #include <string> #include <vector> @@ -50,48 +52,6 @@ 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. - template <typename MessageEventEncoder> - class LogTest : public unittest::Test { - friend class LogTestAppender; - public: - LogTest() : _severityOld(globalLogDomain()->getMinimumLogSeverity()) { - globalLogDomain()->clearAppenders(); - _appenderHandle = globalLogDomain()->attachAppender( - MessageLogDomain::AppenderAutoPtr(new LogTestAppender(this))); - } - - virtual ~LogTest() { - globalLogDomain()->detachAppender(_appenderHandle); - globalLogDomain()->setMinimumLoggedSeverity(_severityOld); - } - - protected: - std::vector<std::string> _logLines; - LogSeverity _severityOld; - - 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; - MessageEventEncoder _encoder; - }; - - MessageLogDomain::AppenderHandle _appenderHandle; - }; - typedef LogTest<MessageEventDetailsEncoder> LogTestDetailsEncoder; typedef LogTest<MessageEventUnadornedEncoder> LogTestUnadornedEncoder; @@ -590,5 +550,69 @@ namespace { ASSERT_EQUALS(_logLines[0].find(componentC.getNameForLog().toString()), std::string::npos); } + // Tests pass through of log component: + // unconditional log functions -> LogStreamBuilder -> MessageEventEphemeral + // -> MessageEventDetailsEncoder + TEST_F(LogTestDetailsEncoder, LogFunctions) { + // severe() - no component specified. + severe() << "This is logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_NOT_EQUALS(_logLines[0].find( + str::stream() << " F " << componentDefault.getNameForLog()), + std::string::npos); + + // severe() - with component. + _logLines.clear(); + severe(componentA) << "This is logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_NOT_EQUALS(_logLines[0].find(str::stream() << " F " << componentA.getNameForLog()), + std::string::npos); + + // error() - no component specified. + _logLines.clear(); + error() << "This is logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_NOT_EQUALS(_logLines[0].find( + str::stream() << " E " << componentDefault.getNameForLog()), + std::string::npos); + + // error() - with component. + _logLines.clear(); + error(componentA) << "This is logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_NOT_EQUALS(_logLines[0].find(str::stream() << " E " << componentA.getNameForLog()), + std::string::npos); + + // warning() - no component specified. + _logLines.clear(); + warning() << "This is logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_NOT_EQUALS(_logLines[0].find( + str::stream() << " W " << componentDefault.getNameForLog()), + std::string::npos); + + // warning() - with component. + _logLines.clear(); + warning(componentA) << "This is logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_NOT_EQUALS(_logLines[0].find(str::stream() << " W " << componentA.getNameForLog()), + std::string::npos); + + // log() - no component specified. + _logLines.clear(); + log() << "This is logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_NOT_EQUALS(_logLines[0].find( + str::stream() << " I " << componentDefault.getNameForLog()), + std::string::npos); + + // log() - with component. + _logLines.clear(); + log(componentA) << "This is logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_NOT_EQUALS(_logLines[0].find(str::stream() << " I " << componentA.getNameForLog()), + std::string::npos); + } + } // namespace } // namespace mongo diff --git a/src/mongo/logger/log_test.h b/src/mongo/logger/log_test.h new file mode 100644 index 00000000000..f0b2f8c7efd --- /dev/null +++ b/src/mongo/logger/log_test.h @@ -0,0 +1,88 @@ +/* Copyright 2013 10gen Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects + * for all of the code used other than as permitted herein. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you do not + * wish to do so, delete this exception statement from your version. If you + * delete this exception statement from all source files in the program, + * then also delete it in the license file. + */ + +#pragma once + +#include <sstream> +#include <string> +#include <vector> + +#include "mongo/base/status.h" +#include "mongo/logger/appender.h" +#include "mongo/logger/log_severity.h" +#include "mongo/logger/logger.h" +#include "mongo/logger/message_log_domain.h" +#include "mongo/unittest/unittest.h" + +namespace mongo { +namespace logger { + + // Used for testing logging framework only. + // TODO(schwerin): Have logger write to a different log from the global log, so that tests can + // redirect their global log output for examination. + template <typename MessageEventEncoder> + class LogTest : public unittest::Test { + friend class LogTestAppender; + public: + LogTest() : _severityOld(globalLogDomain()->getMinimumLogSeverity()) { + globalLogDomain()->clearAppenders(); + _appenderHandle = globalLogDomain()->attachAppender( + MessageLogDomain::AppenderAutoPtr(new LogTestAppender(this))); + } + + virtual ~LogTest() { + globalLogDomain()->detachAppender(_appenderHandle); + globalLogDomain()->setMinimumLoggedSeverity(_severityOld); + } + + protected: + std::vector<std::string> _logLines; + LogSeverity _severityOld; + + 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; + MessageEventEncoder _encoder; + }; + + MessageLogDomain::AppenderHandle _appenderHandle; + }; + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/util/log.h b/src/mongo/util/log.h index f63f858ada4..b8043e0dda1 100644 --- a/src/mongo/util/log.h +++ b/src/mongo/util/log.h @@ -37,6 +37,32 @@ #include "mongo/logger/tee.h" #include "mongo/util/concurrency/thread_name.h" +/** + * Defines default log component for MONGO_LOG. + * Use this macro inside an implementation namespace or code block where debug messages + * are logged using MONGO_LOG(). + * + * Note: Do not use more than once inside any namespace/code block. + */ +#define MONGO_LOG_DEFAULT_COMPONENT_FILE(COMPONENT) \ + static const ::mongo::logger::LogComponent MongoLogDefaultComponent_component = (COMPONENT); + +/** + * MONGO_LOG_DEFAULT_COMPONENT for local code block. + */ +#define MONGO_LOG_DEFAULT_COMPONENT_LOCAL(COMPONENT) \ + const ::mongo::logger::LogComponent MongoLogDefaultComponent_component = (COMPONENT); + +// Provide log component in global scope so that MONGO_LOG will always have a valid component. +// Global log component will be kDefault unless overridden by MONGO_LOG_DEFAULT_COMPONENT. +#if defined(MONGO_LOG_DEFAULT_COMPONENT) +const ::mongo::logger::LogComponent MongoLogDefaultComponent_component = + MONGO_LOG_DEFAULT_COMPONENT; +#else +const ::mongo::logger::LogComponent MongoLogDefaultComponent_component = + ::mongo::logger::LogComponent::kDefault; +#endif // MONGO_LOG_DEFAULT_COMPONENT + namespace mongo { namespace logger { @@ -45,6 +71,8 @@ namespace logger { } // namespace logger +namespace { + using logger::LogstreamBuilder; using logger::LabeledLevel; using logger::Tee; @@ -55,7 +83,15 @@ namespace logger { inline LogstreamBuilder severe() { return LogstreamBuilder(logger::globalLogDomain(), getThreadName(), - logger::LogSeverity::Severe()); + logger::LogSeverity::Severe(), + ::MongoLogDefaultComponent_component); + } + + inline LogstreamBuilder severe(logger::LogComponent component) { + return LogstreamBuilder(logger::globalLogDomain(), + getThreadName(), + logger::LogSeverity::Severe(), + component); } /** @@ -64,7 +100,15 @@ namespace logger { inline LogstreamBuilder error() { return LogstreamBuilder(logger::globalLogDomain(), getThreadName(), - logger::LogSeverity::Error()); + logger::LogSeverity::Error(), + ::MongoLogDefaultComponent_component); + } + + inline LogstreamBuilder error(logger::LogComponent component) { + return LogstreamBuilder(logger::globalLogDomain(), + getThreadName(), + logger::LogSeverity::Error(), + component); } /** @@ -73,7 +117,15 @@ namespace logger { inline LogstreamBuilder warning() { return LogstreamBuilder(logger::globalLogDomain(), getThreadName(), - logger::LogSeverity::Warning()); + logger::LogSeverity::Warning(), + ::MongoLogDefaultComponent_component); + } + + inline LogstreamBuilder warning(logger::LogComponent component) { + return LogstreamBuilder(logger::globalLogDomain(), + getThreadName(), + logger::LogSeverity::Warning(), + component); } /** @@ -82,9 +134,25 @@ namespace logger { inline LogstreamBuilder log() { return LogstreamBuilder(logger::globalLogDomain(), getThreadName(), - logger::LogSeverity::Log()); + logger::LogSeverity::Log(), + ::MongoLogDefaultComponent_component); } + inline LogstreamBuilder log(logger::LogComponent component) { + return LogstreamBuilder(logger::globalLogDomain(), + getThreadName(), + logger::LogSeverity::Log(), + component); + } + + inline LogstreamBuilder log(logger::LogComponent::Value componentValue) { + return LogstreamBuilder(logger::globalLogDomain(), + getThreadName(), + logger::LogSeverity::Log(), + componentValue); + } + +} // namespace // MONGO_LOG uses log component from MongoLogDefaultComponent from current or global namespace. #define MONGO_LOG(DLEVEL) \ @@ -142,23 +210,3 @@ namespace logger { } // namespace mongo -/** - * Defines default log component for MONGO_LOG. - * Use this macro inside an implementation namespace or code block where debug messages - * are logged using MONGO_LOG(). - * - * Note: Do not use more than once inside any namespace/code block. - * Using static function instead of enum to support use inside function code block. - */ -#define MONGO_LOG_DEFAULT_COMPONENT_FILE(COMPONENT) \ - static const ::mongo::logger::LogComponent MongoLogDefaultComponent_component = (COMPONENT); - -/** - * MONGO_LOG_DEFAULT_COMPONENT for local code block. - */ -#define MONGO_LOG_DEFAULT_COMPONENT_LOCAL(COMPONENT) \ - const ::mongo::logger::LogComponent MongoLogDefaultComponent_component = (COMPONENT); - -// Provide log component in global scope so that MONGO_LOG will always have a valid component. -const ::mongo::logger::LogComponent MongoLogDefaultComponent_component = - ::mongo::logger::LogComponent::kDefault; |