diff options
| author | Alan Conway <aconway@apache.org> | 2007-05-30 16:13:58 +0000 |
|---|---|---|
| committer | Alan Conway <aconway@apache.org> | 2007-05-30 16:13:58 +0000 |
| commit | 1d63a898503d20e3221be43fc222f0091971a0ce (patch) | |
| tree | 78d8673b9ed8820e431b6a6f353ba0143adeb800 /cpp/src/qpid/log | |
| parent | f2b034ea3f4fa70e512fafdf18fd42c370194329 (diff) | |
| download | qpid-python-1d63a898503d20e3221be43fc222f0091971a0ce.tar.gz | |
Logging infrastructure: See qpidd --long-help for details.
* src/qpid/log/*: Logging infrastructure, QPID_LOG macro.
* src/*: changed output to cout/cerr to logging.
* src/qpidd.cpp: logging options.
* src/tests/Makefile.am: fixed issues with valgrind
* src/tests/kill|start_broker: use broker daemon options.
* src/tests/run_test: run tests in valgrind. Disabled till leaks in
client_test are fixed.
* src/test/unit/logging.cpp: Logging unit test using boost test framework.
Eventually we should move all unit tests to boost & drop CppUnit.
* src/test/unit/test_tools.h: useful extensions to boost framework:
Regular expression check, ostream << for vectors.
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@542855 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'cpp/src/qpid/log')
| -rw-r--r-- | cpp/src/qpid/log/Logger.cpp | 204 | ||||
| -rw-r--r-- | cpp/src/qpid/log/Logger.h | 116 | ||||
| -rw-r--r-- | cpp/src/qpid/log/Options.cpp | 69 | ||||
| -rw-r--r-- | cpp/src/qpid/log/Options.h | 42 | ||||
| -rw-r--r-- | cpp/src/qpid/log/Selector.cpp | 66 | ||||
| -rw-r--r-- | cpp/src/qpid/log/Selector.h | 68 | ||||
| -rw-r--r-- | cpp/src/qpid/log/Statement.cpp | 67 | ||||
| -rw-r--r-- | cpp/src/qpid/log/Statement.h | 124 |
8 files changed, 756 insertions, 0 deletions
diff --git a/cpp/src/qpid/log/Logger.cpp b/cpp/src/qpid/log/Logger.cpp new file mode 100644 index 0000000000..065d4508c7 --- /dev/null +++ b/cpp/src/qpid/log/Logger.cpp @@ -0,0 +1,204 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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 "Logger.h" +#include "Options.h" +#include "qpid/memory.h" +#include "qpid/sys/Thread.h" +#include <boost/pool/detail/singleton.hpp> +#include <boost/bind.hpp> +#include <boost/function.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/scoped_ptr.hpp> +#include <algorithm> +#include <sstream> +#include <fstream> +#include <iomanip> +#include <syslog.h> + + +namespace qpid { +namespace log { + +using namespace std; + +typedef sys::Mutex::ScopedLock ScopedLock; + +inline void Logger::enable_unlocked(Statement* s) { + s->enabled=selector.isEnabled(s->level, s->file); +} + +struct OstreamOutput : public Logger::Output { + OstreamOutput(std::ostream& o) : out(&o) {} + + OstreamOutput(const string& file) + : out(new ofstream(file.c_str())), mine(out) + { + if (!out->good()) + throw Exception("Can't open log file: "+file); + } + + void log(const Statement&, const std::string& m) { + *out << m << flush; + } + + ostream* out; + boost::scoped_ptr<ostream> mine; +}; + +struct SyslogOutput : public Logger::Output { + SyslogOutput(const std::string& name, int facility_=LOG_USER) + : progName(name), facility(facility_) + { + ::openlog(name.c_str(), LOG_PID, facility); + } + + ~SyslogOutput() { + ::closelog(); + } + + void log(const Statement& s, const std::string& m) + { + syslog(LevelTraits::priority(s.level), "%s", m.c_str()); + } + + std::string progName; + int facility; +}; + +Logger& Logger::instance() { + return boost::details::pool::singleton_default<Logger>::instance(); +} + +Logger::Logger() : flags(0) {} +Logger::~Logger() {} + +void Logger::select(const Selector& s) { + ScopedLock l(lock); + selector=s; + std::for_each(statements.begin(), statements.end(), + boost::bind(&Logger::enable_unlocked, this, _1)); +} + +Logger::Output::Output() {} +Logger::Output::~Output() {} + +void Logger::log(const Statement& s, const std::string& msg) { + // Format the message outside the lock. + std::ostringstream os; + if (flags&TIME) + os << boost::posix_time::second_clock::local_time() << " "; + if (flags&LEVEL) + os << LevelTraits::name(s.level) << " "; + if (flags&THREAD) + os << "[" << hex << qpid::sys::Thread::logId() << "] "; + if (flags&FILE) + os << s.file << ":"; + if (flags&LINE) + os << dec << s.line << ":"; + if (flags&FUNCTION) + os << s.function << ":"; + if (flags & (FILE|LINE|FUNCTION)) + os << " "; + os << msg << endl; + std::string formatted=os.str(); + + { + ScopedLock l(lock); + std::for_each(outputs.begin(), outputs.end(), + boost::bind(&Output::log, _1, s, formatted)); + } +} + +void Logger::output(std::auto_ptr<Output> out) { + ScopedLock l(lock); + outputs.push_back(out.release()); +} + +void Logger::output(std::ostream& out) { + output(make_auto_ptr<Output>(new OstreamOutput(out))); +} + +void Logger::syslog(const std::string& progName) { + output(make_auto_ptr<Output>(new SyslogOutput(progName))); +} + +void Logger::output(const std::string& name) { + if (name=="stderr") + output(clog); + else if (name=="stdout") + output(cout); + else if (name=="syslog") + syslog(syslogName); + else + output(make_auto_ptr<Output>(new OstreamOutput(name))); +} + +void Logger::clear() { + select(Selector()); // locked + format(0); // locked + ScopedLock l(lock); + outputs.clear(); +} + +void Logger::format(int formatFlags) { + ScopedLock l(lock); + flags=formatFlags; +} + +static int bitIf(bool test, int bit) { + return test ? bit : 0; +} + +int Logger::format(const Options& opts) { + int flags= + bitIf(opts.level, LEVEL) | + bitIf(opts.time, TIME) | + bitIf(opts.source, (FILE|LINE)) | + bitIf(opts.function, FUNCTION) | + bitIf(opts.thread, THREAD); + format(flags); + return flags; +} + +void Logger::add(Statement& s) { + ScopedLock l(lock); + enable_unlocked(&s); + statements.insert(&s); +} + +void Logger::remove(Statement& s) { + ScopedLock l(lock); + s.enabled = false; + statements.erase(&s); +} + +void Logger::configure(const Options& o, const std::string& prog) +{ + { + ScopedLock l(lock); + syslogName=prog; + } + format(o); + select(Selector(o)); + void (Logger::* outputFn)(const std::string&) = &Logger::output; + for_each(o.outputs.begin(), o.outputs.end(), + boost::bind(outputFn, this, _1)); +} + +}} // namespace qpid::log diff --git a/cpp/src/qpid/log/Logger.h b/cpp/src/qpid/log/Logger.h new file mode 100644 index 0000000000..a2103f5ec6 --- /dev/null +++ b/cpp/src/qpid/log/Logger.h @@ -0,0 +1,116 @@ +#ifndef LOGGER_H +#define LOGGER_H + +/* + * 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 "Selector.h" +#include "qpid/sys/Mutex.h" +#include <boost/ptr_container/ptr_vector.hpp> +#include <boost/noncopyable.hpp> +#include <set> + +namespace qpid { +namespace log { + +class Options; + +/** + * Central logging agent. + * + * Thread safe, singleton. + */ +class Logger : private boost::noncopyable { + public: + /** Flags indicating what to include in the log output */ + enum FormatFlag { FILE=1, LINE=2, FUNCTION=4, LEVEL=8, TIME=16, THREAD=32}; + + /** Interface for log output destination. + * + * Implementations must be thread safe. + */ + class Output { + public: + Output(); + virtual ~Output(); + /** Receives the statemnt of origin and formatted message to log. */ + virtual void log(const Statement&, const std::string&) =0; + }; + + static Logger& instance(); + + Logger(); + ~Logger(); + + /** Select the messages to be logged. */ + void select(const Selector& s); + + /** Set the formatting flags, bitwise OR of FormatFlag values. */ + void format(int formatFlags); + + /** Set format flags from options object. + *@returns computed flags. + */ + int format(const Options&); + + /** Configure logger from Options */ + void configure(const Options& o, const std::string& progname); + + /** Add a statement. */ + void add(Statement& s); + + /** Remove a statement */ + void remove(Statement& s); + + /** Log a message. */ + void log(const Statement&, const std::string&); + + /** Add an ostream to outputs. + * + * The ostream must not be destroyed while the Logger might + * still be using it. This is the case for std streams cout, + * cerr, clog. + */ + void output(std::ostream&); + + /** Add syslog to outputs. */ + void syslog(const std::string& programName); + + /** Add an output. + *@param name a file name or one of the special tokens: + *stdout, stderr, syslog. + */ + void output(const std::string& name); + + /** Add an output destination for messages */ + void output(std::auto_ptr<Output> out); + + /** Reset the logger to it's original state */ + void clear(); + + private: + typedef boost::ptr_vector<Output> Outputs; + typedef std::set<Statement*> Statements; + + sys::Mutex lock; + inline void enable_unlocked(Statement* s); + + std::string syslogName; + Statements statements; + Outputs outputs; + Selector selector; + int flags; +}; + +}} // namespace qpid::log + + +#endif /*!LOGGER_H*/ diff --git a/cpp/src/qpid/log/Options.cpp b/cpp/src/qpid/log/Options.cpp new file mode 100644 index 0000000000..3e117663ff --- /dev/null +++ b/cpp/src/qpid/log/Options.cpp @@ -0,0 +1,69 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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 "Options.h" +#include "Statement.h" +#include "qpid/CommonOptions.h" + +namespace qpid { +namespace log { + +using namespace std; + +Options::Options() : + time(true), level(true), thread(false), source(false), function(false) +{ + outputs.push_back("stderr"); + selectors.push_back("error+"); +} + +void Options::addTo(po::options_description& desc) { + using namespace po; + ostringstream levels; + levels << LevelTraits::name(Level(0)); + for (int i = 1; i < LevelTraits::COUNT; ++i) + levels << " " << LevelTraits::name(Level(i)); + desc.add_options() + ("log.enable", optValue(selectors, "RULE"), + "You can specify this option mutliple times.\n" + "RULE is of the form 'LEVEL[+][:COMPONENT]'" + "Levels are: trace, debug, info, notice, warning, error, critical." + "For example:\n" + "\t'--log.enable warning+' " + "enables all warning, error and critical messages.\n" + "\t'--log.enable debug:framing' " + "enables debug messages from the framing component.") + ("log.output", optValue(outputs, "FILE"), + "File to receive log output, or one of these special values: " + "'stderr', 'stdout', 'syslog'.") + ("log.time", optValue(time, "yes|no"), + "Include time in log messages") + ("log.level", optValue(level,"yes|no"), + "Include severity level in log messages") + ("log.source", optValue(source,"yes|no"), + "Include source file:line in log messages") + ("log.thread", optValue(thread,"yes|no"), + "Include thread ID in log messages") + ("log.function", optValue(function,"yes|no"), + "Include function signature in log messages"); + +} + + + +}} // namespace qpid::log diff --git a/cpp/src/qpid/log/Options.h b/cpp/src/qpid/log/Options.h new file mode 100644 index 0000000000..39261c9b5d --- /dev/null +++ b/cpp/src/qpid/log/Options.h @@ -0,0 +1,42 @@ +#ifndef OPTIONS_H +#define OPTIONS_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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 "qpid/CommonOptions.h" + + +namespace qpid { +namespace log { + +/** Logging options for config parser. */ +struct Options { + Options(); + void addTo(po::options_description&); + + std::vector<std::string> selectors; + std::vector<std::string> outputs; + bool time, level, thread, source, function; +}; + + +}} // namespace qpid::log + + + +#endif /*!OPTIONS_H*/ diff --git a/cpp/src/qpid/log/Selector.cpp b/cpp/src/qpid/log/Selector.cpp new file mode 100644 index 0000000000..e8cef2b17d --- /dev/null +++ b/cpp/src/qpid/log/Selector.cpp @@ -0,0 +1,66 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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 "Selector.h" +#include "Options.h" +#include <boost/bind.hpp> +#include <algorithm> + +namespace qpid { +namespace log { + +using namespace std; + +void Selector::enable(const string& enableStr) { + string level, pattern; + size_t c=enableStr.find(':'); + if (c==string::npos) { + level=enableStr; + } + else { + level=enableStr.substr(0,c); + pattern=enableStr.substr(c+1); + } + if (!level.empty() && level[level.size()-1]=='+') { + for (int i = LevelTraits::level(level.substr(0,level.size()-1)); + i < LevelTraits::COUNT; + ++i) + enable(Level(i), pattern); + } + else { + enable(LevelTraits::level(level), pattern); + } +} + +Selector::Selector(const Options& opt){ + for_each(opt.selectors.begin(), opt.selectors.end(), + boost::bind(&Selector::enable, this, _1)); +} + +bool Selector::isEnabled(Level level, const std::string& file) { + for (std::vector<std::string>::iterator i=substrings[level].begin(); + i != substrings[level].end(); + ++i) + { + if (file.find(*i) != std::string::npos) + return true; + } + return false; +} + +}} // namespace qpid::log diff --git a/cpp/src/qpid/log/Selector.h b/cpp/src/qpid/log/Selector.h new file mode 100644 index 0000000000..329541b7fc --- /dev/null +++ b/cpp/src/qpid/log/Selector.h @@ -0,0 +1,68 @@ +#ifndef SELECTOR_H +#define SELECTOR_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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 "Statement.h" +#include <vector> + +namespace qpid { +namespace log { +class Options; + +/** + * A selector identifies the set of log messages to enable. + * + * Thread object unsafe, pass-by-value type. + */ +class Selector { + public: + /** Empty selector selects nothing */ + Selector() {} + + /** Set selector from Options */ + Selector(const Options&); + + /** Equavlient to: Selector s; s.enable(l, s) */ + Selector(Level l, const std::string& s=std::string()) { + enable(l,s); + } + /** + * Enable messages with level in levels where the file + * name contains substring. Empty string matches all. + */ + void enable(Level level, const std::string& substring=std::string()) { + substrings[level].push_back(substring); + } + + /** Enable based on a 'level[+]:file' string */ + void enable(const std::string& enableStr); + + /** True if level is enabld for file. */ + bool isEnabled(Level level, const std::string& file); + + private: + std::vector<std::string> substrings[LevelTraits::COUNT]; +}; + + +}} // namespace qpid::log + + +#endif /*!SELECTOR_H*/ diff --git a/cpp/src/qpid/log/Statement.cpp b/cpp/src/qpid/log/Statement.cpp new file mode 100644 index 0000000000..9ab314b81c --- /dev/null +++ b/cpp/src/qpid/log/Statement.cpp @@ -0,0 +1,67 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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 "Statement.h" +#include "Logger.h" +#include "qpid/Exception.h" +#include <syslog.h> + +namespace qpid { +namespace log { + +void Statement::log(const std::string& message) { + Logger::instance().log(*this,message); +} + +Statement::Initializer::Initializer(Statement& s) : statement(s) { + Logger::instance().add(s); +} + +Statement::Initializer::~Initializer() { + Logger::instance().remove(statement); +} + +namespace { +const char* names[LevelTraits::COUNT] = { + "trace", "debug", "info", "notice", "warning", "error", "critical" +}; + +int priorities[LevelTraits::COUNT] = { + LOG_DEBUG, LOG_DEBUG, LOG_INFO, LOG_NOTICE, + LOG_WARNING, LOG_ERR, LOG_CRIT +}; + +} // namespace + +Level LevelTraits::level(const char* name) { + for (int i =0; i < LevelTraits::COUNT; ++i) { + if (strcmp(names[i], name)==0) + return Level(i); + } + throw qpid::Exception(std::string("Invalid log level name: ")+name); +} + +const char* LevelTraits::name(Level l) { + return names[l]; +} + +int LevelTraits::priority(Level l) { + return priorities[l]; +} + +}} // namespace qpid::log diff --git a/cpp/src/qpid/log/Statement.h b/cpp/src/qpid/log/Statement.h new file mode 100644 index 0000000000..a04755012b --- /dev/null +++ b/cpp/src/qpid/log/Statement.h @@ -0,0 +1,124 @@ +#ifndef STATEMENT_H +#define STATEMENT_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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 "qpid/log/Statement.h" +#include <boost/current_function.hpp> +#include <sstream> + +namespace qpid { +namespace log { + +/** Debugging severity levels + * - trace: High-volume debugging messages. + * - debug: Debugging messages. + * - info: Informational messages. + * - notice: Normal but significant condition. + * - warning: Warn of a possible problem. + * - error: A definite error has occured. + * - critical: System in danger of severe failure. + */ +enum Level { trace, debug, info, notice, warning, error, critical }; +struct LevelTraits { + static const int COUNT=critical+1; + + /** Get level from string name. + *@exception if name invalid. + */ + static Level level(const char* name); + + /** Get level from string name. + *@exception if name invalid. + */ + static Level level(const std::string& name) { + return level(name.c_str()); + } + + /** String name of level */ + static const char* name(Level); + + /** Syslog priority of level */ + static int priority(Level); +}; + +/** POD struct representing a logging statement in source code. */ +struct Statement { + bool enabled; + const char* file; + int line; + const char* function; + Level level; + + void log(const std::string& message); + + struct Initializer { + Initializer(Statement& s); + ~Initializer(); + Statement& statement; + }; +}; + +///@internal trickery to make QPID_LOG_STRINGSTREAM work. +inline std::ostream& noop(std::ostream& s) { return s; } + +///@internal static initializer for a Statement. +#define QPID_LOG_STATEMENT_INIT(level) \ + { 0, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, (::qpid::log::level) } + +///@internal Stream streamable message and return a string. +#define QPID_LOG_STRINGSTREAM(message) \ + static_cast<std::ostringstream&>( \ + std::ostringstream() << qpid::log::noop << message).str() + +/** + * Macro for log statements. Example of use: + * @code + * QPID_LOG(debug, "There are " << foocount << " foos in the bar."); + * QPID_LOG(error, boost::format("Dohickey %s exploded") % dohicky.name()); + * @endcode + * + * All code with logging statements should be built with + * -DQPID_COMPONENT=<component name> + * where component name is the name of the component this file belongs to. + * + * You can subscribe to log messages by level, by component, by filename + * or a combination @see Configuration. + + *@param LEVEL severity Level for message, should be one of: + * debug, info, notice, warning, error, critical. NB no qpid::log:: prefix. + *@param MESSAGE any object with an @eostream operator<<, or a sequence + * like of ostreamable objects separated by @e<<. + */ +#define QPID_LOG(level, message) \ + do { \ + static ::qpid::log::Statement stmt_= QPID_LOG_STATEMENT_INIT(level); \ + static ::qpid::log::Statement::Initializer init_(stmt_); \ + if (stmt_.enabled) \ + stmt_.log(QPID_LOG_STRINGSTREAM(message)); \ + } while(0) + + +}} // namespace qpid::log + + + + +#endif /*!STATEMENT_H*/ + |
