diff options
author | Matt Kangas <matt.kangas@mongodb.com> | 2014-10-08 16:17:35 -0400 |
---|---|---|
committer | Matt Kangas <matt.kangas@mongodb.com> | 2014-10-16 22:06:04 -0400 |
commit | 0eb6558191d0408262163ae22f80b786a305eaef (patch) | |
tree | 4d1943e25439859b4e230720c503fc2150500327 /src | |
parent | 024c9130eb1f9b819ac16aae5a38ba5d7b9614f1 (diff) | |
download | mongo-0eb6558191d0408262163ae22f80b786a305eaef.tar.gz |
SERVER-15486 parse_log_component_settings
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/logger/SConscript | 8 | ||||
-rw-r--r-- | src/mongo/logger/parse_log_component_settings.cpp | 122 | ||||
-rw-r--r-- | src/mongo/logger/parse_log_component_settings.h | 62 | ||||
-rw-r--r-- | src/mongo/logger/parse_log_component_settings_test.cpp | 184 |
5 files changed, 377 insertions, 0 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript index 6d07f4f422d..44d09d578ce 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -515,6 +515,7 @@ coredbEnv.Library("coredb", [ '$BUILD_DIR/third_party/shim_snappy', 'server_options', '$BUILD_DIR/mongo/util/cmdline_utils/cmdline_utils', + '$BUILD_DIR/mongo/logger/parse_log_component_settings', 'clientdriver', ]) diff --git a/src/mongo/logger/SConscript b/src/mongo/logger/SConscript index 4febd815244..0a790a79679 100644 --- a/src/mongo/logger/SConscript +++ b/src/mongo/logger/SConscript @@ -21,6 +21,10 @@ env.Library('logger', LIBDEPS=['$BUILD_DIR/mongo/base/base', '$BUILD_DIR/mongo/util/concurrency/thread_name']) +env.Library('parse_log_component_settings', + ['parse_log_component_settings.cpp'], + LIBDEPS=['logger', '$BUILD_DIR/mongo/bson']) + # writes multi-byte sequences to console # to support manual testing of console stream under Windows # output should be visually verified under Command Prompt or Power Shell @@ -38,3 +42,7 @@ env.CppUnitTest('log_function_test', 'log_function_test.cpp', env.CppUnitTest('rotatable_file_writer_test', 'rotatable_file_writer_test.cpp', LIBDEPS=['logger']) + +env.CppUnitTest(target='parse_log_component_settings_test', + source='parse_log_component_settings_test.cpp', + LIBDEPS=['logger', 'parse_log_component_settings']) diff --git a/src/mongo/logger/parse_log_component_settings.cpp b/src/mongo/logger/parse_log_component_settings.cpp new file mode 100644 index 00000000000..5ebb6faa9b5 --- /dev/null +++ b/src/mongo/logger/parse_log_component_settings.cpp @@ -0,0 +1,122 @@ +/** + * Copyright (C) 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 <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. + */ + +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault + +#include "mongo/platform/basic.h" + +#include "mongo/logger/parse_log_component_settings.h" + +#include <vector> + +#include "mongo/bson/bsonobj.h" +#include "mongo/bson/bsonobjbuilder.h" +#include "mongo/bson/bsonobjiterator.h" +#include "mongo/bson/bsontypes.h" +#include "mongo/logger/log_component.h" +#include "mongo/util/assert_util.h" +#include "mongo/util/log.h" +#include "mongo/util/mongoutils/str.h" +#include "mongo/util/stringutils.h" + +namespace mongo { +namespace logger { + + /* + * Looks up a component by its short name, or returns kNumLogComponents + * if the shortName is invalid + */ + const LogComponent _getComponentForShortName(const StringData& shortName) { + for (int i = 0; i < int(LogComponent::kNumLogComponents); ++i) { + LogComponent component = static_cast<LogComponent::Value>(i); + if (component.getShortName() == shortName) + return component; + } + return static_cast<LogComponent::Value>(LogComponent::kNumLogComponents); + } + + StatusWith< std::vector<LogComponentSetting> > parseLogComponentSettings( + const BSONObj& settings) { + + typedef std::vector<LogComponentSetting> Result; + + std::vector<LogComponentSetting> levelsToSet; + std::vector<BSONObjIterator> iterators; + + LogComponent parentComponent = LogComponent::kDefault; + BSONObjIterator iter(settings); + + while (iter.moreWithEOO()) { + BSONElement elem = iter.next(); + if (elem.eoo()) { + if (!iterators.empty()) { + iter = iterators.back(); + iterators.pop_back(); + parentComponent = parentComponent.parent(); + } + continue; + } + if (elem.fieldNameStringData() == "verbosity") { + if (!elem.isNumber()) { + return StatusWith<Result>(ErrorCodes::BadValue, str::stream() + << "Expected " << parentComponent.getDottedName() + << ".verbosity to be a number, but found " + << typeName(elem.type())); + } + levelsToSet.push_back((LogComponentSetting(parentComponent, + elem.numberInt()))); + continue; + } + const StringData shortName = elem.fieldNameStringData(); + const LogComponent curr = _getComponentForShortName(shortName); + + if (curr == LogComponent::kNumLogComponents || curr.parent() != parentComponent) { + return StatusWith<Result>(ErrorCodes::BadValue, str::stream() + << "Invalid component name " + << parentComponent.getDottedName() << "." << shortName); + } + if (elem.isNumber()) { + levelsToSet.push_back(LogComponentSetting(curr, elem.numberInt())); + continue; + } + if (elem.type() != Object) { + return StatusWith<Result>(ErrorCodes::BadValue, str::stream() + << "Invalid type " << typeName(elem.type()) << "for component " + << parentComponent.getDottedName() << "." << shortName); + } + iterators.push_back(iter); + parentComponent = curr; + iter = BSONObjIterator(elem.Obj()); + } + + // Done walking settings + return StatusWith<Result>(levelsToSet); + } + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/parse_log_component_settings.h b/src/mongo/logger/parse_log_component_settings.h new file mode 100644 index 00000000000..70094b27f04 --- /dev/null +++ b/src/mongo/logger/parse_log_component_settings.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 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 <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 <vector> + +#include "mongo/logger/log_component.h" + +namespace mongo { + + class BSONObj; + template <typename T> class StatusWith; + +namespace logger { + + /** + * One parsed LogComponent and desired log level + */ + struct LogComponentSetting { + LogComponentSetting(LogComponent c, int lvl) : component(c), level(lvl) {} + + LogComponent component; + int level; + }; + + /** + * Parses instructions for modifying component log levels from "settings". + * + * Returns an error status describing why parsing failed, or a vector of LogComponentSettings, + * each describing how to change a particular log components verbosity level. + */ + StatusWith< std::vector<LogComponentSetting> > parseLogComponentSettings( + const BSONObj& settings); + +} // namespace logger +} // namespace mongo diff --git a/src/mongo/logger/parse_log_component_settings_test.cpp b/src/mongo/logger/parse_log_component_settings_test.cpp new file mode 100644 index 00000000000..7d712426990 --- /dev/null +++ b/src/mongo/logger/parse_log_component_settings_test.cpp @@ -0,0 +1,184 @@ +/** + * Copyright (C) 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 <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_component.h" +#include "mongo/logger/parse_log_component_settings.h" + +#include "mongo/db/jsobj.h" +#include "mongo/unittest/unittest.h" + +namespace { + + using namespace mongo; + using namespace mongo::logger; + + typedef std::vector<LogComponentSetting> Settings; + + TEST(Empty, Empty) { + BSONObj input; + StatusWith<Settings> result = parseLogComponentSettings(input); + + ASSERT_OK(result.getStatus()); + ASSERT_EQUALS(result.getValue().size(), 0u); + } + + TEST(Flat, Numeric) { + BSONObj input = BSON( "verbosity" << 1 ); + + StatusWith<Settings> result = parseLogComponentSettings(input); + + ASSERT_OK(result.getStatus()); + ASSERT_EQUALS(result.getValue().size(), 1u); + ASSERT_EQUALS(result.getValue()[0].level, 1); + ASSERT_EQUALS(result.getValue()[0].component, LogComponent::kDefault); + } + + TEST(Flat, FailNonNumeric) { + BSONObj input = BSON( "verbosity" << "not a number" ); + + StatusWith<Settings> result = parseLogComponentSettings(input); + + ASSERT_NOT_OK(result.getStatus()); + ASSERT_EQUALS(ErrorCodes::BadValue, result.getStatus().code()); + ASSERT_EQUALS(result.getStatus().reason(), + "Expected default.verbosity to be a number, but found String"); + } + + TEST(Flat, FailBadComponent) { + BSONObj input = BSON( "NoSuchComponent" << 2 ); + + StatusWith<Settings> result = parseLogComponentSettings(input); + + ASSERT_NOT_OK(result.getStatus()); + ASSERT_EQUALS(result.getStatus().code(), ErrorCodes::BadValue); + ASSERT_EQUALS(result.getStatus().reason(), + "Invalid component name default.NoSuchComponent"); + } + + TEST(Nested, Numeric) { + BSONObj input = BSON("accessControl" << BSON("verbosity" << 1)); + + StatusWith<Settings> result = parseLogComponentSettings(input); + + ASSERT_OK(result.getStatus()); + ASSERT_EQUALS(result.getValue().size(), 1u); + ASSERT_EQUALS(result.getValue()[0].level, 1); + ASSERT_EQUALS(result.getValue()[0].component, LogComponent::kAccessControl); + } + + TEST(Nested, FailNonNumeric) { + BSONObj input = BSON("accessControl" << BSON("verbosity" << "Not a number")); + + StatusWith<Settings> result = parseLogComponentSettings(input); + + ASSERT_NOT_OK(result.getStatus()); + ASSERT_EQUALS(result.getStatus().code(), ErrorCodes::BadValue); + ASSERT_EQUALS(result.getStatus().reason(), + "Expected accessControl.verbosity to be a number, but found String"); + } + + TEST(Nested, FailBadComponent) { + BSONObj input = BSON("NoSuchComponent" << BSON("verbosity" << 2)); + + StatusWith<Settings> result = parseLogComponentSettings(input); + + ASSERT_NOT_OK(result.getStatus()); + ASSERT_EQUALS(result.getStatus().code(), ErrorCodes::BadValue); + ASSERT_EQUALS(result.getStatus().reason(), + "Invalid component name default.NoSuchComponent"); + } + + TEST(Multi, Numeric) { + BSONObj input = BSON("verbosity" << 2 << + "accessControl" << BSON("verbosity" << 0) << + "storage" << + BSON("verbosity" << 3 << + "journaling" << BSON("verbosity" << 5))); + + StatusWith<Settings> result = parseLogComponentSettings(input); + + ASSERT_OK(result.getStatus()); + ASSERT_EQUALS(result.getValue().size(), 4u); + + ASSERT_EQUALS(result.getValue()[0].level, 2); + ASSERT_EQUALS(result.getValue()[0].component, LogComponent::kDefault); + ASSERT_EQUALS(result.getValue()[1].level, 0); + ASSERT_EQUALS(result.getValue()[1].component, LogComponent::kAccessControl); + ASSERT_EQUALS(result.getValue()[2].level, 3); + ASSERT_EQUALS(result.getValue()[2].component, LogComponent::kStorage); + ASSERT_EQUALS(result.getValue()[3].level, 5); + ASSERT_EQUALS(result.getValue()[3].component, LogComponent::kJournaling); + } + + TEST(Multi, FailBadComponent) { + BSONObj input = BSON("verbosity" << 6 << + "accessControl"<< BSON("verbosity" << 5) << + "storage" << + BSON("verbosity" << 4 << + "journaling" << BSON("verbosity" << 6)) << + "No Such Component" << BSON("verbosity" << 2) << + "extrafield" << 123); + + StatusWith<Settings> result = parseLogComponentSettings(input); + + ASSERT_NOT_OK(result.getStatus()); + ASSERT_EQUALS(result.getStatus().code(), ErrorCodes::BadValue); + ASSERT_EQUALS(result.getStatus().reason(), + "Invalid component name default.No Such Component"); + } + + TEST(DeeplyNested, FailFast) { + BSONObj input = BSON("storage" << + BSON("this" << + BSON("is" << + BSON("nested" << + BSON("too" << "deeply"))))); + + StatusWith<Settings> result = parseLogComponentSettings(input); + + ASSERT_NOT_OK(result.getStatus()); + ASSERT_EQUALS(result.getStatus().code(), ErrorCodes::BadValue); + ASSERT_EQUALS(result.getStatus().reason(), + "Invalid component name storage.this"); + } + + TEST(DeeplyNested, FailLast) { + BSONObj input = BSON("storage" << + BSON("journaling" << + BSON("No Such Component" << "bad"))); + + StatusWith<Settings> result = parseLogComponentSettings(input); + + ASSERT_NOT_OK(result.getStatus()); + ASSERT_EQUALS(result.getStatus().code(), ErrorCodes::BadValue); + ASSERT_EQUALS(result.getStatus().reason(), + "Invalid component name storage.journaling.No Such Component"); + } +} |