/**
* Copyright (C) 2015 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/db/ftdc/ftdc_mongod.h"
#include
#include
#include
#include "mongo/base/init.h"
#include "mongo/base/status.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/commands.h"
#include "mongo/db/ftdc/collector.h"
#include "mongo/db/ftdc/config.h"
#include "mongo/db/ftdc/controller.h"
#include "mongo/db/ftdc/ftdc_system_stats.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/db/repl/replication_coordinator_global.h"
#include "mongo/db/server_parameters.h"
#include "mongo/db/service_context.h"
#include "mongo/db/storage/storage_options.h"
namespace mongo {
namespace {
const auto getFTDCController = ServiceContext::declareDecoration>();
FTDCController* getGlobalFTDCController() {
if (!hasGlobalServiceContext()) {
return nullptr;
}
return getFTDCController(getGlobalServiceContext()).get();
}
AtomicBool localEnabledFlag(FTDCConfig::kEnabledDefault);
class ExportedFTDCEnabledParameter
: public ExportedServerParameter {
public:
ExportedFTDCEnabledParameter()
: ExportedServerParameter(
ServerParameterSet::getGlobal(),
"diagnosticDataCollectionEnabled",
&localEnabledFlag) {}
virtual Status validate(const bool& potentialNewValue) {
auto controller = getGlobalFTDCController();
if (controller) {
controller->setEnabled(potentialNewValue);
}
return Status::OK();
}
} exportedFTDCEnabledParameter;
AtomicInt32 localPeriodMillis(FTDCConfig::kPeriodMillisDefault);
class ExportedFTDCPeriodParameter
: public ExportedServerParameter {
public:
ExportedFTDCPeriodParameter()
: ExportedServerParameter(
ServerParameterSet::getGlobal(),
"diagnosticDataCollectionPeriodMillis",
&localPeriodMillis) {}
virtual Status validate(const std::int32_t& potentialNewValue) {
if (potentialNewValue < 100) {
return Status(
ErrorCodes::BadValue,
"diagnosticDataCollectionPeriodMillis must be greater than or equal to 100ms");
}
auto controller = getGlobalFTDCController();
if (controller) {
controller->setPeriod(Milliseconds(potentialNewValue));
}
return Status::OK();
}
} exportedFTDCPeriodParameter;
// Scale the values down since are defaults are in bytes, but the user interface is MB
AtomicInt32 localMaxDirectorySizeMB(FTDCConfig::kMaxDirectorySizeBytesDefault / (1024 * 1024));
AtomicInt32 localMaxFileSizeMB(FTDCConfig::kMaxFileSizeBytesDefault / (1024 * 1024));
class ExportedFTDCDirectorySizeParameter
: public ExportedServerParameter {
public:
ExportedFTDCDirectorySizeParameter()
: ExportedServerParameter(
ServerParameterSet::getGlobal(),
"diagnosticDataCollectionDirectorySizeMB",
&localMaxDirectorySizeMB) {}
virtual Status validate(const std::int32_t& potentialNewValue) {
if (potentialNewValue < 10) {
return Status(
ErrorCodes::BadValue,
"diagnosticDataCollectionDirectorySizeMB must be greater than or equal to 10");
}
if (potentialNewValue < localMaxFileSizeMB.load()) {
return Status(
ErrorCodes::BadValue,
str::stream()
<< "diagnosticDataCollectionDirectorySizeMB must be greater than or equal to '"
<< localMaxFileSizeMB.load()
<< "' which is the current value of diagnosticDataCollectionFileSizeMB.");
}
auto controller = getGlobalFTDCController();
if (controller) {
controller->setMaxDirectorySizeBytes(potentialNewValue * 1024 * 1024);
}
return Status::OK();
}
} exportedFTDCDirectorySizeParameter;
class ExportedFTDCFileSizeParameter
: public ExportedServerParameter {
public:
ExportedFTDCFileSizeParameter()
: ExportedServerParameter(
ServerParameterSet::getGlobal(),
"diagnosticDataCollectionFileSizeMB",
&localMaxFileSizeMB) {}
virtual Status validate(const std::int32_t& potentialNewValue) {
if (potentialNewValue < 1) {
return Status(ErrorCodes::BadValue,
"diagnosticDataCollectionFileSizeMB must be greater than or equal to 1");
}
if (potentialNewValue > localMaxDirectorySizeMB.load()) {
return Status(
ErrorCodes::BadValue,
str::stream()
<< "diagnosticDataCollectionFileSizeMB must be less than or equal to '"
<< localMaxDirectorySizeMB.load()
<< "' which is the current value of diagnosticDataCollectionDirectorySizeMB.");
}
auto controller = getGlobalFTDCController();
if (controller) {
controller->setMaxFileSizeBytes(potentialNewValue * 1024 * 1024);
}
return Status::OK();
}
} exportedFTDCFileSizeParameter;
AtomicInt32 localMaxSamplesPerArchiveMetricChunk(
FTDCConfig::kMaxSamplesPerArchiveMetricChunkDefault);
class ExportedFTDCArchiveChunkSizeParameter
: public ExportedServerParameter {
public:
ExportedFTDCArchiveChunkSizeParameter()
: ExportedServerParameter(
ServerParameterSet::getGlobal(),
"diagnosticDataCollectionSamplesPerChunk",
&localMaxSamplesPerArchiveMetricChunk) {}
virtual Status validate(const std::int32_t& potentialNewValue) {
if (potentialNewValue < 2) {
return Status(
ErrorCodes::BadValue,
"diagnosticDataCollectionSamplesPerChunk must be greater than or equal to 2");
}
auto controller = getGlobalFTDCController();
if (controller) {
controller->setMaxSamplesPerArchiveMetricChunk(potentialNewValue);
}
return Status::OK();
}
} exportedFTDCArchiveChunkSizeParameter;
AtomicInt32 localMaxSamplesPerInterimMetricChunk(
FTDCConfig::kMaxSamplesPerInterimMetricChunkDefault);
class ExportedFTDCInterimChunkSizeParameter
: public ExportedServerParameter {
public:
ExportedFTDCInterimChunkSizeParameter()
: ExportedServerParameter(
ServerParameterSet::getGlobal(),
"diagnosticDataCollectionSamplesPerInterimUpdate",
&localMaxSamplesPerInterimMetricChunk) {}
virtual Status validate(const std::int32_t& potentialNewValue) {
if (potentialNewValue < 2) {
return Status(ErrorCodes::BadValue,
"diagnosticDataCollectionSamplesPerInterimUpdate must be greater than or "
"equal to 2");
}
auto controller = getGlobalFTDCController();
if (controller) {
controller->setMaxSamplesPerInterimMetricChunk(potentialNewValue);
}
return Status::OK();
}
} exportedFTDCInterimChunkSizeParameter;
class FTDCSimpleInternalCommandCollector final : public FTDCCollectorInterface {
public:
FTDCSimpleInternalCommandCollector(StringData command,
StringData name,
StringData ns,
BSONObj cmdObj)
: _name(name.toString()), _ns(ns.toString()), _cmdObj(std::move(cmdObj)) {
_command = Command::findCommand(command);
invariant(_command);
}
void collect(OperationContext* opCtx, BSONObjBuilder& builder) override {
std::string errmsg;
bool ret = _command->run(opCtx, _ns, _cmdObj, 0, errmsg, builder);
// Some commands return errmsgs when they return false (collstats)
// Some commands return bson objs when they return false (replGetStatus)
// We append the status as needed to ensure readers of the collected data can check the
// status of any individual command.
_command->appendCommandStatus(builder, ret, errmsg);
}
std::string name() const override {
return _name;
}
private:
std::string _name;
std::string _ns;
BSONObj _cmdObj;
// Not owned
Command* _command;
};
} // namespace
// Register the FTDC system
// Note: This must be run before the server parameters are parsed during startup
// so that the FTDCController is initialized.
//
void startFTDC() {
boost::filesystem::path dir(storageGlobalParams.dbpath);
dir /= "diagnostic.data";
FTDCConfig config;
config.period = Milliseconds(localPeriodMillis.load());
config.enabled = localEnabledFlag.load();
config.maxFileSizeBytes = localMaxFileSizeMB.load() * 1024 * 1024;
config.maxDirectorySizeBytes = localMaxDirectorySizeMB.load() * 1024 * 1024;
config.maxSamplesPerArchiveMetricChunk = localMaxSamplesPerArchiveMetricChunk.load();
config.maxSamplesPerInterimMetricChunk = localMaxSamplesPerInterimMetricChunk.load();
auto controller = stdx::make_unique(dir, config);
// Install periodic collectors
// These are collected on the period interval in FTDCConfig.
// NOTE: For each command here, there must be an equivalent privilege check in
// GetDiagnosticDataCommand
// CmdServerStatus
// The "sharding" section is filtered out because at this time it only consists of strings in
// migration status. This section triggers too many schema changes in the serverStatus which
// hurt ftdc compression efficiency, because its output varies depending on the list of active
// migrations.
controller->addPeriodicCollector(stdx::make_unique(
"serverStatus",
"serverStatus",
"",
BSON("serverStatus" << 1 << "tcMalloc" << true << "sharding" << false)));
// These metrics are only collected if replication is enabled
if (repl::getGlobalReplicationCoordinator()->getReplicationMode() !=
repl::ReplicationCoordinator::modeNone) {
// CmdReplSetGetStatus
controller->addPeriodicCollector(stdx::make_unique(
"replSetGetStatus", "replSetGetStatus", "", BSON("replSetGetStatus" << 1)));
// CollectionStats
controller->addPeriodicCollector(
stdx::make_unique("collStats",
"local.oplog.rs.stats",
"local",
BSON("collStats"
<< "oplog.rs")));
}
// Install System Metric Collector as a periodic collector
installSystemMetricsCollector(controller.get());
// Install file rotation collectors
// These are collected on each file rotation.
// CmdBuildInfo
controller->addOnRotateCollector(stdx::make_unique(
"buildInfo", "buildInfo", "", BSON("buildInfo" << 1)));
// CmdGetCmdLineOpts
controller->addOnRotateCollector(stdx::make_unique(
"getCmdLineOpts", "getCmdLineOpts", "", BSON("getCmdLineOpts" << 1)));
// HostInfoCmd
controller->addOnRotateCollector(stdx::make_unique(
"hostInfo", "hostInfo", "", BSON("hostInfo" << 1)));
// Install the new controller
auto& staticFTDC = getFTDCController(getGlobalServiceContext());
staticFTDC = std::move(controller);
staticFTDC->start();
}
void stopFTDC() {
auto controller = getGlobalFTDCController();
if (controller) {
controller->stop();
}
}
FTDCController* FTDCController::get(ServiceContext* serviceContext) {
return getFTDCController(serviceContext).get();
}
} // namespace mongo