summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShaun Verch <shaun.verch@10gen.com>2013-10-02 17:47:24 -0400
committerShaun Verch <shaun.verch@10gen.com>2013-10-11 15:09:26 -0400
commit8af35e1ed355a413782a45d13ce50b582ab91355 (patch)
treebd8251362396ee3c6050cf967abecbd4886d9a5f
parent39c6861e3a012cd6cc2148cd961be9cb7502ac3e (diff)
downloadmongo-8af35e1ed355a413782a45d13ce50b582ab91355.tar.gz
SERVER-10885 Create new Console class to redirect tool logging to stderr instead of stdout
-rw-r--r--src/mongo/SConscript5
-rw-r--r--src/mongo/logger/console_appender.h4
-rw-r--r--src/mongo/tools/bsondump_options.cpp3
-rw-r--r--src/mongo/tools/dump.cpp10
-rw-r--r--src/mongo/tools/export.cpp16
-rw-r--r--src/mongo/tools/mongodump_options.cpp6
-rw-r--r--src/mongo/tools/mongoexport_options.cpp9
-rw-r--r--src/mongo/tools/stat.cpp4
-rw-r--r--src/mongo/tools/tool.cpp21
-rw-r--r--src/mongo/tools/tool.h10
-rw-r--r--src/mongo/tools/tool_logger.cpp81
-rw-r--r--src/mongo/tools/tool_logger.h39
-rw-r--r--src/mongo/tools/tool_options.h3
13 files changed, 161 insertions, 50 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index e885b7d9f43..49b4d96fabe 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -806,7 +806,10 @@ mongod = env.Install(
Default( mongod )
# tools
-allToolFiles = [ "tools/tool.cpp", "tools/stat_util.cpp", "tools/tool_options.cpp" ]
+allToolFiles = ["tools/tool.cpp",
+ "tools/stat_util.cpp",
+ "tools/tool_options.cpp",
+ "tools/tool_logger.cpp"]
env.StaticLibrary("alltools",
allToolFiles,
LIBDEPS=["serveronly",
diff --git a/src/mongo/logger/console_appender.h b/src/mongo/logger/console_appender.h
index 214fc921148..901b98dde6f 100644
--- a/src/mongo/logger/console_appender.h
+++ b/src/mongo/logger/console_appender.h
@@ -30,7 +30,7 @@ namespace logger {
/**
* Appender for writing to the console (stdout).
*/
- template <typename Event>
+ template <typename Event, typename ConsoleType = Console>
class ConsoleAppender : public Appender<Event> {
MONGO_DISALLOW_COPYING(ConsoleAppender);
@@ -39,7 +39,7 @@ namespace logger {
explicit ConsoleAppender(EventEncoder* encoder) : _encoder(encoder) {}
virtual Status append(const Event& event) {
- Console console;
+ ConsoleType console;
_encoder->encode(event, console.out()).flush();
if (!console.out())
return Status(ErrorCodes::LogWriteFailed, "Error writing log message to console.");
diff --git a/src/mongo/tools/bsondump_options.cpp b/src/mongo/tools/bsondump_options.cpp
index f75d0dd96e2..dbeadc1d524 100644
--- a/src/mongo/tools/bsondump_options.cpp
+++ b/src/mongo/tools/bsondump_options.cpp
@@ -90,6 +90,9 @@ namespace mongo {
toolGlobalParams.db = "";
}
+ // bsondump always outputs data to stdout, so we can't send messages there
+ toolGlobalParams.canUseStdout = false;
+
return Status::OK();
}
diff --git a/src/mongo/tools/dump.cpp b/src/mongo/tools/dump.cpp
index a58640a57c1..9d163d311d0 100644
--- a/src/mongo/tools/dump.cpp
+++ b/src/mongo/tools/dump.cpp
@@ -41,15 +41,7 @@ class Dump : public Tool {
FILE* _f;
};
public:
- Dump() : Tool(true/*usesstdout*/) { }
-
- virtual void preSetup() {
- if (mongoDumpGlobalParams.outputFile == "-") {
- // write output to standard error to avoid mangling output
- // must happen early to avoid sending junk to stdout
- useStandardOutput(false);
- }
- }
+ Dump() : Tool() { }
virtual void printHelp(ostream& out) {
printMongoDumpHelp(&out);
diff --git a/src/mongo/tools/export.cpp b/src/mongo/tools/export.cpp
index f135d9853f3..1b1c38c5a0e 100644
--- a/src/mongo/tools/export.cpp
+++ b/src/mongo/tools/export.cpp
@@ -25,24 +25,14 @@
#include "mongo/db/json.h"
#include "mongo/tools/mongoexport_options.h"
#include "mongo/tools/tool.h"
+#include "mongo/tools/tool_logger.h"
#include "mongo/util/options_parser/option_section.h"
using namespace mongo;
class Export : public Tool {
public:
- Export() : Tool(false/*usesstdout*/) { }
-
- virtual void preSetup() {
- if (mongoExportGlobalParams.outputFileSpecified) {
- if (mongoExportGlobalParams.outputFile != "-") {
- // we write output to standard error by default to avoid
- // mangling output, but we don't need to do this if an output
- // file was specified
- useStandardOutput(true);
- }
- }
- }
+ Export() : Tool() { }
virtual void printHelp( ostream & out ) {
printMongoExportHelp(&out);
@@ -226,7 +216,7 @@ public:
out << ']' << endl;
if (!toolGlobalParams.quiet) {
- (_usesstdout ? cout : cerr ) << "exported " << num << " records" << endl;
+ toolOutput() << "exported " << num << " records" << endl;
}
return 0;
diff --git a/src/mongo/tools/mongodump_options.cpp b/src/mongo/tools/mongodump_options.cpp
index c132079acb9..99fb70b8e1c 100644
--- a/src/mongo/tools/mongodump_options.cpp
+++ b/src/mongo/tools/mongodump_options.cpp
@@ -130,6 +130,12 @@ namespace mongo {
toolGlobalParams.db = "";
}
+ if (mongoDumpGlobalParams.outputFile == "-") {
+ // write output to standard error to avoid mangling output
+ // must happen early to avoid sending junk to stdout
+ toolGlobalParams.canUseStdout = false;
+ }
+
return Status::OK();
}
diff --git a/src/mongo/tools/mongoexport_options.cpp b/src/mongo/tools/mongoexport_options.cpp
index 375e2ff624b..b6148046d80 100644
--- a/src/mongo/tools/mongoexport_options.cpp
+++ b/src/mongo/tools/mongoexport_options.cpp
@@ -138,6 +138,15 @@ namespace mongo {
mongoExportGlobalParams.limit = getParam("limit", 0);
mongoExportGlobalParams.skip = getParam("skip", 0);
+ // we write output to standard error by default to avoid mangling output, but we don't need
+ // to do this if an output file was specified
+ toolGlobalParams.canUseStdout = false;
+ if (mongoExportGlobalParams.outputFileSpecified) {
+ if (mongoExportGlobalParams.outputFile != "-") {
+ toolGlobalParams.canUseStdout = true;
+ }
+ }
+
return Status::OK();
}
diff --git a/src/mongo/tools/stat.cpp b/src/mongo/tools/stat.cpp
index 15baf05b831..0c9cb612fa6 100644
--- a/src/mongo/tools/stat.cpp
+++ b/src/mongo/tools/stat.cpp
@@ -81,10 +81,6 @@ namespace mongo {
return out.getOwned();
}
-
- virtual void preSetup() {
- }
-
int run() {
_statUtil.setAll(mongoStatGlobalParams.allFields);
_statUtil.setSeconds(mongoStatGlobalParams.sleep);
diff --git a/src/mongo/tools/tool.cpp b/src/mongo/tools/tool.cpp
index 8d9a396a29f..af40129f9da 100644
--- a/src/mongo/tools/tool.cpp
+++ b/src/mongo/tools/tool.cpp
@@ -44,8 +44,8 @@ using namespace mongo;
namespace mongo {
- Tool::Tool(bool usesstdout) :
- _usesstdout(usesstdout), _autoreconnect(false), _conn(0), _slaveConn(0) { }
+ Tool::Tool() :
+ _autoreconnect(false), _conn(0), _slaveConn(0) { }
Tool::~Tool() {
if ( _conn )
@@ -69,8 +69,6 @@ namespace mongo {
}
}
- preSetup();
-
if (!toolGlobalParams.useDirectClient) {
if (toolGlobalParams.noconnection) {
// do nothing
@@ -94,9 +92,8 @@ namespace mongo {
}
if (!toolGlobalParams.quiet) {
- (_usesstdout ? std::cout : std::cerr ) << "connected to: "
- << toolGlobalParams.connectionString
- << std::endl;
+ toolOutput() << "connected to: " << toolGlobalParams.connectionString
+ << std::endl;
}
}
@@ -243,7 +240,7 @@ namespace mongo {
toolGlobalParams.authenticationMechanism));
}
- BSONTool::BSONTool() : Tool(false/*usesstdout*/) { }
+ BSONTool::BSONTool() : Tool() { }
int BSONTool::run() {
@@ -261,7 +258,7 @@ namespace mongo {
if ( fileLength == 0 ) {
if (!toolGlobalParams.quiet) {
- (_usesstdout ? cout : cerr ) << "file " << fileName << " empty, skipping" << endl;
+ toolOutput() << "file " << fileName << " empty, skipping" << endl;
}
return 0;
}
@@ -279,7 +276,7 @@ namespace mongo {
if (!toolGlobalParams.quiet &&
logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1))) {
- (_usesstdout ? cout : cerr ) << "\t file size: " << fileLength << endl;
+ toolOutput() << "\t file size: " << fileLength << endl;
}
unsigned long long read = 0;
@@ -336,9 +333,9 @@ namespace mongo {
uassert( 10265 , "counts don't match" , m.done() == fileLength );
if (!toolGlobalParams.quiet) {
- (_usesstdout ? cout : cerr ) << m.hits() << " objects found" << endl;
+ toolOutput() << m.hits() << " objects found" << std::endl;
if (bsonToolGlobalParams.hasFilter)
- (_usesstdout ? cout : cerr ) << processed << " objects processed" << endl;
+ toolOutput() << processed << " objects processed" << std::endl;
}
return processed;
}
diff --git a/src/mongo/tools/tool.h b/src/mongo/tools/tool.h
index d422e8507cb..8138aaeffa2 100644
--- a/src/mongo/tools/tool.h
+++ b/src/mongo/tools/tool.h
@@ -26,6 +26,7 @@
#include "mongo/db/instance.h"
#include "mongo/db/matcher.h"
+#include "mongo/tools/tool_logger.h"
#include "mongo/tools/tool_options.h"
#include "mongo/util/options_parser/environment.h"
@@ -35,7 +36,7 @@ namespace mongo {
class Tool {
public:
- Tool(bool usesstdout=true);
+ Tool();
virtual ~Tool();
static auto_ptr<Tool> (*createInstance)();
@@ -52,14 +53,8 @@ namespace mongo {
string getAuthenticationDatabase();
- void useStandardOutput( bool mode ) {
- _usesstdout = mode;
- }
-
bool isMaster();
bool isMongos();
-
- virtual void preSetup() {}
virtual int run() = 0;
@@ -69,7 +64,6 @@ namespace mongo {
mongo::DBClientBase &conn( bool slaveIfPaired = false );
- bool _usesstdout;
bool _autoreconnect;
protected:
diff --git a/src/mongo/tools/tool_logger.cpp b/src/mongo/tools/tool_logger.cpp
new file mode 100644
index 00000000000..f719ba0b464
--- /dev/null
+++ b/src/mongo/tools/tool_logger.cpp
@@ -0,0 +1,81 @@
+/* Copyright 2013 10gen Inc.
+ *
+ * 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.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/tools/tool_logger.h"
+
+#include <iostream>
+
+#include "mongo/base/init.h"
+#include "mongo/logger/console_appender.h"
+#include "mongo/logger/log_manager.h"
+#include "mongo/logger/logger.h"
+#include "mongo/logger/message_event.h"
+#include "mongo/logger/message_event_utf8_encoder.h"
+#include "mongo/tools/tool_options.h"
+
+namespace mongo {
+namespace {
+
+ /*
+ * Theory of operation:
+ *
+ * At process start, the loader initializes "consoleMutex" to NULL. At some point during static
+ * initialization, the static initialization process, running in the one and only extant thread,
+ * allocates a new boost::mutex on the heap and assigns consoleMutex to point to it. While
+ * consoleMutex is still NULL, we know that there is only one thread extant, so it is safe to
+ * skip locking the consoleMutex in the ErrorConsole constructor. Once the mutex is initialized,
+ * users of ErrorConsole can start acquiring it.
+ */
+
+ boost::mutex *consoleMutex = new boost::mutex;
+
+} // namespace
+
+ ErrorConsole::ErrorConsole() : _consoleLock() {
+ if (consoleMutex) {
+ boost::unique_lock<boost::mutex> lk(*consoleMutex);
+ lk.swap(_consoleLock);
+ }
+ }
+
+ std::ostream& ErrorConsole::out() { return std::cerr; }
+
+MONGO_INITIALIZER_GENERAL(ToolLogRedirection,
+ ("GlobalLogManager", "EndStartupOptionHandling"),
+ ("default"))(InitializerContext*) {
+
+ using logger::MessageEventEphemeral;
+ using logger::MessageEventDetailsEncoder;
+ using logger::MessageLogDomain;
+ using logger::ConsoleAppender;
+
+ // If we are outputting data to stdout, we may need to redirect all logging to stderr
+ if (!toolGlobalParams.canUseStdout) {
+ logger::globalLogDomain()->clearAppenders();
+ logger::globalLogDomain()->attachAppender(MessageLogDomain::AppenderAutoPtr(
+ new ConsoleAppender<MessageEventEphemeral, ErrorConsole>(
+ new MessageEventDetailsEncoder)));
+ }
+
+ return Status::OK();
+}
+
+ std::ostream& toolOutput() {
+ return toolGlobalParams.canUseStdout ? std::cout : std::cerr;
+ }
+
+} // namespace mongo
diff --git a/src/mongo/tools/tool_logger.h b/src/mongo/tools/tool_logger.h
new file mode 100644
index 00000000000..7d845bc28b4
--- /dev/null
+++ b/src/mongo/tools/tool_logger.h
@@ -0,0 +1,39 @@
+/* Copyright 2013 10gen Inc.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <boost/thread/mutex.hpp>
+#include <iosfwd>
+
+namespace mongo {
+
+ /**
+ * This is a version of the Console class that uses stderr for output instead of stdout. See
+ * the description of the Console class for other details about how this class should operate
+ */
+ class ErrorConsole {
+ public:
+ ErrorConsole();
+
+ std::ostream& out();
+
+ private:
+ boost::unique_lock<boost::mutex> _consoleLock;
+ };
+
+ std::ostream& toolOutput();
+
+} // namespace mongo
diff --git a/src/mongo/tools/tool_options.h b/src/mongo/tools/tool_options.h
index 470960c0b05..7e30e38a7c8 100644
--- a/src/mongo/tools/tool_options.h
+++ b/src/mongo/tools/tool_options.h
@@ -33,7 +33,7 @@ namespace mongo {
struct ToolGlobalParams {
- ToolGlobalParams() : hostSet(false), portSet(false) { }
+ ToolGlobalParams() : canUseStdout(true), hostSet(false), portSet(false) { }
std::string name;
std::string db;
@@ -45,6 +45,7 @@ namespace mongo {
std::string authenticationMechanism;
bool quiet;
+ bool canUseStdout;
bool noconnection;
std::vector<std::string> fields;