From 4d00dd9723f84f197afffd901101276916d1ee6c Mon Sep 17 00:00:00 2001 From: Benety Goh Date: Tue, 10 Jun 2014 18:50:40 -0400 Subject: SERVER-5092 support log tags for LOG() messages --- src/mongo/logger/SConscript | 3 + src/mongo/logger/log_domain-impl.h | 2 +- src/mongo/logger/log_domain.h | 19 +- src/mongo/logger/log_manager.h | 6 +- src/mongo/logger/log_tag.cpp | 57 ++++++ src/mongo/logger/log_tag.h | 72 +++++++ src/mongo/logger/log_tag_settings.cpp | 105 +++++++++++ src/mongo/logger/log_tag_settings.h | 97 ++++++++++ src/mongo/logger/log_test.cpp | 281 +++++++++++++++++++++++++++- src/mongo/logger/logger.h | 2 +- src/mongo/logger/tag_message_log_domain.cpp | 82 ++++++++ src/mongo/logger/tag_message_log_domain.h | 84 +++++++++ src/mongo/util/log.h | 37 +++- 13 files changed, 821 insertions(+), 26 deletions(-) create mode 100644 src/mongo/logger/log_tag.cpp create mode 100644 src/mongo/logger/log_tag.h create mode 100644 src/mongo/logger/log_tag_settings.cpp create mode 100644 src/mongo/logger/log_tag_settings.h create mode 100644 src/mongo/logger/tag_message_log_domain.cpp create mode 100644 src/mongo/logger/tag_message_log_domain.h (limited to 'src/mongo') diff --git a/src/mongo/logger/SConscript b/src/mongo/logger/SConscript index 624c2d7a923..926d3c68350 100644 --- a/src/mongo/logger/SConscript +++ b/src/mongo/logger/SConscript @@ -7,10 +7,13 @@ env.Library('logger', 'console.cpp', 'log_manager.cpp', 'log_severity.cpp', + 'log_tag.cpp', + 'log_tag_settings.cpp', 'logger.cpp', 'logstream_builder.cpp', 'message_event_utf8_encoder.cpp', 'message_log_domain.cpp', + 'tag_message_log_domain.cpp', 'ramlog.cpp', 'rotatable_file_manager.cpp', 'rotatable_file_writer.cpp', diff --git a/src/mongo/logger/log_domain-impl.h b/src/mongo/logger/log_domain-impl.h index cc5df19afd1..67ff6bb2f1a 100644 --- a/src/mongo/logger/log_domain-impl.h +++ b/src/mongo/logger/log_domain-impl.h @@ -43,7 +43,7 @@ namespace logger { template LogDomain::LogDomain() - : _minimumLoggedSeverity(LogSeverity::Log()), _abortOnFailure(false) + : _abortOnFailure(false) {} template diff --git a/src/mongo/logger/log_domain.h b/src/mongo/logger/log_domain.h index 0d788c462e7..f55f04dd6f4 100644 --- a/src/mongo/logger/log_domain.h +++ b/src/mongo/logger/log_domain.h @@ -42,7 +42,7 @@ namespace logger { /** * Logging domain for events of type E. * - * A logging domain consists of a set of Appenders and a minimum severity. + * A logging domain consists of a set of Appenders. * * 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 @@ -95,22 +95,6 @@ namespace logger { */ Status 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; } - /** * Gets the state of the abortOnFailure flag. */ @@ -147,7 +131,6 @@ namespace logger { private: typedef std::vector AppenderVector; - LogSeverity _minimumLoggedSeverity; AppenderVector _appenders; bool _abortOnFailure; }; diff --git a/src/mongo/logger/log_manager.h b/src/mongo/logger/log_manager.h index 7b60d2feaed..bdef7870b9b 100644 --- a/src/mongo/logger/log_manager.h +++ b/src/mongo/logger/log_manager.h @@ -30,7 +30,7 @@ #include #include "mongo/base/disallow_copying.h" -#include "mongo/logger/message_log_domain.h" +#include "mongo/logger/tag_message_log_domain.h" #include "mongo/logger/rotatable_file_writer.h" #include "mongo/platform/unordered_map.h" @@ -51,7 +51,7 @@ namespace logger { /** * Gets the global domain for this manager. It has no name. */ - MessageLogDomain* getGlobalDomain() { return &_globalDomain; } + TagMessageLogDomain* getGlobalDomain() { return &_globalDomain; } /** * Get the log domain with the given name, creating if needed. @@ -62,7 +62,7 @@ namespace logger { typedef unordered_map DomainsByNameMap; DomainsByNameMap _domains; - MessageLogDomain _globalDomain; + TagMessageLogDomain _globalDomain; }; } // namespace logger diff --git a/src/mongo/logger/log_tag.cpp b/src/mongo/logger/log_tag.cpp new file mode 100644 index 00000000000..f2df1d7585e --- /dev/null +++ b/src/mongo/logger/log_tag.cpp @@ -0,0 +1,57 @@ +/* Copyright 2014 MongoDB 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 . + * + * 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_tag.h" + +#include "mongo/util/assert_util.h" + +namespace mongo { +namespace logger { + + std::string LogTag::getShortName() const { + switch (_value) { + case kDefault: return "Default"; + case kAccessControl: return "AccessControl"; + case kCommands: return "Commands"; + case kIndexing: return "Indexing"; + case kJournalling: return "Journalling"; + case kNetworking: return "Networking"; + case kQuery: return "Query"; + case kReplication: return "Replication"; + case kSharding: return "Sharding"; + case kStorage: return "Storage"; + case kWrites: return "Writes"; + case kNumLogTags: return "Total"; + // No default. Compiler should complain if there's a tag that's not handled. + } + invariant(0); + } + +} // logger +} // mongo diff --git a/src/mongo/logger/log_tag.h b/src/mongo/logger/log_tag.h new file mode 100644 index 00000000000..06e39613833 --- /dev/null +++ b/src/mongo/logger/log_tag.h @@ -0,0 +1,72 @@ +/* Copyright 2014 MongoDB 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 . + * + * 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 + +namespace mongo { +namespace logger { + + /** + * Log tags. + * Debug messages logged using the LOG() or MONGO_LOG_TAG() + * macros may be associated with one or more log tags. + */ + class LogTag { + public: + enum Value { + kDefault = 0, + kAccessControl, + kCommands, + kIndexing, + kJournalling, + kNetworking, + kQuery, + kReplication, + kSharding, + kStorage, + kWrites, + kNumLogTags + }; + + /* implicit */ LogTag(Value value) : _value(value) {} + + operator Value() const { return _value; } + + /** + * Returns short name of log tag. + * Used to generate server parameter names in the format "logLevel_". + */ + std::string getShortName() const; + + private: + Value _value; + }; + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/log_tag_settings.cpp b/src/mongo/logger/log_tag_settings.cpp new file mode 100644 index 00000000000..82ff1f48357 --- /dev/null +++ b/src/mongo/logger/log_tag_settings.cpp @@ -0,0 +1,105 @@ +/* Copyright 2014 MongoDB 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 . + * + * 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_tag_settings.h" + +#include "mongo/util/assert_util.h" + +namespace mongo { +namespace logger { + + LogTagSettings::LogTagSettings() { + _minimumLoggedSeverity[LogTag::kDefault] = char(LogSeverity::Log().toInt()); + + for (int i = 0; i < int(LogTag::kNumLogTags); ++i) { + _minimumLoggedSeverity[i] = _minimumLoggedSeverity[LogTag::kDefault]; + _hasMinimumLoggedSeverity[i] = false; + } + + _hasMinimumLoggedSeverity[LogTag::kDefault] = true; + } + + LogTagSettings::~LogTagSettings() { } + + bool LogTagSettings::hasMinimumLogSeverity(LogTag tag) const { + dassert(int(tag) >= 0 && int(tag) < LogTag::kNumLogTags); + return _hasMinimumLoggedSeverity[tag]; + } + + LogSeverity LogTagSettings::getMinimumLogSeverity(LogTag tag) const { + dassert(int(tag) >= 0 && int(tag) < LogTag::kNumLogTags); + return LogSeverity::cast(_minimumLoggedSeverity[tag]); + } + + void LogTagSettings::setMinimumLoggedSeverity(LogTag tag, LogSeverity severity) { + dassert(int(tag) >= 0 && int(tag) < LogTag::kNumLogTags); + _minimumLoggedSeverity[tag] = char(severity.toInt()); + _hasMinimumLoggedSeverity[tag] = true; + + // Set severities for unconfigured tags to be the same as LogTag::kDefault. + if (tag == LogTag::kDefault) { + for (int i = 0; i < int(LogTag::kNumLogTags); ++i) { + if (!_hasMinimumLoggedSeverity[i]) { + _minimumLoggedSeverity[i] = char(severity.toInt()); + } + } + } + } + + void LogTagSettings::clearMinimumLoggedSeverity(LogTag tag) { + dassert(int(tag) >= 0 && int(tag) < LogTag::kNumLogTags); + + // LogTag::kDefault must always be configured. + if (tag == LogTag::kDefault) { + setMinimumLoggedSeverity(tag, LogSeverity::Log()); + return; + } + + // Set unconfigured severity level to match LogTag::kDefault. + _minimumLoggedSeverity[tag] = _minimumLoggedSeverity[LogTag::kDefault]; + _hasMinimumLoggedSeverity[tag] = false; + } + + bool LogTagSettings::shouldLog(LogSeverity severity) const { + return severity >= LogSeverity::cast(_minimumLoggedSeverity[LogTag::kDefault]); + } + + bool LogTagSettings::shouldLog(LogTag tag, LogSeverity severity) const { + dassert(int(tag) >= 0 && int(tag) < LogTag::kNumLogTags); + + // Should match LogTag::kDefault if minimum severity level is not configured for tag. + dassert(_hasMinimumLoggedSeverity[tag] || + _minimumLoggedSeverity[tag] == + _minimumLoggedSeverity[LogTag::kDefault]); + + return severity >= LogSeverity::cast(_minimumLoggedSeverity[tag]); + } + +} // logger +} // mongo diff --git a/src/mongo/logger/log_tag_settings.h b/src/mongo/logger/log_tag_settings.h new file mode 100644 index 00000000000..abed408c42f --- /dev/null +++ b/src/mongo/logger/log_tag_settings.h @@ -0,0 +1,97 @@ +/* Copyright 2014 MongoDB 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 . + * + * 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 "mongo/base/disallow_copying.h" +#include "mongo/logger/log_tag.h" +#include "mongo/logger/log_severity.h" + +namespace mongo { +namespace logger { + + /** + * Contains log severities for a list of log tags. + * kDefault always has a log severity defined but it is not necessary to + * provide log severities for the other tags (up to but not including kNumLogTags). + */ + class LogTagSettings { + MONGO_DISALLOW_COPYING(LogTagSettings); + public: + LogTagSettings(); + ~LogTagSettings(); + + /** + * Returns true if a minimum log severity has been set for this tag. + * Used by log level commands to query tag severity configuration. + */ + bool hasMinimumLogSeverity(LogTag tag) const; + + /** + * Gets the minimum log severity for tag. + * Result is defined only if hasMinimumLogSeverity() returns true for tag. + */ + LogSeverity getMinimumLogSeverity(LogTag tag) const; + + /** + * Sets the minimum log severity for tag. + */ + void setMinimumLoggedSeverity(LogTag tag, LogSeverity severity); + + /** + * Clears the minimum log severity for tag. + * For kDefault, severity level is initialized to default value. + */ + void clearMinimumLoggedSeverity(LogTag tag); + + /** + * Predicate that answers the question, "Should I, the caller, append to you, the log + * domain, tagged messages of the given severity?" True means yes. + * + * No tags provided means to check against kDefault only. + * + * If a tag is specified but minimum severity levels are not configured, + * compare 'severity' against the configured level for kDefault. + */ + bool shouldLog(LogSeverity severity) const; + bool shouldLog(LogTag tag, LogSeverity severity) const; + + private: + // True if a log severity is explicitly set for a tag. + // This differentiates between unconfigured tags and tags that happen to have the same + // severity as kDefault. + // This is also used to update the severities of unconfigured tags when the severity for + // kDefault is modified. + bool _hasMinimumLoggedSeverity[LogTag::kNumLogTags]; + + // Log severities for tags. + // Store numerical values of severities to be cache-line friendly. + // Set to kDefault minimum logged severity if _hasMinimumLoggedSeverity[i] is false. + char _minimumLoggedSeverity[LogTag::kNumLogTags]; + }; +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/log_test.cpp b/src/mongo/logger/log_test.cpp index abc2e1abd4c..26d6bce05d0 100644 --- a/src/mongo/logger/log_test.cpp +++ b/src/mongo/logger/log_test.cpp @@ -33,6 +33,8 @@ #include "mongo/logger/appender.h" #include "mongo/logger/encoder.h" +#include "mongo/logger/log_tag.h" +#include "mongo/logger/log_tag_settings.h" #include "mongo/logger/message_event_utf8_encoder.h" #include "mongo/logger/message_log_domain.h" #include "mongo/logger/rotatable_file_appender.h" @@ -52,16 +54,20 @@ namespace { class LogTest : public unittest::Test { friend class LogTestAppender; public: - LogTest() { + LogTest() : _severityOld(globalLogDomain()->getMinimumLogSeverity()) { globalLogDomain()->clearAppenders(); _appenderHandle = globalLogDomain()->attachAppender( MessageLogDomain::AppenderAutoPtr(new LogTestAppender(this))); } - virtual ~LogTest() { globalLogDomain()->detachAppender(_appenderHandle); } + virtual ~LogTest() { + globalLogDomain()->detachAppender(_appenderHandle); + globalLogDomain()->setMinimumLoggedSeverity(_severityOld); + } protected: std::vector _logLines; + LogSeverity _severityOld; private: class LogTestAppender : public MessageLogDomain::EventAppender { @@ -155,5 +161,276 @@ namespace { B() { log() << "Exercising initializer time logging."; } } b; + // Constants for log tag test cases. + const LogTag tagA = LogTag::kCommands; + const LogTag tagB = LogTag::kAccessControl; + const LogTag tagC = LogTag::kNetworking; + + // No log tag declared at file scope. + // Tag severity configuration: + // LogTag::kDefault: 2 + TEST_F(LogTest, MongoLogMacroNoFileScopeLogTag) { + globalLogDomain()->setMinimumLoggedSeverity(LogSeverity::Debug(2)); + + LOG(2) << "This is logged"; + LOG(3) << "This is not logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_EQUALS(std::string("This is logged\n"), _logLines[0]); + + // MONGO_LOG_TAG + _logLines.clear(); + MONGO_LOG_TAG(2, tagA) << "This is logged"; + MONGO_LOG_TAG(3, tagA) << "This is not logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_EQUALS(std::string("This is logged\n"), _logLines[0]); + + // MONGO_LOG_TAG2 + _logLines.clear(); + MONGO_LOG_TAG2(2, tagA, tagB) << "This is logged"; + MONGO_LOG_TAG2(3, tagA, tagB) << "This is not logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_EQUALS(std::string("This is logged\n"), _logLines[0]); + + // MONGO_LOG_TAG3 + _logLines.clear(); + MONGO_LOG_TAG3(2, tagA, tagB, tagC) << "This is logged"; + MONGO_LOG_TAG3(3, tagA, tagB, tagC) << "This is not logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_EQUALS(std::string("This is logged\n"), _logLines[0]); + } + + // Default log tag declared at inner namespace scope (tagB). + // Tag severity configuration: + // LogTag::kDefault: 1 + // tagB: 2 + namespace scoped_default_log_tag_test { + + // Set MONGO_LOG's default tag to tagB. + MONGO_LOG_DEFAULT_TAG_FILE(tagB); + + TEST_F(LogTest, MongoLogMacroNamespaceScopeLogTagDeclared) { + globalLogDomain()->setMinimumLoggedSeverity(LogSeverity::Debug(1)); + globalLogDomain()->setMinimumLoggedSeverity(tagB, + LogSeverity::Debug(2)); + + // LOG - uses log tag (tagB) declared in MONGO_LOG_DEFAULT_TAG_FILE. + LOG(2) << "This is logged"; + LOG(3) << "This is not logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_EQUALS(std::string("This is logged\n"), _logLines[0]); + + globalLogDomain()->clearMinimumLoggedSeverity(tagB); + } + + } // namespace scoped_default_log_tag_test + + // Default log tag declared at function scope (tagA). + // Tag severity configuration: + // LogTag::kDefault: 1 + // tagA: 2 + TEST_F(LogTest, MongoLogMacroFunctionScopeLogTagDeclared) { + globalLogDomain()->setMinimumLoggedSeverity(LogSeverity::Debug(1)); + globalLogDomain()->setMinimumLoggedSeverity(tagA, LogSeverity::Debug(2)); + + // Set MONGO_LOG's default tag to tagA. + MONGO_LOG_DEFAULT_TAG_LOCAL(tagA); + + // LOG - uses log tag (tagA) declared in MONGO_LOG_DEFAULT_TAG. + LOG(2) << "This is logged"; + LOG(3) << "This is not logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_EQUALS(std::string("This is logged\n"), _logLines[0]); + + // MONGO_LOG_TAG - log message tag matches function scope tag. + _logLines.clear(); + MONGO_LOG_TAG(2, tagA) << "This is logged"; + MONGO_LOG_TAG(3, tagA) << "This is not logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_EQUALS(std::string("This is logged\n"), _logLines[0]); + + // MONGO_LOG_TAG - log message tag not configured - fall back on LogTag::kDefault severity. + _logLines.clear(); + MONGO_LOG_TAG(1, tagB) << "This is logged"; + MONGO_LOG_TAG(2, tagB) << "This is not logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_EQUALS(std::string("This is logged\n"), _logLines[0]); + + // MONGO_LOG_TAG2 + _logLines.clear(); + MONGO_LOG_TAG2(2, tagA, tagB) << "This is logged"; + MONGO_LOG_TAG2(3, tagA, tagB) << "This is not logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_EQUALS(std::string("This is logged\n"), _logLines[0]); + + // MONGO_LOG_TAG2 - reverse order. + _logLines.clear(); + MONGO_LOG_TAG2(2, tagB, tagA) << "This is logged"; + MONGO_LOG_TAG2(3, tagB, tagA) << "This is not logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_EQUALS(std::string("This is logged\n"), _logLines[0]); + + // MONGO_LOG_TAG2 - none of the log message tags configured - fall back on LogTag::kDefault. + _logLines.clear(); + MONGO_LOG_TAG2(1, tagB, tagC) << "This is logged"; + MONGO_LOG_TAG2(2, tagB, tagC) << "This is not logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_EQUALS(std::string("This is logged\n"), _logLines[0]); + + // MONGO_LOG_TAG3 + _logLines.clear(); + MONGO_LOG_TAG3(2, tagA, tagB, tagC) << "This is logged"; + MONGO_LOG_TAG3(3, tagA, tagB, tagC) << "This is not logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_EQUALS(std::string("This is logged\n"), _logLines[0]); + + // MONGO_LOG_TAG3 - configured tag as 2nd tag. + _logLines.clear(); + MONGO_LOG_TAG3(2, tagB, tagA, tagC) << "This is logged"; + MONGO_LOG_TAG3(3, tagB, tagA, tagC) << "This is not logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_EQUALS(std::string("This is logged\n"), _logLines[0]); + + // MONGO_LOG_TAG3 - configured tag as 3rd tag. + _logLines.clear(); + MONGO_LOG_TAG3(2, tagB, tagC, tagA) << "This is logged"; + MONGO_LOG_TAG3(3, tagB, tagC, tagA) << "This is not logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_EQUALS(std::string("This is logged\n"), _logLines[0]); + + // MONGO_LOG_TAG3 - none of the log message tags configured - fall back on LogTag::kDefault. + _logLines.clear(); + MONGO_LOG_TAG3(1, tagB, tagC, LogTag::kIndexing) << "This is logged"; + MONGO_LOG_TAG3(2, tagB, tagC, LogTag::kIndexing) << "This is not logged"; + ASSERT_EQUALS(1U, _logLines.size()); + ASSERT_EQUALS(std::string("This is logged\n"), _logLines[0]); + + globalLogDomain()->clearMinimumLoggedSeverity(tagA); + } + + // + // Tag log level tests. + // The global log manager holds the tag log level configuration for the global log domain. + // LOG() and MONGO_LOG_TAG() macros in util/log.h determine at runtime if a log message + // should be written to the log domain. + // + + TEST_F(LogTest, LogTagSettingsMinimumLogSeverity) { + LogTagSettings settings; + ASSERT_TRUE(settings.hasMinimumLogSeverity(LogTag::kDefault)); + ASSERT_TRUE(settings.getMinimumLogSeverity(LogTag::kDefault) == LogSeverity::Log()); + for (int i = 0; i < int(LogTag::kNumLogTags); ++i) { + LogTag tag = static_cast(i); + if (tag == LogTag::kDefault) { continue; } + ASSERT_FALSE(settings.hasMinimumLogSeverity(tag)); + } + + // Override and clear minimum severity level. + for (int i = 0; i < int(LogTag::kNumLogTags); ++i) { + LogTag tag = static_cast(i); + LogSeverity severity = LogSeverity::Debug(2); + + // Override severity level. + settings.setMinimumLoggedSeverity(tag, severity); + ASSERT_TRUE(settings.hasMinimumLogSeverity(tag)); + ASSERT_TRUE(settings.getMinimumLogSeverity(tag) == severity); + + // Clear severity level. + // Special case: when clearing LogTag::kDefault, the corresponding + // severity level is set to default values (ie. Log()). + settings.clearMinimumLoggedSeverity(tag); + if (tag == LogTag::kDefault) { + ASSERT_TRUE(settings.hasMinimumLogSeverity(tag)); + ASSERT_TRUE(settings.getMinimumLogSeverity(LogTag::kDefault) == LogSeverity::Log()); + } + else { + ASSERT_FALSE(settings.hasMinimumLogSeverity(tag)); + } + } + } + + // Test for shouldLog() when the minimum logged severity is set only for LogTag::kDefault. + TEST_F(LogTest, LogTagSettingsShouldLogDefaultLogTagOnly) { + LogTagSettings settings; + + // Initial log severity for LogTag::kDefault is Log(). + ASSERT_TRUE(settings.shouldLog(LogSeverity::Info())); + ASSERT_TRUE(settings.shouldLog(LogSeverity::Log())); + ASSERT_FALSE(settings.shouldLog(LogSeverity::Debug(1))); + ASSERT_FALSE(settings.shouldLog(LogSeverity::Debug(2))); + + // If any tags are provided to shouldLog(), we should get the same outcome + // because we have not configured any non-LogTag::kDefault tags. + ASSERT_TRUE(settings.shouldLog(tagA, LogSeverity::Log())); + ASSERT_FALSE(settings.shouldLog(tagA, LogSeverity::Debug(1))); + + // Set minimum logged severity so that Debug(1) messages are written to log domain. + settings.setMinimumLoggedSeverity(LogTag::kDefault, LogSeverity::Debug(1)); + ASSERT_TRUE(settings.shouldLog(LogSeverity::Info())); + ASSERT_TRUE(settings.shouldLog(LogSeverity::Log())); + ASSERT_TRUE(settings.shouldLog(LogSeverity::Debug(1))); + ASSERT_FALSE(settings.shouldLog(LogSeverity::Debug(2))); + + // Same results when tags are supplied to shouldLog(). + ASSERT_TRUE(settings.shouldLog(tagA, LogSeverity::Debug(1))); + ASSERT_FALSE(settings.shouldLog(tagA, LogSeverity::Debug(2))); + } + + // Test for shouldLog() when we have configured a single tag. + // Also checks that severity level has been reverted to match LogTag::kDefault + // after clearing level. + // Minimum severity levels: + // LogTag::kDefault: 1 + // tagA: 2 + TEST_F(LogTest, LogTagSettingsShouldLogSingleTag) { + LogTagSettings settings; + + settings.setMinimumLoggedSeverity(LogTag::kDefault, LogSeverity::Debug(1)); + settings.setMinimumLoggedSeverity(tagA, LogSeverity::Debug(2)); + + // Tags for log message: LogTag::kDefault only. + ASSERT_TRUE(settings.shouldLog(LogSeverity::Debug(1))); + ASSERT_FALSE(settings.shouldLog(LogSeverity::Debug(2))); + + // Tags for log message: tagA only. + ASSERT_TRUE(settings.shouldLog(tagA, LogSeverity::Debug(2))); + ASSERT_FALSE(settings.shouldLog(tagA, LogSeverity::Debug(3))); + + // Clear severity level for tagA and check shouldLog() again. + settings.clearMinimumLoggedSeverity(tagA); + ASSERT_TRUE(settings.shouldLog(tagA, LogSeverity::Debug(1))); + ASSERT_FALSE(settings.shouldLog(tagA, LogSeverity::Debug(2))); + } + + // Test for shouldLog() when we have configured multiple tags. + // Minimum severity levels: + // LogTag::kDefault: 1 + // tagA: 2 + // tagB: 0 + TEST_F(LogTest, LogTagSettingsShouldLogMultipleTagsConfigured) { + LogTagSettings settings; + + settings.setMinimumLoggedSeverity(LogTag::kDefault, LogSeverity::Debug(1)); + settings.setMinimumLoggedSeverity(tagA, LogSeverity::Debug(2)); + settings.setMinimumLoggedSeverity(tagB, LogSeverity::Log()); + + // Tags for log message: LogTag::kDefault only. + ASSERT_TRUE(settings.shouldLog(LogSeverity::Debug(1))); + ASSERT_FALSE(settings.shouldLog(LogSeverity::Debug(2))); + + // Tags for log message: tagA only. + ASSERT_TRUE(settings.shouldLog(tagA, LogSeverity::Debug(2))); + ASSERT_FALSE(settings.shouldLog(tagA, LogSeverity::Debug(3))); + + // Tags for log message: tagB only. + ASSERT_TRUE(settings.shouldLog(tagB, LogSeverity::Log())); + ASSERT_FALSE(settings.shouldLog(tagB, LogSeverity::Debug(1))); + + // Tags for log message: tagC only. + // Since a tag-specific minimum severity is not configured for tagC, + // shouldLog() falls back on LogTag::kDefault. + ASSERT_TRUE(settings.shouldLog(tagC, LogSeverity::Debug(1))); + ASSERT_FALSE(settings.shouldLog(tagC, LogSeverity::Debug(2))); + } + } // namespace } // namespace mongo diff --git a/src/mongo/logger/logger.h b/src/mongo/logger/logger.h index e6125e4e99b..c3077a14102 100644 --- a/src/mongo/logger/logger.h +++ b/src/mongo/logger/logger.h @@ -48,7 +48,7 @@ namespace logger { /** * Gets the global MessageLogDomain associated for the global log manager. */ - inline MessageLogDomain* globalLogDomain() { return globalLogManager()->getGlobalDomain(); } + inline TagMessageLogDomain* globalLogDomain() { return globalLogManager()->getGlobalDomain(); } } // namespace logger } // namespace mongo diff --git a/src/mongo/logger/tag_message_log_domain.cpp b/src/mongo/logger/tag_message_log_domain.cpp new file mode 100644 index 00000000000..00ca1c32b00 --- /dev/null +++ b/src/mongo/logger/tag_message_log_domain.cpp @@ -0,0 +1,82 @@ +/* Copyright 2014 MongoDB 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 . + * + * 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/tag_message_log_domain.h" + +namespace mongo { +namespace logger { + + TagMessageLogDomain::TagMessageLogDomain() {} + + TagMessageLogDomain::~TagMessageLogDomain() {} + + bool TagMessageLogDomain::hasMinimumLogSeverity(LogTag tag) const { + return _settings.hasMinimumLogSeverity(tag); + } + + bool TagMessageLogDomain::shouldLog(LogSeverity severity) const { + return _settings.shouldLog(severity); + } + + bool TagMessageLogDomain::shouldLog(LogTag tag, LogSeverity severity) const { + return _settings.shouldLog(tag, severity); + } + + bool TagMessageLogDomain::shouldLog(LogTag tag1, LogTag tag2, LogSeverity severity) const { + return _settings.shouldLog(tag1, severity) || _settings.shouldLog(tag2, severity); + } + + bool TagMessageLogDomain::shouldLog(LogTag tag1, LogTag tag2, LogTag tag3, + LogSeverity severity) const { + return _settings.shouldLog(tag1, severity) || _settings.shouldLog(tag2, severity) || + _settings.shouldLog(tag3, severity); + } + + LogSeverity TagMessageLogDomain::getMinimumLogSeverity() const { + return _settings.getMinimumLogSeverity(LogTag::kDefault); + } + + LogSeverity TagMessageLogDomain::getMinimumLogSeverity(LogTag tag) const { + return _settings.getMinimumLogSeverity(tag); + } + + void TagMessageLogDomain::setMinimumLoggedSeverity(LogSeverity severity) { + _settings.setMinimumLoggedSeverity(LogTag::kDefault, severity); + } + + void TagMessageLogDomain::setMinimumLoggedSeverity(LogTag tag, LogSeverity severity) { + _settings.setMinimumLoggedSeverity(tag, severity); + } + + void TagMessageLogDomain::clearMinimumLoggedSeverity(LogTag tag) { + _settings.clearMinimumLoggedSeverity(tag); + } + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/tag_message_log_domain.h b/src/mongo/logger/tag_message_log_domain.h new file mode 100644 index 00000000000..59e419d4700 --- /dev/null +++ b/src/mongo/logger/tag_message_log_domain.h @@ -0,0 +1,84 @@ +/* Copyright 2014 MongoDB 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 . + * + * 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 "mongo/logger/log_tag_settings.h" +#include "mongo/logger/message_log_domain.h" + +namespace mongo { +namespace logger { + + /** + * Logging domain for ephemeral messages with minimum severity. + */ + class TagMessageLogDomain : public MessageLogDomain { + MONGO_DISALLOW_COPYING(TagMessageLogDomain); + public: + TagMessageLogDomain(); + + ~TagMessageLogDomain(); + + /** + * 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) const; + bool shouldLog(LogTag tag, LogSeverity severity) const; + bool shouldLog(LogTag tag1, LogTag tag2, LogSeverity severity) const; + bool shouldLog(LogTag tag1, LogTag tag2, LogTag tag3, LogSeverity severity) const; + + /** + * Returns true if a minimum log severity has been set for this tag. + * Called by log level commands to query tag severity configuration. + */ + bool hasMinimumLogSeverity(LogTag tag) const; + + /** + * Gets the minimum severity of messages that should be sent to this LogDomain. + */ + LogSeverity getMinimumLogSeverity() const; + LogSeverity getMinimumLogSeverity(LogTag tag) const; + + /** + * Sets the minimum severity of messages that should be sent to this LogDomain. + */ + void setMinimumLoggedSeverity(LogSeverity severity); + void setMinimumLoggedSeverity(LogTag, LogSeverity severity); + + /** + * Clears the minimum log severity for tag. + * For kDefault, severity level is initialized to default value. + */ + void clearMinimumLoggedSeverity(LogTag tag); + + private: + LogTagSettings _settings; + }; + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/util/log.h b/src/mongo/util/log.h index ab221bd01da..a98b9b161f5 100644 --- a/src/mongo/util/log.h +++ b/src/mongo/util/log.h @@ -31,6 +31,7 @@ #include "mongo/base/status.h" #include "mongo/bson/util/builder.h" +#include "mongo/logger/log_tag.h" #include "mongo/logger/logger.h" #include "mongo/logger/logstream_builder.h" #include "mongo/logger/tee.h" @@ -85,12 +86,25 @@ namespace logger { } +// MONGO_LOG uses log tag from MongoLogDefaultTag from current or global namespace. #define MONGO_LOG(DLEVEL) \ - if (!(::mongo::logger::globalLogDomain())->shouldLog(::mongo::LogstreamBuilder::severityCast(DLEVEL))) {} \ + if (!(::mongo::logger::globalLogDomain())->shouldLog(MongoLogDefaultTag_tag, ::mongo::LogstreamBuilder::severityCast(DLEVEL))) {} \ else LogstreamBuilder(::mongo::logger::globalLogDomain(), getThreadName(), ::mongo::LogstreamBuilder::severityCast(DLEVEL)) #define LOG MONGO_LOG +#define MONGO_LOG_TAG(DLEVEL, TAG1) \ + if (!(::mongo::logger::globalLogDomain())->shouldLog((TAG1), ::mongo::LogstreamBuilder::severityCast(DLEVEL))) {} \ + else LogstreamBuilder(::mongo::logger::globalLogDomain(), getThreadName(), ::mongo::LogstreamBuilder::severityCast(DLEVEL)) + +#define MONGO_LOG_TAG2(DLEVEL, TAG1, TAG2) \ + if (!(::mongo::logger::globalLogDomain())->shouldLog((TAG1), (TAG2), ::mongo::LogstreamBuilder::severityCast(DLEVEL))) {} \ + else LogstreamBuilder(::mongo::logger::globalLogDomain(), getThreadName(), ::mongo::LogstreamBuilder::severityCast(DLEVEL)) + +#define MONGO_LOG_TAG3(DLEVEL, TAG1, TAG2, TAG3) \ + if (!(::mongo::logger::globalLogDomain())->shouldLog((TAG1), (TAG2), (TAG3), ::mongo::LogstreamBuilder::severityCast(DLEVEL))) {} \ + else LogstreamBuilder(::mongo::logger::globalLogDomain(), getThreadName(), ::mongo::LogstreamBuilder::severityCast(DLEVEL)) + /** * Rotates the log files. Returns true if all logs rotate successfully. * @@ -127,3 +141,24 @@ namespace logger { void logContext(const char *msg = NULL); } // namespace mongo + +/** + * Defines default log tag 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_TAG_FILE(TAG) \ + static const ::mongo::logger::LogTag MongoLogDefaultTag_tag = (TAG); + +/** + * MONGO_LOG_DEFAULT_TAG for local code block. + */ +#define MONGO_LOG_DEFAULT_TAG_LOCAL(TAG) \ + const ::mongo::logger::LogTag MongoLogDefaultTag_tag = (TAG); + +// Provide log tag in global scope so that MONGO_LOG will always have +// a valid tag. +const ::mongo::logger::LogTag MongoLogDefaultTag_tag = ::mongo::logger::LogTag::kDefault; -- cgit v1.2.1