void rotate() { if ( ! _enabled ) { cout << "LoggingManager not enabled" << endl; return; } if ( _file ) { #ifdef _WIN32 cout << "log rotation net yet supported on windows" << endl; return; #else struct tm t; localtime_r( &_opened , &t ); stringstream ss; ss << _path << "." << terseCurrentTime(false); string s = ss.str(); rename( _path.c_str() , s.c_str() ); #endif } FILE* tmp = freopen(_path.c_str(), (_append ? "a" : "w"), stdout); if (!tmp) { cerr << "can't open: " << _path.c_str() << " for log file" << endl; dbexit( EXIT_BADOPTIONS ); assert(0); } Logstream::setLogFile(tmp); // after this point no thread will be using old file _file = tmp; _opened = time(0); }
void ConfigServer::logChange( const string& what , const string& ns , const BSONObj& detail ){ assert( _primary.ok() ); static bool createdCapped = false; static AtomicUInt num; ScopedDbConnection conn( _primary ); if ( ! createdCapped ){ try { conn->createCollection( "config.changelog" , 1024 * 1024 * 10 , true ); } catch ( UserException& e ){ log(1) << "couldn't create changelog (like race condition): " << e << endl; // don't care } createdCapped = true; } stringstream id; id << getHostNameCached() << "-" << terseCurrentTime() << "-" << num++; BSONObj msg = BSON( "_id" << id.str() << "server" << getHostNameCached() << "time" << DATENOW << "what" << what << "ns" << ns << "details" << detail ); log() << "config change: " << msg << endl; try { conn->insert( "config.changelog" , msg ); } catch ( std::exception& e ){ log() << "not logging config change: " << e.what() << endl; } conn.done(); }
void start( const string& lp , bool append ) { uassert( 10268 , "LoggingManager already started" , ! _enabled ); _append = append; bool exists = boost::filesystem::exists(lp); bool isdir = boost::filesystem::is_directory(lp); bool isreg = boost::filesystem::is_regular(lp); if ( exists ) { if ( isdir ) { cout << "logpath [" << lp << "] should be a filename, not a directory" << endl; dbexit( EXIT_BADOPTIONS ); assert( 0 ); } if ( ! append ) { // only attempt rename if log is regular file if ( isreg ) { stringstream ss; ss << lp << "." << terseCurrentTime( false ); string s = ss.str(); if ( ! rename( lp.c_str() , s.c_str() ) ) { cout << "log file [" << lp << "] exists; copied to temporary file [" << s << "]" << endl; } else { cout << "log file [" << lp << "] exists and couldn't make backup; run with --logappend or manually remove file (" << strerror(errno) << ")" << endl; dbexit( EXIT_BADOPTIONS ); assert( 0 ); } } } } // test path FILE * test = fopen( lp.c_str() , _append ? "a" : "w" ); if ( ! test ) { cout << "can't open [" << lp << "] for log file: " << errnoWithDescription() << endl; dbexit( EXIT_BADOPTIONS ); assert( 0 ); } if (append && exists){ // two blank lines before and after const string msg = "\n\n***** SERVER RESTARTED *****\n\n\n"; massert(14036, errnoWithPrefix("couldn't write to log file"), fwrite(msg.data(), 1, msg.size(), test) == msg.size()); } fclose( test ); _path = lp; _enabled = 1; rotate(); }
void printDiff( BSONObj prev , BSONObj now ) { if ( ! prev["totals"].isABSONObj() || ! now["totals"].isABSONObj() ) { cout << "." << endl; return; } prev = prev["totals"].Obj(); now = now["totals"].Obj(); vector<NSInfo> data; unsigned longest = 30; BSONObjIterator i( now ); while ( i.more() ) { BSONElement e = i.next(); // invalid, data fixed in 1.8.0 if ( e.fieldName()[0] == '?' ) continue; if ( ! str::contains( e.fieldName() , '.' ) ) continue; BSONElement old = prev[e.fieldName()]; if ( old.eoo() ) continue; if ( strlen( e.fieldName() ) > longest ) longest = strlen(e.fieldName()); data.push_back( NSInfo( e.fieldName() , old.Obj() , e.Obj() ) ); } std::sort( data.begin() , data.end() ); cout << "\n" << setw(longest) << "ns" << "\ttotal" << "\tread" << "\twrite" << "\t\t" << terseCurrentTime() << endl; for ( int i=data.size()-1; i>=0 && data.size() - i < 10 ; i-- ) { cout << setw(longest) << data[i].ns << "\t" << setprecision(3) << data[i].diffTimeMS( "total" ) << "ms" << "\t" << setprecision(3) << data[i].diffTimeMS( "readLock" ) << "ms" << "\t" << setprecision(3) << data[i].diffTimeMS( "writeLock" ) << "ms" << endl; } }
void rotate() { if ( ! _enabled ) { cout << "LoggingManager not enabled" << endl; return; } if ( _file ) { #ifdef _WIN32 cout << "log rotation net yet supported on windows" << endl; return; #else struct tm t; localtime_r( &_opened , &t ); stringstream ss; ss << _path << "." << terseCurrentTime(false); string s = ss.str(); rename( _path.c_str() , s.c_str() ); #endif } FILE* tmp = freopen(_path.c_str(), (_append ? "a" : "w"), stdout); if (!tmp) { cerr << "can't open: " << _path.c_str() << " for log file" << endl; dbexit( EXIT_BADOPTIONS ); assert(0); } #ifdef _WIN32 // windows has these functions it just gives them a funny name # define dup2 _dup2 # define fileno _fileno #endif // redirect stderr to log file dup2(fileno(tmp), 2); Logstream::setLogFile(tmp); // after this point no thread will be using old file #if 0 // enable to test redirection cout << "written to cout" << endl; cerr << "written to cerr" << endl; log() << "written to log()" << endl; #endif _file = tmp; _opened = time(0); }
RemoveSaver::RemoveSaver( const string& a , const string& b , const string& why) : _out(0) { static int NUM = 0; _root = dbpath; if ( a.size() ) _root /= a; if ( b.size() ) _root /= b; verify( a.size() || b.size() ); _file = _root; stringstream ss; ss << why << "." << terseCurrentTime(false) << "." << NUM++ << ".bson"; _file /= ss.str(); }
/* must never throw */ void ConfigServer::logChange( const string& what , const string& ns , const BSONObj& detail ) { string changeID; try { // get this entry's ID so we can use on the exception code path too stringstream id; static AtomicUInt num; id << getHostNameCached() << "-" << terseCurrentTime() << "-" << num++; changeID = id.str(); // send a copy of the message to the log in case it doesn't manage to reach config.changelog Client* c = currentClient.get(); BSONObj msg = BSON( "_id" << changeID << "server" << getHostNameCached() << "clientAddr" << (c ? c->clientAddress(true) : "N/A") << "time" << DATENOW << "what" << what << "ns" << ns << "details" << detail ); log() << "about to log metadata event: " << msg << endl; assert( _primary.ok() ); ScopedDbConnection conn( _primary ); static bool createdCapped = false; if ( ! createdCapped ) { try { conn->createCollection( "config.changelog" , 1024 * 1024 * 10 , true ); } catch ( UserException& e ) { log(1) << "couldn't create changelog (like race condition): " << e << endl; // don't care } createdCapped = true; } conn->insert( "config.changelog" , msg ); conn.done(); } catch ( std::exception& e ) { // if we got here, it means the config change is only in the log; it didn't make it to config.changelog log() << "not logging config change: " << changeID << " " << e.what() << endl; } }
void printDiff( const NamespaceStats& prev , const NamespaceStats& now ) { if ( prev.size() == 0 || now.size() == 0 ) { cout << "." << endl; return; } vector<NamespaceDiff> data = StatUtil::computeDiff( prev , now ); unsigned longest = 30; for ( unsigned i=0; i < data.size(); i++ ) { const string& ns = data[i].ns; if ( ! useLocks() && ns.find( '.' ) == string::npos ) continue; if ( ns.size() > longest ) longest = ns.size(); } int numberWidth = 10; cout << "\n" << setw(longest) << ( useLocks() ? "db" : "ns" ) << setw(numberWidth+2) << "total" << setw(numberWidth+2) << "read" << setw(numberWidth+2) << "write" << "\t\t" << terseCurrentTime() << endl; for ( int i=data.size()-1; i>=0 && data.size() - i < 10 ; i-- ) { if ( ! useLocks() && data[i].ns.find( '.' ) == string::npos ) continue; cout << setw(longest) << data[i].ns << setw(numberWidth) << setprecision(3) << data[i].total() << "ms" << setw(numberWidth) << setprecision(3) << data[i].read << "ms" << setw(numberWidth) << setprecision(3) << data[i].write << "ms" << endl; } }
void ConfigServer::logChange( const string& what , const string& ns , const BSONObj& detail ){ static bool createdCapped = false; static AtomicUInt num; ScopedDbConnection conn( _primary ); if ( ! createdCapped ){ conn->createCollection( "config.changelog" , 1024 * 1024 * 10 , true ); createdCapped = true; } stringstream id; id << ourHostname << "-" << terseCurrentTime() << "-" << num++; conn->insert( "config.changelog" , BSON( "_id" << id.str() << "server" << ourHostname << "time" << DATENOW << "what" << what << "ns" << ns << "details" << detail ) ); conn.done(); }
bool rotate() { if ( ! _enabled ) { cout << "LoggingManager not enabled" << endl; return true; } if ( _file ) { #ifdef POSIX_FADV_DONTNEED posix_fadvise(fileno(_file), 0, 0, POSIX_FADV_DONTNEED); #endif // Rename the (open) existing log file to a timestamped name stringstream ss; ss << _path << "." << terseCurrentTime( false ); string s = ss.str(); if (0 != rename(_path.c_str(), s.c_str())) { error() << "Failed to rename " << _path << " to " << s; return false; } } FILE* tmp = 0; // The new file using the original logpath name #if _WIN32 // We rename an open log file (above, on next rotation) and the trick to getting Windows to do that is // to open the file with FILE_SHARE_DELETE. So, we can't use the freopen() call that non-Windows // versions use because it would open the file without the FILE_SHARE_DELETE flag we need. // HANDLE newFileHandle = CreateFileA( _path.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if ( INVALID_HANDLE_VALUE != newFileHandle ) { int newFileDescriptor = _open_osfhandle( reinterpret_cast<intptr_t>(newFileHandle), _O_APPEND ); tmp = _fdopen( newFileDescriptor, _append ? "a" : "w" ); } #else tmp = freopen(_path.c_str(), _append ? "a" : "w", stdout); #endif if ( !tmp ) { cerr << "can't open: " << _path.c_str() << " for log file" << endl; return false; } // redirect stdout and stderr to log file dup2( fileno( tmp ), 1 ); // stdout dup2( fileno( tmp ), 2 ); // stderr Logstream::setLogFile(tmp); // after this point no thread will be using old file #if _WIN32 if ( _file ) fclose( _file ); // In Windows, we still have the old file open, close it now #endif #if 0 // enable to test redirection cout << "written to cout" << endl; cerr << "written to cerr" << endl; log() << "written to log()" << endl; #endif _file = tmp; // Save new file for next rotation return true; }
int ConfigServer::checkConfigVersion( bool upgrade ) { int cur = dbConfigVersion(); if ( cur == VERSION ) return 0; if ( cur == 0 ) { scoped_ptr<ScopedDbConnection> conn( ScopedDbConnection::getScopedDbConnection( _primary.getConnString() ) ); // If the cluster has not previously been initialized, we need to set the version before using so // subsequent mongoses use the config data the same way. This requires all three config servers online // initially. try { conn->get()->insert( "config.version" , BSON( "_id" << 1 << "version" << VERSION ) ); } catch( DBException& ) { error() << "All config servers must initially be reachable for the cluster to be initialized." << endl; throw; } pool.flush(); verify( VERSION == dbConfigVersion( conn->conn() ) ); conn->done(); return 0; } if ( cur == 2 ) { // need to upgrade verify( VERSION == 3 ); if ( ! upgrade ) { log() << "newer version of mongo meta data\n" << "need to --upgrade after shutting all mongos down" << endl; return -9; } scoped_ptr<ScopedDbConnection> connPtr( ScopedDbConnection::getInternalScopedDbConnection( _primary.getConnString() ) ); ScopedDbConnection& conn = *connPtr; // do a backup string backupName; { stringstream ss; ss << "config-backup-" << terseCurrentTime(false); backupName = ss.str(); } log() << "backing up config to: " << backupName << endl; conn->copyDatabase( "config" , backupName ); map<string,string> hostToShard; set<string> shards; // shards { unsigned n = 0; auto_ptr<DBClientCursor> c = conn->query( ShardNS::shard , BSONObj() ); while ( c->more() ) { BSONObj o = c->next(); string host = o["host"].String(); string name = ""; BSONElement id = o["_id"]; if ( id.type() == String ) { name = id.String(); } else { stringstream ss; ss << "shard" << hostToShard.size(); name = ss.str(); } hostToShard[host] = name; shards.insert( name ); n++; } verify( n == hostToShard.size() ); verify( n == shards.size() ); conn->remove( ShardNS::shard , BSONObj() ); for ( map<string,string>::iterator i=hostToShard.begin(); i != hostToShard.end(); i++ ) { conn->insert( ShardNS::shard , BSON( "_id" << i->second << "host" << i->first ) ); } } // databases { auto_ptr<DBClientCursor> c = conn->query( ShardNS::database , BSONObj() ); map<string,BSONObj> newDBs; unsigned n = 0; while ( c->more() ) { BSONObj old = c->next(); n++; if ( old["name"].eoo() ) { // already done newDBs[old["_id"].String()] = old; continue; } BSONObjBuilder b(old.objsize()); b.appendAs( old["name"] , "_id" ); BSONObjIterator i(old); while ( i.more() ) { BSONElement e = i.next(); if ( strcmp( "_id" , e.fieldName() ) == 0 || strcmp( "name" , e.fieldName() ) == 0 ) { continue; } b.append( e ); } BSONObj x = b.obj(); log() << old << "\n\t" << x << endl; newDBs[old["name"].String()] = x; } verify( n == newDBs.size() ); conn->remove( ShardNS::database , BSONObj() ); for ( map<string,BSONObj>::iterator i=newDBs.begin(); i!=newDBs.end(); i++ ) { conn->insert( ShardNS::database , i->second ); } } // chunks { unsigned num = 0; map<string,BSONObj> chunks; auto_ptr<DBClientCursor> c = conn->query( ShardNS::chunk , BSONObj() ); while ( c->more() ) { BSONObj x = c->next(); BSONObjBuilder b; string id = Chunk::genID( x["ns"].String() , x["min"].Obj() ); b.append( "_id" , id ); BSONObjIterator i(x); while ( i.more() ) { BSONElement e = i.next(); if ( strcmp( e.fieldName() , "_id" ) == 0 ) continue; b.append( e ); } BSONObj n = b.obj(); log() << x << "\n\t" << n << endl; chunks[id] = n; num++; } verify( num == chunks.size() ); conn->remove( ShardNS::chunk , BSONObj() ); for ( map<string,BSONObj>::iterator i=chunks.begin(); i!=chunks.end(); i++ ) { conn->insert( ShardNS::chunk , i->second ); } } conn->update( "config.version" , BSONObj() , BSON( "_id" << 1 << "version" << VERSION ) ); conn.done(); pool.flush(); return 1; } log() << "don't know how to upgrade " << cur << " to " << VERSION << endl; return -8; }
int ConfigServer::checkConfigVersion( bool upgrade ) { int cur = dbConfigVersion(); if ( cur == VERSION ) return 0; if ( cur == 0 ) { ScopedDbConnection conn( _primary ); conn->insert( "config.version" , BSON( "_id" << 1 << "version" << VERSION ) ); pool.flush(); assert( VERSION == dbConfigVersion( conn.conn() ) ); conn.done(); return 0; } if ( cur == 2 ) { // need to upgrade assert( VERSION == 3 ); if ( ! upgrade ) { log() << "newer version of mongo meta data\n" << "need to --upgrade after shutting all mongos down" << endl; return -9; } ScopedDbConnection conn( _primary ); // do a backup string backupName; { stringstream ss; ss << "config-backup-" << terseCurrentTime(false); backupName = ss.str(); } log() << "backing up config to: " << backupName << endl; conn->copyDatabase( "config" , backupName ); map<string,string> hostToShard; set<string> shards; // shards { unsigned n = 0; auto_ptr<DBClientCursor> c = conn->query( ShardNS::shard , BSONObj() ); while ( c->more() ) { BSONObj o = c->next(); string host = o["host"].String(); string name = ""; BSONElement id = o["_id"]; if ( id.type() == String ) { name = id.String(); } else { stringstream ss; ss << "shard" << hostToShard.size(); name = ss.str(); } hostToShard[host] = name; shards.insert( name ); n++; } assert( n == hostToShard.size() ); assert( n == shards.size() ); conn->remove( ShardNS::shard , BSONObj() ); for ( map<string,string>::iterator i=hostToShard.begin(); i != hostToShard.end(); i++ ) { conn->insert( ShardNS::shard , BSON( "_id" << i->second << "host" << i->first ) ); } } // databases { auto_ptr<DBClientCursor> c = conn->query( ShardNS::database , BSONObj() ); map<string,BSONObj> newDBs; unsigned n = 0; while ( c->more() ) { BSONObj old = c->next(); n++; if ( old["name"].eoo() ) { // already done newDBs[old["_id"].String()] = old; continue; } BSONObjBuilder b(old.objsize()); b.appendAs( old["name"] , "_id" ); BSONObjIterator i(old); while ( i.more() ) { BSONElement e = i.next(); if ( strcmp( "_id" , e.fieldName() ) == 0 || strcmp( "name" , e.fieldName() ) == 0 ) { continue; } b.append( e ); } BSONObj x = b.obj(); log() << old << "\n\t" << x << endl; newDBs[old["name"].String()] = x; } assert( n == newDBs.size() ); conn->remove( ShardNS::database , BSONObj() ); for ( map<string,BSONObj>::iterator i=newDBs.begin(); i!=newDBs.end(); i++ ) { conn->insert( ShardNS::database , i->second ); } } // chunks { unsigned num = 0; map<string,BSONObj> chunks; auto_ptr<DBClientCursor> c = conn->query( ShardNS::chunk , BSONObj() ); while ( c->more() ) { BSONObj x = c->next(); BSONObjBuilder b; string id = Chunk::genID( x["ns"].String() , x["min"].Obj() ); b.append( "_id" , id ); BSONObjIterator i(x); while ( i.more() ) { BSONElement e = i.next(); if ( strcmp( e.fieldName() , "_id" ) == 0 ) continue; b.append( e ); } BSONObj n = b.obj(); log() << x << "\n\t" << n << endl; chunks[id] = n; num++; } assert( num == chunks.size() ); conn->remove( ShardNS::chunk , BSONObj() ); for ( map<string,BSONObj>::iterator i=chunks.begin(); i!=chunks.end(); i++ ) { conn->insert( ShardNS::chunk , i->second ); } } conn->update( "config.version" , BSONObj() , BSON( "_id" << 1 << "version" << VERSION ) ); conn.done(); pool.flush(); return 1; } log() << "don't know how to upgrade " << cur << " to " << VERSION << endl; return -8; }