// introspect.cpp /** * Copyright (C) 2008 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 . */ #include "mongo/pch.h" #include "mongo/bson/util/builder.h" #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/auth/principal_set.h" #include "mongo/db/curop.h" #include "mongo/db/databaseholder.h" #include "mongo/db/introspect.h" #include "mongo/db/jsobj.h" #include "mongo/db/pdfile.h" #include "mongo/util/goodies.h" namespace { const size_t MAX_PROFILE_DOC_SIZE_BYTES = 100*1024; } namespace mongo { namespace { void _appendUserInfo(const Client& c, BSONObjBuilder& builder, AuthorizationSession* authSession) { PrincipalSet::NameIterator nameIter = authSession->getAuthenticatedPrincipalNames(); UserName bestUser; if (nameIter.more()) bestUser = *nameIter; StringData opdb( nsToDatabaseSubstring( c.ns() ) ); BSONArrayBuilder allUsers(builder.subarrayStart("allUsers")); for ( ; nameIter.more(); nameIter.next()) { BSONObjBuilder nextUser(allUsers.subobjStart()); nextUser.append(AuthorizationManager::USER_NAME_FIELD_NAME, nameIter->getUser()); nextUser.append(AuthorizationManager::USER_SOURCE_FIELD_NAME, nameIter->getDB()); nextUser.doneFast(); if (nameIter->getDB() == opdb) { bestUser = *nameIter; } } allUsers.doneFast(); builder.append("user", bestUser.getUser().empty() ? "" : bestUser.getFullName()); } } // namespace static void _profile(const Client& c, CurOp& currentOp, BufBuilder& profileBufBuilder) { Database *db = c.database(); DEV verify( db ); const char *ns = db->getProfilingNS(); // build object BSONObjBuilder b(profileBufBuilder); const bool isQueryObjTooBig = !currentOp.debug().append(currentOp, b, MAX_PROFILE_DOC_SIZE_BYTES); b.appendDate("ts", jsTime()); b.append("client", c.clientAddress()); AuthorizationSession * authSession = c.getAuthorizationSession(); _appendUserInfo(c, b, authSession); BSONObj p = b.done(); if (static_cast(p.objsize()) > MAX_PROFILE_DOC_SIZE_BYTES || isQueryObjTooBig) { string small = p.toString(/*isArray*/false, /*full*/false); warning() << "can't add full line to system.profile: " << small << endl; // rebuild with limited info BSONObjBuilder b(profileBufBuilder); b.appendDate("ts", jsTime()); b.append("client", c.clientAddress() ); _appendUserInfo(c, b, authSession); b.append("err", "profile line too large (max is 100KB)"); // should be much smaller but if not don't break anything if (small.size() < MAX_PROFILE_DOC_SIZE_BYTES){ b.append("abbreviated", small); } p = b.done(); } // write: not replicated // get or create the profiling collection NamespaceDetails *details = getOrCreateProfileCollection(db); if (details) { int len = p.objsize(); Record *r = theDataFileMgr.fast_oplog_insert(details, ns, len); memcpy(getDur().writingPtr(r->data(), len), p.objdata(), len); } } void profile(const Client& c, int op, CurOp& currentOp) { // initialize with 1kb to start, to avoid realloc later // doing this outside the dblock to improve performance BufBuilder profileBufBuilder(1024); try { Lock::DBWrite lk( currentOp.getNS() ); if ( dbHolder()._isLoaded( nsToDatabase( currentOp.getNS() ) , dbpath ) ) { Client::Context cx(currentOp.getNS(), dbpath); _profile(c, currentOp, profileBufBuilder); } else { mongo::log() << "note: not profiling because db went away - probably a close on: " << currentOp.getNS() << endl; } } catch (const AssertionException& assertionEx) { warning() << "Caught Assertion while trying to profile " << opToString(op) << " against " << currentOp.getNS() << ": " << assertionEx.toString() << endl; } } NamespaceDetails* getOrCreateProfileCollection(Database *db, bool force, string* errmsg ) { fassert(16372, db); const char* profileName = db->getProfilingNS(); NamespaceDetails* details = db->namespaceIndex().details(profileName); if (!details && (cmdLine.defaultProfile || force)) { // system.profile namespace doesn't exist; create it log() << "creating profile collection: " << profileName << endl; string myerrmsg; if (!userCreateNS(profileName, BSON("capped" << true << "size" << 1024 * 1024), myerrmsg , false)) { myerrmsg = str::stream() << "could not create ns " << profileName << ": " << myerrmsg; log() << myerrmsg << endl; if ( errmsg ) *errmsg = myerrmsg; return NULL; } details = db->namespaceIndex().details(profileName); } else if ( details && !details->isCapped() ) { string myerrmsg = str::stream() << profileName << " exists but isn't capped"; log() << myerrmsg << endl; if ( errmsg ) *errmsg = myerrmsg; return NULL; } if (!details) { // failed to get or create profile collection static time_t last = time(0) - 10; // warn the first time if( time(0) > last+10 ) { log() << "profile: warning ns " << profileName << " does not exist" << endl; last = time(0); } } return details; } } // namespace mongo