Skip to content

Adding the getMongoReport script #92

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
371 changes: 371 additions & 0 deletions getMongoReport/getMongoReport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,371 @@
/* global db, tojson, tojsononeline, rs, print, printjson */

/* =================================================
* getMongoReport.js: MongoDB Deployment and Schema Report
* =================================================
*
* Copyright MongoDB, Inc, 2015
*
* Gather MongoDB deployment and schema information.
*
* To execute on a locally running mongod on default port (27017) without
* authentication, run:
*
* mongo getMongoReport.js > getMongoReport.log
*
* To execute on a remote mongod or mongos with authentication, run:
*
* mongo HOST:PORT/admin -u ADMIN_USER -p ADMIN_PASSWORD getMongoReport.js > getMongoReport.log
*
* For details, see
* https://github.com/mongodb/support-tools/tree/master/getMongoReport.
*
*
* DISCLAIMER
*
* Please note: all tools/ scripts in this repo are released for use "AS
* IS" without any warranties of any kind, including, but not limited to
* their installation, use, or performance. We disclaim any and all
* warranties, either express or implied, including but not limited to
* any warranty of noninfringement, merchantability, and/ or fitness for
* a particular purpose. We do not warrant that the technology will
* meet your requirements, that the operation thereof will be
* uninterrupted or error-free, or that any errors will be corrected.
*
* Any use of these scripts and tools is at your own risk. There is no
* guarantee that they have been through thorough testing in a
* comparable environment and we are not responsible for any damage
* or data loss incurred with their use.
*
* You are responsible for reviewing and testing any scripts you run
* thoroughly before use in any non-testing environment.
*
*
* LICENSE
*
* 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.
*/

var _version = "1.0.0";

// Limits the number of collections that this script will gather stats on in order to avoid
// the possibility of running out of file descriptors.
var _LIMIT_COLLECTIONS = 2500;

(function () {
"use strict";
}());


// Taken from the >= 3.1.9 shell to capture print output
if (typeof print.captureAllOutput === "undefined") {
print.captureAllOutput = function (fn, args) {
var res = {};
res.output = [];
var __orig_print = print;
print = function () {
Array.prototype.push.apply(res.output, Array.prototype.slice.call(arguments).join(" ").split("\n"));
};
try {
res.result = fn.apply(undefined, args);
}
finally {
// Stop capturing print() output
print = __orig_print;
}
return res;
};
}

// Convert NumberLongs to strings to save precision
function longmangle(n) {
if (! n instanceof NumberLong)
return null;
var s = n.toString();
s = s.replace("NumberLong(","").replace(")","");
if (s[0] == '"')
s = s.slice(1, s.length-1)
return s;
}

// For use in JSON.stringify to properly serialize known types
function jsonStringifyReplacer(k, v){
if (v instanceof ObjectId)
return { "$oid" : v.valueOf() };
if (v instanceof NumberLong)
return { "$numberLong" : longmangle(v) };
if (v instanceof NumberInt)
return v.toNumber();
// For ISODates; the $ check prevents recursion
if (typeof v === "string" && k.startsWith('$') == false){
try {
iso = ISODate(v);
return { "$date" : iso.valueOf() };
}
// Nothing to do here, we'll get the return at the end
catch(e) {}
}
return v;
}

// Copied from Mongo Shell
function printShardInfo(){
section = "shard_info";
var configDB = db.getSiblingDB("config");

printInfo("Sharding version",
function(){return db.getSiblingDB("config").getCollection("version").findOne()},
section);

printInfo("Shards", function(){
return configDB.shards.find().sort({ _id : 1 }).toArray();
}, section);

printInfo("Sharded databases", function(){
var ret = [];
configDB.databases.find().sort( { name : 1 } ).forEach(
function(db) {
doc = {};
for (k in db) {
if (db.hasOwnProperty(k)) doc[k] = db[k];
}
if (db.partitioned) {
doc['collections'] = [];
configDB.collections.find( { _id : new RegExp( "^" +
RegExp.escape(db._id) + "\\." ) } ).
sort( { _id : 1 } ).forEach( function( coll ) {
if ( coll.dropped === false ){
collDoc = {};
collDoc['_id'] = coll._id;
collDoc['key'] = coll.key;

var res = configDB.chunks.aggregate(
{ "$match": { ns: coll._id } },
{ "$group": { _id: "$shard", nChunks: { "$sum": 1 } } }
);
// MongoDB 2.6 and above returns a cursor instead of a document
res = (res.result ? res.result : res.toArray());

collDoc['distribution'] = [];
res.forEach( function(z) {
chunkDistDoc = {'shard': z._id, 'nChunks': z.nChunks};
collDoc['distribution'].push(chunkDistDoc);
} );

collDoc['chunks'] = [];
configDB.chunks.find( { "ns" : coll._id } ).sort( { min : 1 } ).forEach(
function(chunk) {
chunkDoc = {}
chunkDoc['min'] = chunk.min;
chunkDoc['max'] = chunk.max;
chunkDoc['shard'] = chunk.shard;
chunkDoc['jumbo'] = chunk.jumbo ? true : false;
collDoc['chunks'].push(chunkDoc);
}
);

collDoc['tags'] = [];
configDB.tags.find( { ns : coll._id } ).sort( { min : 1 } ).forEach(
function(tag) {
tagDoc = {}
tagDoc['tag'] = tag.tag;
tagDoc['min'] = tag.min;
tagDoc['max'] = tag.max;
collDoc['tags'].push(tagDoc);
}
);
}
doc['collections'].push(collDoc);
}
);
}
ret.push(doc);
}
);
return ret;
}, section);
}

