diff options
| author | Andrew Stitcher <astitcher@apache.org> | 2014-01-03 18:54:46 +0000 |
|---|---|---|
| committer | Andrew Stitcher <astitcher@apache.org> | 2014-01-03 18:54:46 +0000 |
| commit | e497a8527d42a93dce9320b5919cad93aa00a32b (patch) | |
| tree | 4aafb677ff8f5af6f01d3b07b7d8497cdeed22d8 /qpid/cpp | |
| parent | 3130ec0a9ea6bf541d0b365c0eb2081f67224474 (diff) | |
| download | qpid-python-e497a8527d42a93dce9320b5919cad93aa00a32b.tar.gz | |
QPID-5415: Implement control of internal log output in qpid::messaging API
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1555202 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/cpp')
| -rw-r--r-- | qpid/cpp/include/qpid/messaging/Logger.h | 165 | ||||
| -rw-r--r-- | qpid/cpp/src/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | qpid/cpp/src/qpid/log/Logger.cpp | 2 | ||||
| -rw-r--r-- | qpid/cpp/src/qpid/log/Options.cpp | 38 | ||||
| -rw-r--r-- | qpid/cpp/src/qpid/log/Options.h | 6 | ||||
| -rw-r--r-- | qpid/cpp/src/qpid/log/Statement.cpp | 3 | ||||
| -rw-r--r-- | qpid/cpp/src/qpid/log/Statement.h | 3 | ||||
| -rw-r--r-- | qpid/cpp/src/qpid/messaging/Logger.cpp | 200 | ||||
| -rw-r--r-- | qpid/cpp/src/tests/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | qpid/cpp/src/tests/MessagingLogger.cpp | 149 |
10 files changed, 555 insertions, 20 deletions
diff --git a/qpid/cpp/include/qpid/messaging/Logger.h b/qpid/cpp/include/qpid/messaging/Logger.h new file mode 100644 index 0000000000..c4d8a213f2 --- /dev/null +++ b/qpid/cpp/include/qpid/messaging/Logger.h @@ -0,0 +1,165 @@ +#ifndef QPID_MESSAGING_LOGGING_H +#define QPID_MESSAGING_LOGGING_H + +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 "qpid/messaging/ImportExport.h" + +#include <string> + +namespace qpid { +namespace messaging { +/** + * These log levels need to be kept in sync with the log levels + * defined internally in qpid::log (but I don't think they are likely + * to change anyway + */ +enum Level { trace, debug, info, notice, warning, error, critical }; + +/** \ingroup messaging + * Interface class to allow redirection of log output + */ +class QPID_MESSAGING_CLASS_EXTERN LoggerOutput +{ +public: + virtual ~LoggerOutput(); + + /** + * Override this member function to receive log messages. + * + * log() will be called for every log message that would be output from the qpid::messaging + * logging subsystem after applying the specified logging filters. + * + * The logging subsystem ensures that log() will not be called simultaneously in different threads. + * @param level The severity of the log message can be (in order of severity) + * trace, debug, info, notice, warning, error, critical + * @param user Flag which is set if the log message came from the user application ( using qpid::messaging::Logger::log() ) + * (if not set then the message comes from the qpid library) + * @param file The source code file name reported as the origin of the log message + * @param line The source code line number reported as the origin of the log message + * @param function The source code function reported as the origin of the log message + * @param message The log message + */ + virtual void log(Level level, bool user, const char* file, int line, const char* function, const std::string& message) = 0; +}; + +/** \ingroup messaging + * A utility class to allow the application to control the logging + * output of the qpid messaging library + * + * This class represents a singleton logging facility within the qpid messaging library so there are only static + * methods in the class + */ +class QPID_MESSAGING_CLASS_EXTERN Logger +{ +public: + /** + * Configure the logging subsystem + * + * This function takes an array of text options (which could easily come from a programs + * command line) and uses them to configure the logging subsystem. + * + * If the prefix parameter is specified then the accepted command line options are prefixed + * by <<prefix>>- for example if the prefix is "qpid" then the options all start "--qpid-log..." + * + * Accepted options are: + * --log-enable RULE + * --log-disable RULE + * + * Both --log-enable and --log-disable can be specified multiple times in a single command line. + * The enables are acted upon and after them the disables are acted upon. + * + * RULE is in the form LEVEL[("+"|"-")][:PATTERN] + * LEVEL is one of "trace", "debug", "info", "notice", "warning", "error", "critical" + * "+" operates on the level and all higher levels + * "-" operates on the level and all lower levels + * PATTERN is a category name or a fragment of a fully namespace qualified function (Case sensitive). + * + * --log-to-stdout ("on"|"off|"0"|"1") + * --log-to-stderr ("on"|"off|"0"|"1") + * --log-to-file FILENAME + * + * These options control where the qpid logging subsystem sends log messages + * + * --log-time ("on"|"off|"0"|"1") + * --log-level ("on"|"off|"0"|"1") + * --log-source ("on"|"off|"0"|"1") + * --log-thread ("on"|"off|"0"|"1") + * --log-function ("on"|"off|"0"|"1") + * --log-hires-timestamp ("on"|"off|"0"|"1") + * + * These options control what information is included in the logging message sent by the logging subsystem. + * + * @param argc count of options - identical to meaning for main(). + * @param argv array of pointers to options - identical to meaning for main(). + * @param prefix (optional) If present prefix all logging options with this string + * @throws MessagingException if it cannot parse an option it recognises + */ + QPID_MESSAGING_EXTERN static void configure(int argc, const char* argv[], const std::string& prefix=std::string()); + + /** + * Get a user friendly usage message. + * + * This returns a usage message that is suitable for outputting directly to + * a console user. The message only contains the command line options that + * are understood by qpid::messaging::Logger::configure(). + * + * NB. You must call qpid::messaging::Logger::configure() before calling this + * to populate the usage string as the usage string depends on the prefix that + * is passed in to qpid::messaging::Logger::configure(). + * + * @return string containing the usage message for the command line options + */ + QPID_MESSAGING_EXTERN static std::string usage(); + + /** + * Register a custom handler for log messages + * + * This allows application programs to intercept the log messages coming from qpid::messaging + * and handle them in whatever way is consonent with the applications own handling of + * log messages. + * + * In order to do this create a class that inherits from qpid::messaging::LoggerOutput + * and override the log() member function. + */ + QPID_MESSAGING_EXTERN static void setOutput(LoggerOutput& output); + + /** + * Output a log message. This will get sent to all the specified logging outputs including any + * the application has registered. The message will get filtered along with the internal messages + * according to the specified logging filters. + * + * When a log message output using log() is received by a LoggerOutput::log() method the "user" bool parameter will be set true. + */ + QPID_MESSAGING_EXTERN static void log(Level level, const char* file, int line, const char* function, const std::string& message); + +private: + //This class has only one instance so no need to copy + Logger(); + ~Logger(); + + Logger(const Logger&); + Logger operator=(const Logger&); +}; +}} // namespace qpid::messaging + +#endif /*!QPID_MESSAGING_LOGGING_H*/ diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt index 70288c7661..c57061c0e3 100644 --- a/qpid/cpp/src/CMakeLists.txt +++ b/qpid/cpp/src/CMakeLists.txt @@ -1055,6 +1055,7 @@ set (qpidmessaging_SOURCES qpid/messaging/Duration.cpp qpid/messaging/exceptions.cpp qpid/messaging/FailoverUpdates.cpp + qpid/messaging/Logger.cpp qpid/messaging/Message.cpp qpid/messaging/Receiver.cpp qpid/messaging/Session.cpp @@ -1073,7 +1074,7 @@ set (qpidmessaging_SOURCES add_msvc_version (qpidmessaging library dll) add_library (qpidmessaging SHARED ${qpidmessaging_SOURCES}) -target_link_libraries (qpidmessaging qpidtypes qpidclient qpidcommon ${PROTON_LIBRARIES}) +target_link_libraries (qpidmessaging qpidtypes qpidclient qpidcommon "${Boost_PROGRAM_OPTIONS_LIBRARY}" ${PROTON_LIBRARIES}) set_target_properties (qpidmessaging PROPERTIES LINK_FLAGS "${HIDE_SYMBOL_FLAGS} ${LINK_VERSION_SCRIPT_FLAG}" COMPILE_FLAGS "${HIDE_SYMBOL_FLAGS}" diff --git a/qpid/cpp/src/qpid/log/Logger.cpp b/qpid/cpp/src/qpid/log/Logger.cpp index 8242acc87e..8dd970c3b9 100644 --- a/qpid/cpp/src/qpid/log/Logger.cpp +++ b/qpid/cpp/src/qpid/log/Logger.cpp @@ -68,7 +68,7 @@ Logger::Logger() : flags(0) { // Initialize myself from env variables so all programs // (e.g. tests) can use logging even if they don't parse // command line args. - Options opts(""); + Options opts; opts.parse(0, 0); configure(opts); } diff --git a/qpid/cpp/src/qpid/log/Options.cpp b/qpid/cpp/src/qpid/log/Options.cpp index b310b7cfac..f816124b4e 100644 --- a/qpid/cpp/src/qpid/log/Options.cpp +++ b/qpid/cpp/src/qpid/log/Options.cpp @@ -27,8 +27,6 @@ namespace qpid { namespace log { -using namespace std; - Options::Options(const std::string& argv0_, const std::string& name_) : qpid::Options(name_), argv0(argv0_), @@ -45,25 +43,15 @@ Options::Options(const std::string& argv0_, const std::string& name_) : { selectors.push_back("notice+"); - ostringstream levels; - levels << LevelTraits::name(Level(0)); - for (int i = 1; i < LevelTraits::COUNT; ++i) - levels << " " << LevelTraits::name(Level(i)); - - ostringstream categories; - categories << CategoryTraits::name(Category(0)); - for (int i = 1; i < CategoryTraits::COUNT; ++i) - categories << " " << CategoryTraits::name(Category(i)); - addOptions() ("trace,t", optValue(trace), "Enables all logging" ) ("log-enable", optValue(selectors, "RULE"), ("Enables logging for selected levels and components. " "RULE is in the form 'LEVEL[+-][:PATTERN]'\n" - "LEVEL is one of: \n\t "+levels.str()+"\n" + "LEVEL is one of: \n\t "+getLevels()+"\n" "PATTERN is a logging category name, or a namespace-qualified " "function name or name fragment. " - "Logging category names are: \n\t "+categories.str()+"\n" + "Logging category names are: \n\t "+getCategories()+"\n" "For example:\n" "\t'--log-enable warning+'\n" "logs all warning, error and critical messages.\n" @@ -75,10 +63,10 @@ Options::Options(const std::string& argv0_, const std::string& name_) : ("log-disable", optValue(deselectors, "RULE"), ("Disables logging for selected levels and components. " "RULE is in the form 'LEVEL[+-][:PATTERN]'\n" - "LEVEL is one of: \n\t "+levels.str()+"\n" + "LEVEL is one of: \n\t "+getLevels()+"\n" "PATTERN is a logging category name, or a namespace-qualified " "function name or name fragment. " - "Logging category names are: \n\t "+categories.str()+"\n" + "Logging category names are: \n\t "+getCategories()+"\n" "For example:\n" "\t'--log-disable warning-'\n" "disables logging all warning, notice, info, debug, and trace messages.\n" @@ -139,4 +127,22 @@ Options& Options::operator=(const Options& x) { return *this; } +std::string getLevels() +{ + std::ostringstream levels; + levels << LevelTraits::name(Level(0)); + for (int i = 1; i < LevelTraits::COUNT; ++i) + levels << " " << LevelTraits::name(Level(i)); + return levels.str(); +} + +std::string getCategories() +{ + std::ostringstream categories; + categories << CategoryTraits::name(Category(0)); + for (int i = 1; i < CategoryTraits::COUNT; ++i) + categories << " " << CategoryTraits::name(Category(i)); + return categories.str(); +} + }} // namespace qpid::log diff --git a/qpid/cpp/src/qpid/log/Options.h b/qpid/cpp/src/qpid/log/Options.h index 42a8fb40fe..ed534168e8 100644 --- a/qpid/cpp/src/qpid/log/Options.h +++ b/qpid/cpp/src/qpid/log/Options.h @@ -46,6 +46,12 @@ struct Options : public qpid::Options { std::auto_ptr<SinkOptions> sinkOptions; }; +/** Get a string list of the allowed levels */ +QPID_COMMON_EXTERN std::string getLevels(); + +/** Get a string list of the allowed categories */ +QPID_COMMON_EXTERN std::string getCategories(); + }} // namespace qpid::log #endif /*!QPID_LOG_OPTIONS_H*/ diff --git a/qpid/cpp/src/qpid/log/Statement.cpp b/qpid/cpp/src/qpid/log/Statement.cpp index e70069fe7b..e91756cb9a 100644 --- a/qpid/cpp/src/qpid/log/Statement.cpp +++ b/qpid/cpp/src/qpid/log/Statement.cpp @@ -200,7 +200,7 @@ const char* names[LevelTraits::COUNT] = { const char* catNames[CategoryTraits::COUNT] = { "Security", "Broker", "Management", "Protocol", "System", "HA", "Messaging", - "Store", "Network", "Test", "Client", "Model", "Unspecified" + "Store", "Network", "Test", "Client", "Application", "Model", "Unspecified" }; } // namespace @@ -235,4 +235,5 @@ Category CategoryTraits::category(const char* name) { const char* CategoryTraits::name(Category c) { return catNames[c]; } + }} // namespace qpid::log diff --git a/qpid/cpp/src/qpid/log/Statement.h b/qpid/cpp/src/qpid/log/Statement.h index e928e19f22..20fd3745b6 100644 --- a/qpid/cpp/src/qpid/log/Statement.h +++ b/qpid/cpp/src/qpid/log/Statement.h @@ -71,11 +71,12 @@ struct LevelTraits { * Store store * Network tcp rdma AsynchIO socket epoll * Test + * External_application <no directory - signifies log message from non qpid application code> * Model <not related to a directory> * Unspecified <must be last in enum> */ enum Category { security, broker, management, protocol, system, ha, messaging, - store, network, test, client, model, unspecified }; + store, network, test, client, external_application, model, unspecified }; struct CategoryTraits { static const int COUNT=unspecified+1; diff --git a/qpid/cpp/src/qpid/messaging/Logger.cpp b/qpid/cpp/src/qpid/messaging/Logger.cpp new file mode 100644 index 0000000000..c259cb4c1b --- /dev/null +++ b/qpid/cpp/src/qpid/messaging/Logger.cpp @@ -0,0 +1,200 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 "qpid/messaging/Logger.h" + +#include "qpid/log/Logger.h" +#include "qpid/log/OstreamOutput.h" +#include "qpid/messaging/exceptions.h" + +#include <sstream> +#include <string> +#include <vector> + +using std::string; +using std::vector; + +namespace qpid { +namespace messaging { + +// Proxy class to call the users output class/routine +class ProxyOutput : public qpid::log::Logger::Output { + LoggerOutput& output; + + void log(const qpid::log::Statement& s, const string& message) { + output.log(qpid::messaging::Level(s.level), s.category==qpid::log::external_application, s.file, s.line, s.function, message); + } + +public: + ProxyOutput(LoggerOutput& o) : + output(o) + {} +}; + +LoggerOutput::~LoggerOutput() +{ +} + +inline qpid::log::Logger& logger() { + static qpid::log::Logger& theLogger=qpid::log::Logger::instance(); + return theLogger; +} + +namespace { + std::string loggerUsage; + qpid::log::Selector loggerSelector; +} + +std::string Logger::usage() +{ + return loggerUsage; +} + +void Logger::configure(int argc, const char* argv[], const string& pre) +try +{ + bool logToStdout = false; + bool logToStderr = false; + string logFile; + std::vector<std::string> selectors; + std::vector<std::string> deselectors; + bool time = false; + bool level = false; + bool thread = false; + bool source = false; + bool function = false; + bool hiresTs = false; + + selectors.push_back("notice+"); // Set this for the usage message default + + string prefix = pre.empty() ? pre : pre+"-"; + qpid::Options myOptions; + myOptions.addOptions() + ((prefix+"log-enable").c_str(), optValue(selectors, "RULE"), + ("Enables logging for selected levels and components. " + "RULE is in the form 'LEVEL[+-][:PATTERN]'\n" + "LEVEL is one of: \n\t "+qpid::log::getLevels()+"\n" + "PATTERN is a logging category name, or a namespace-qualified " + "function name or name fragment. " + "Logging category names are: \n\t "+qpid::log::getCategories()+"\n" + "The category \"Application\" contains all messages logged by the application.\n" + "For example:\n" + "\t'--log-enable warning+'\n" + "logs all warning, error and critical messages.\n" + "\t'--log-enable trace+:Application --log-enable notice+'\n" + "logs all application messages, but only notice or higher for the qpid library messages\n" + "\t'--log-enable debug:framing'\n" + "logs debug messages from all functions with 'framing' in the namespace or function name.\n" + "This option can be used multiple times").c_str()) + ((prefix+"log-disable").c_str(), optValue(deselectors, "RULE"), + ("Disables logging for selected levels and components. " + "RULE is in the form 'LEVEL[+-][:PATTERN]'\n" + "LEVEL is one of: \n\t "+qpid::log::getLevels()+"\n" + "PATTERN is a logging category name, or a namespace-qualified " + "function name or name fragment. " + "Logging category names are: \n\t "+qpid::log::getCategories()+"\n" + "For example:\n" + "\t'--log-disable warning-'\n" + "disables logging all warning, notice, info, debug, and trace messages.\n" + "\t'--log-disable trace:Application'\n" + "disables all application trace messages.\n" + "\t'--log-disable debug-:qmf::'\n" + "disables logging debug and trace messages from all functions with 'qmf::' in the namespace.\n" + "This option can be used multiple times").c_str()) + ((prefix+"log-time").c_str(), optValue(time, "yes|no"), "Include time in log messages") + ((prefix+"log-level").c_str(), optValue(level,"yes|no"), "Include severity level in log messages") + ((prefix+"log-source").c_str(), optValue(source,"yes|no"), "Include source file:line in log messages") + ((prefix+"log-thread").c_str(), optValue(thread,"yes|no"), "Include thread ID in log messages") + ((prefix+"log-function").c_str(), optValue(function,"yes|no"), "Include function signature in log messages") + ((prefix+"log-hires-timestamp").c_str(), optValue(hiresTs,"yes|no"), "Use hi-resolution timestamps in log messages") + ((prefix+"log-to-stderr").c_str(), optValue(logToStderr, "yes|no"), "Send logging output to stderr") + ((prefix+"log-to-stdout").c_str(), optValue(logToStdout, "yes|no"), "Send logging output to stdout") + ((prefix+"log-to-file").c_str(), optValue(logFile, "FILE"), "Send log output to FILE.") + ; + + std::ostringstream loggerSStream; + myOptions.print(loggerSStream); + + loggerUsage=loggerSStream.str(); + + selectors.clear(); // Clear to give passed in options precedence + + // Parse the command line not failing for unrecognised options + myOptions.parse(argc, argv, std::string(), true); + + // If no passed in enable or disable log specification then go back to default + if (selectors.empty() && deselectors.empty()) + selectors.push_back("notice+"); + // Set the logger options according to what we just parsed + qpid::log::Options logOptions; + logOptions.selectors = selectors; + logOptions.deselectors = deselectors; + logOptions.time = time; + logOptions.level = level; + logOptions.category = false; + logOptions.thread = thread; + logOptions.source = source; + logOptions.function = function; + logOptions.hiresTs = hiresTs; + + loggerSelector = qpid::log::Selector(logOptions); + logger().clear(); // Need to clear before configuring as it will have been initialised statically already + logger().format(logOptions); + logger().select(loggerSelector); + + // Have to set up the standard output sinks manually + if (logToStderr) + logger().output(std::auto_ptr<qpid::log::Logger::Output> + (new qpid::log::OstreamOutput(std::clog))); + if (logToStdout) + logger().output(std::auto_ptr<qpid::log::Logger::Output> + (new qpid::log::OstreamOutput(std::cout))); + + if (logFile.length() > 0) + logger().output(std::auto_ptr<qpid::log::Logger::Output> + (new qpid::log::OstreamOutput(logFile))); +} +catch (std::exception& e) +{ + throw MessagingException(e.what()); +} + +void Logger::setOutput(LoggerOutput& o) +{ + logger().output(std::auto_ptr<qpid::log::Logger::Output>(new ProxyOutput(o))); +} + +void Logger::log(Level level, const char* file, int line, const char* function, const string& message) +{ + if (loggerSelector.isEnabled(qpid::log::Level(level), function, qpid::log::unspecified)) { + qpid::log::Statement s = { + true, + file, + line, + function, + qpid::log::Level(level), + qpid::log::external_application, + }; + logger().log(s, message); + } +} + +}} // namespace qpid::messaging diff --git a/qpid/cpp/src/tests/CMakeLists.txt b/qpid/cpp/src/tests/CMakeLists.txt index c0a48d8dd3..125982a26e 100644 --- a/qpid/cpp/src/tests/CMakeLists.txt +++ b/qpid/cpp/src/tests/CMakeLists.txt @@ -42,6 +42,11 @@ macro(remember_location testname) set (${testname}_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/${testname}${CMAKE_EXECUTABLE_SUFFIX}) endmacro(remember_location) +# If we're using GCC allow variadic macros (even though they're c99 not c++01) +if (CMAKE_COMPILER_IS_GNUCXX) + add_definitions(-Wno-variadic-macros) +endif (CMAKE_COMPILER_IS_GNUCXX) + # Windows uses some process-startup calls to ensure that errors, etc. don't # result in error boxes being thrown up. Since it's expected that most test # runs will be in scripts, the default is to force these outputs to stderr @@ -175,6 +180,7 @@ set(all_unit_tests ManagementTest MessageReplayTracker MessageTest + MessagingLogger MessagingSessionTests PollableCondition ProxyTest diff --git a/qpid/cpp/src/tests/MessagingLogger.cpp b/qpid/cpp/src/tests/MessagingLogger.cpp new file mode 100644 index 0000000000..195a33db12 --- /dev/null +++ b/qpid/cpp/src/tests/MessagingLogger.cpp @@ -0,0 +1,149 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 "qpid/log/Statement.h" +#include "qpid/messaging/Connection.h" +#include "qpid/messaging/exceptions.h" +#include "qpid/messaging/Logger.h" + +#include <iostream> +#include <memory> +#include <stdexcept> + +#include <vector> + +#include "unit_test.h" + +namespace qpid { +namespace tests { + +QPID_AUTO_TEST_SUITE(MessagingLoggerSuite) + +class StringLogger : public qpid::messaging::LoggerOutput { + std::string& outString; + + void log(qpid::messaging::Level /*level*/, bool user, const char* /*file*/, int /*line*/, const char* /*function*/, const std::string& message){ + if (user) outString += "User "; + outString += message; + } + +public: + StringLogger(std::string& os) : + outString(os) + {} +}; + +#define SETUP_LOGGING(logger, ...) \ +do {\ + const char* args[]={"", __VA_ARGS__, 0};\ + qpid::messaging::Logger::configure((sizeof (args)/sizeof (char*))-1, args);\ + logOutput.clear();\ + qpid::messaging::Logger::setOutput(logger);\ +} while (0) +#define LOG_LEVEL(level)\ + QPID_LOG(level, #level " level output") +#define LOG_ALL_LOGGING_LEVELS \ +do { \ + LOG_LEVEL(trace); \ + LOG_LEVEL(debug); \ + LOG_LEVEL(info); \ + LOG_LEVEL(notice); \ + LOG_LEVEL(warning); \ + LOG_LEVEL(critical); \ +} while (0) +#define LOG_USER_LEVEL(level)\ + qpid::messaging::Logger::log(qpid::messaging::level, __FILE__, __LINE__, __FUNCTION__, #level " message") +#define LOG_ALL_USER_LOGGING_LEVELS \ +do { \ + LOG_USER_LEVEL(trace); \ + LOG_USER_LEVEL(debug); \ + LOG_USER_LEVEL(info); \ + LOG_USER_LEVEL(notice); \ + LOG_USER_LEVEL(warning); \ + LOG_USER_LEVEL(critical); \ +} while (0) + +std::string logOutput; + +QPID_AUTO_TEST_CASE(testLoggerLevels) +{ + StringLogger logger(logOutput); + + SETUP_LOGGING(logger, "--log-enable", "debug"); + LOG_ALL_LOGGING_LEVELS; + BOOST_CHECK_EQUAL(logOutput, "debug level output\ncritical level output\n"); + + SETUP_LOGGING(logger, "--log-enable", "trace+", "--log-disable", "notice"); + LOG_ALL_LOGGING_LEVELS; + BOOST_CHECK_EQUAL(logOutput, "trace level output\ndebug level output\ninfo level output\nwarning level output\ncritical level output\n"); + + SETUP_LOGGING(logger, "--log-enable", "info-"); + LOG_ALL_LOGGING_LEVELS; + BOOST_CHECK_EQUAL(logOutput, "trace level output\ndebug level output\ninfo level output\ncritical level output\n"); + + SETUP_LOGGING(logger, "--log-enable", "trace+", "--log-disable", "notice+"); + LOG_ALL_LOGGING_LEVELS; + BOOST_CHECK_EQUAL(logOutput, "trace level output\ndebug level output\ninfo level output\ncritical level output\n"); +} + +QPID_AUTO_TEST_CASE(testUserLoggerLevels) +{ + StringLogger logger(logOutput); + + SETUP_LOGGING(logger, "--log-enable", "debug"); + LOG_ALL_USER_LOGGING_LEVELS; + BOOST_CHECK_EQUAL(logOutput, "User debug message\nUser critical message\n"); + + SETUP_LOGGING(logger, "--log-enable", "trace+", "--log-disable", "notice"); + LOG_ALL_USER_LOGGING_LEVELS; + BOOST_CHECK_EQUAL(logOutput, "User trace message\nUser debug message\nUser info message\nUser warning message\nUser critical message\n"); + + SETUP_LOGGING(logger, "--log-enable", "info-"); + LOG_ALL_USER_LOGGING_LEVELS; + BOOST_CHECK_EQUAL(logOutput, "User trace message\nUser debug message\nUser info message\nUser critical message\n"); + + SETUP_LOGGING(logger, "--log-enable", "trace+", "--log-disable", "notice+"); + LOG_ALL_USER_LOGGING_LEVELS; + BOOST_CHECK_EQUAL(logOutput, "User trace message\nUser debug message\nUser info message\nUser critical message\n"); + + SETUP_LOGGING(logger, "--log-disable", "trace+"); + LOG_ALL_LOGGING_LEVELS; + LOG_ALL_USER_LOGGING_LEVELS; + BOOST_CHECK_EQUAL(logOutput, "critical level output\nUser critical message\n"); +} + +QPID_AUTO_TEST_CASE(testLoggerUsage) +{ + qpid::messaging::Logger::configure(0, 0, "blah"); + std::string u = qpid::messaging::Logger::usage(); + + BOOST_CHECK(!u.empty()); + BOOST_CHECK( u.find("--blah-log-enable")!=u.npos ); +} + +QPID_AUTO_TEST_CASE(testLoggerException) +{ + const char* args[]={"", "--blah-log-enable", "illegal", 0}; + BOOST_CHECK_THROW(qpid::messaging::Logger::configure(3, args, "blah"), qpid::messaging::MessagingException); +} + +QPID_AUTO_TEST_SUITE_END() +}} |