function printInfo(message, command, section, printCapture) {
var result = false;
printCapture = (printCapture === undefined ? false: true);
if (! _printJSON) print("\n** " + message + ":");
startTime = new Date();
try {
if (printCapture) {
result = print.captureAllOutput(command);
} else {
result = command();
}
err = null
} catch(err) {
if (! _printJSON) {
print("Error running '" + command + "':");
print(err);
}
result = null
}
endTime = new Date();
doc = {};
doc['command'] = command.toString();
doc['error'] = err;
doc['host'] = _host;
doc['ref'] = _ref;
doc['tag'] = _tag;
doc['output'] = result;
if (typeof(section) !== "undefined") {
doc['section'] = section;
doc['subsection'] = message.toLowerCase().replace(/ /g, "_");
} else {
doc['section'] = message.toLowerCase().replace(/ /g, "_");
}
doc['ts'] = {'start': startTime, 'end': endTime};
doc['version'] = _version;
_output.push(doc);
if (! _printJSON) printjson(result);
return result;
}

function printServerInfo() {
section = "server_info";
printInfo('Shell version', version, section);
printInfo('Shell hostname', hostname, section);
printInfo('db', function(){return db.getName()}, section);
printInfo('Server status info', function(){return db.serverStatus()}, section);
printInfo('Host info', function(){return db.hostInfo()}, section);
printInfo('Command line info', function(){return db.serverCmdLineOpts()}, section);
printInfo('Server build info', function(){return db.serverBuildInfo()}, section);
}

function printReplicaSetInfo() {
section = "replicaset_info";
printInfo('Replica set config', function(){return rs.conf()}, section);
printInfo('Replica status', function(){return rs.status()}, section);
printInfo('Replica info', function(){return db.getReplicationInfo()}, section);
printInfo('Replica slave info', function(){return db.printSlaveReplicationInfo()}, section, true);
}

function printDataInfo(isMongoS) {
section = "data_info";
var dbs = printInfo('List of databases', function(){return db.getMongo().getDBs()}, section);

if (dbs.databases) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation somehow got messed up here.

dbs.databases.forEach(function(mydb) {

var collections = printInfo("List of collections for database '"+ mydb.name +"'",
function(){return db.getSiblingDB(mydb.name).getCollectionNames()}, section);

printInfo('Database stats (MB)',
function(){return db.getSiblingDB(mydb.name).stats(1024*1024)}, section);
if (!isMongoS) {
printInfo('Database profiler',
function(){return db.getSiblingDB(mydb.name).getProfilingStatus()}, section);
}

if (collections && _collections_counter > _LIMIT_COLLECTIONS) {
print("Error: Too many collections to process, stopped to avoid stressing the server");
} else if (collections) {

for (let c = 0; c < collections.length; c++) {
var col = collections[c];
_collections_counter++;

if (_collections_counter > _LIMIT_COLLECTIONS) { break; }

printInfo('Collection stats (MB)',
function(){return db.getSiblingDB(mydb.name).getCollection(col).stats(1024*1024)}, section);
if (isMongoS) {
printInfo('Shard distribution',
function(){return db.getSiblingDB(mydb.name).getCollection(col).getShardDistribution()}, section, true);
}
printInfo('Indexes',
function(){return db.getSiblingDB(mydb.name).getCollection(col).getIndexes()}, section);
printInfo('Index Stats',
function(){
var res = db.getSiblingDB(mydb.name).runCommand( {
aggregate: col,
pipeline: [
{$indexStats: {}},
{$group: {_id: "$key", stats: {$push: {accesses: "$accesses.ops", host: "$host", since: "$accesses.since"}}}},
{$project: {key: "$_id", stats: 1, _id: 0}}
],
cursor: {}
});

//It is assumed that there always will be a single batch as collections
//are limited to 64 indexes and usage from all shards is grouped
//into a single document
if (res.hasOwnProperty('cursor') && res.cursor.hasOwnProperty('firstBatch')) {
res.cursor.firstBatch.forEach(
function(d){
d.stats.forEach(
function(d){
d.since = d.since.toUTCString();
})
});
}

return res;
}, section);
}
}
});
}
}

function printShardOrReplicaSetInfo() {
section = "shard_or_replicaset_info";
printInfo('isMaster', function(){return db.isMaster()}, section);
var state;
var stateInfo = rs.status();
if (stateInfo.ok) {
stateInfo.members.forEach( function( member ) { if ( member.self ) { state = member.stateStr; } } );
if ( !state ) state = stateInfo.myState;
} else {
var info = stateInfo.info;
if ( info && info.length < 20 ) {
state = info; // "mongos", "configsvr"
}
if ( ! state ) state = "standalone";
}
if (! _printJSON) print("\n** Connected to " + state);
if (state == "mongos") {
printShardInfo();
return true;
} else if (state != "standalone" && state != "configsvr") {
if (state == "SECONDARY" || state == 2) {
rs.slaveOk();
}
printReplicaSetInfo();
}
return false;
}



if (typeof _printJSON === "undefined") var _printJSON = false;
if (typeof _ref === "undefined") var _ref = null;
var _collections_counter = 0;
var _output = [];
var _tag = ObjectId();
if (! _printJSON) {
print("================================");
print("MongoDB Deployment and Schema Report");
print("getMongoReport.js version " + _version);
print("================================");
}
var _host = hostname();
printServerInfo();
var isMongoS = printShardOrReplicaSetInfo();
printDataInfo(isMongoS);
if (_printJSON) print(JSON.stringify(_output, jsonStringifyReplacer, 4));
Loading