diff options
Diffstat (limited to 'qpid/cpp/src/qpid/log')
-rw-r--r-- | qpid/cpp/src/qpid/log/Helpers.h | 79 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/log/Logger.cpp | 200 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/log/Logger.h | 122 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/log/Options.cpp | 148 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/log/Options.h | 57 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/log/OstreamOutput.cpp | 41 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/log/OstreamOutput.h | 41 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/log/Selector.cpp | 237 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/log/Selector.h | 99 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/log/SinkOptions.h | 64 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/log/Statement.cpp | 218 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/log/Statement.h | 244 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/log/posix/SinkOptions.cpp | 222 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/log/posix/SinkOptions.h | 64 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/log/windows/SinkOptions.cpp | 148 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/log/windows/SinkOptions.h | 54 |
16 files changed, 2038 insertions, 0 deletions
diff --git a/qpid/cpp/src/qpid/log/Helpers.h b/qpid/cpp/src/qpid/log/Helpers.h new file mode 100644 index 0000000000..82ef8244be --- /dev/null +++ b/qpid/cpp/src/qpid/log/Helpers.h @@ -0,0 +1,79 @@ +#ifndef QPID_LOG_HELPERS_H +#define QPID_LOG_HELPERS_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 <boost/range.hpp> + +#include <ostream> + +namespace qpid { +namespace log { + +/** @file Helper classes for logging complex types */ + +/// @internal +template <class Range> +struct ListFormatter { + typedef typename boost::range_const_iterator<Range>::type Iterator; + boost::iterator_range<Iterator> range; + const char* separator; + + ListFormatter(const Range& r, const char* s=", ") : range(r), separator(s) {} +}; + +/// @internal +template <class Range> +std::ostream& operator<<(std::ostream& out, const ListFormatter<Range>& sl) { + typename ListFormatter<Range>::Iterator i = sl.range.begin(); + if (i != sl.range.end()) out << *(i++); + while (i != sl.range.end()) out << sl.separator << *(i++); + return out; +} + +/** Return a formatting object with operator << + * to stream range as a separated list. + *@param range: a range - all standard containers are ranges, + * as is a pair of iterators. + *@param separator: printed between elements, default ", " + */ +template <class Range> +ListFormatter<Range> formatList(const Range& range, const char* separator=", ") { + return ListFormatter<Range>(range, separator); +} + +/** Return a formatting object with operator << + * to stream the range defined by iterators [begin, end) + * as a separated list. + *@param begin, end: Beginning and end of range. + *@param separator: printed between elements, default ", " + */ +template <class U, class V> +ListFormatter<std::pair<U,V> > formatList(U begin, V end, const char* separator=", ") { + return formatList(std::make_pair(begin,end), separator); +} + + +}} // namespace qpid::log + + + +#endif /*!QPID_LOG_HELPERS_H*/ diff --git a/qpid/cpp/src/qpid/log/Logger.cpp b/qpid/cpp/src/qpid/log/Logger.cpp new file mode 100644 index 0000000000..fc254f2857 --- /dev/null +++ b/qpid/cpp/src/qpid/log/Logger.cpp @@ -0,0 +1,200 @@ +/* + * + * 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/Logger.h" +#include "qpid/log/Options.h" +#include "qpid/log/SinkOptions.h" +#include "qpid/memory.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Time.h" +#include "qpid/DisableExceptionLogging.h" + +#include "boost/version.hpp" +#if (BOOST_VERSION >= 104000) +#include <boost/serialization/singleton.hpp> +#else +#include <boost/pool/detail/singleton.hpp> +#endif + +#include <boost/bind.hpp> +#include <boost/function.hpp> +#include <algorithm> +#include <sstream> +#include <iomanip> +#include <stdexcept> +#include <time.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->function, s->category); +} + +Logger& Logger::instance() { +#if (BOOST_VERSION >= 104000) + return boost::serialization::singleton<Logger>::get_mutable_instance(); +#else + return boost::details::pool::singleton_default<Logger>::instance(); +#endif +} + +Logger::Logger() : flags(0) { + // Disable automatic logging in Exception constructors to avoid + // re-entrant use of logger singleton if there is an error in + // option parsing. + DisableExceptionLogging del; + + // 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; + opts.parse(0, 0); + configure(opts); +} + +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 (!prefix.empty()) + os << prefix << ": "; + if (flags&TIME) { + if (flags&HIRES) + qpid::sys::outputHiresNow(os); + else + qpid::sys::outputFormattedNow(os); + } + if (flags&CATEGORY) + os << "[" << CategoryTraits::name(s.category) << "] "; + if (flags&LEVEL) + os << LevelTraits::name(s.level) << " "; + if (flags&THREAD) + os << "[0x" << hex << qpid::sys::Thread::logId() << "] "; + if (flags&FILE) + os << s.file << ":"; + if (flags&LINE) + os << dec << s.line << ":"; + if ((flags&FUNCTION) && s.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::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) | + bitIf(opts.hiresTs, HIRES) | + bitIf(opts.category, CATEGORY); + format(flags); + return flags; +} + +void Logger::add(Statement& s) { + ScopedLock l(lock); + enable_unlocked(&s); + statements.insert(&s); +} + +void Logger::configure(const Options& opts) { + clear(); + Options o(opts); + if (o.trace) + o.selectors.push_back("trace+"); + format(o); + select(Selector(o)); + options = opts; + setPrefix(opts.prefix); + options.sinkOptions->setup(this); +} + +void Logger::reconfigure(const std::vector<std::string>& selectors) { + Options o(options); + o.selectors = selectors; + o.deselectors.clear(); + select(Selector(o)); + options = o; // Don't update options till selectors has been validated. +} + +void Logger::setPrefix(const std::string& p) { prefix = p; } + + +bool Logger::getHiresTimestamp() +{ + return flags & HIRES; +} + + +void Logger::setHiresTimestamp(bool setting) +{ + ScopedLock l(lock); + if (setting) + flags |= HIRES; + else + flags &= ~HIRES; +} + +}} // namespace qpid::log diff --git a/qpid/cpp/src/qpid/log/Logger.h b/qpid/cpp/src/qpid/log/Logger.h new file mode 100644 index 0000000000..8c4beb0785 --- /dev/null +++ b/qpid/cpp/src/qpid/log/Logger.h @@ -0,0 +1,122 @@ +#ifndef QPID_LOG_LOGGER_H +#define QPID_LOG_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 "qpid/log/Selector.h" +#include "qpid/log/Options.h" +#include "qpid/sys/Mutex.h" +#include <boost/ptr_container/ptr_vector.hpp> +#include <boost/noncopyable.hpp> +#include <set> +#include "qpid/CommonImportExport.h" + +namespace qpid { +namespace log { + +/** + * Central logging agent. + * + * Thread safe, singleton. + * + * The Logger provides all needed functionality for selecting and + * formatting logging output. The actual outputting of log records + * is handled by Logger::Output-derived classes instantiated by the + * platform's sink-related options. + */ +class QPID_COMMON_CLASS_EXTERN 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, HIRES=64, CATEGORY=128}; + + /** + * Logging output sink. + * + * The Output sink provides an interface to direct logging output to. + * Logging sinks are primarily platform-specific as provided for on + * each platform. + * + * Implementations of Output must be thread safe. + */ + class Output { + public: + QPID_COMMON_EXTERN Output(); + QPID_COMMON_EXTERN virtual ~Output(); + /** Receives the statemnt of origin and formatted message to log. */ + virtual void log(const Statement&, const std::string&) =0; + }; + + QPID_COMMON_EXTERN static Logger& instance(); + + QPID_COMMON_EXTERN Logger(); + QPID_COMMON_EXTERN ~Logger(); + + /** Select the messages to be logged. */ + QPID_COMMON_EXTERN void select(const Selector& s); + + /** Set the formatting flags, bitwise OR of FormatFlag values. */ + QPID_COMMON_EXTERN void format(int formatFlags); + + /** Set format flags from options object. + *@returns computed flags. + */ + QPID_COMMON_EXTERN int format(const Options&); + + /** Configure logger from Options */ + QPID_COMMON_EXTERN void configure(const Options& o); + + /** Reset the log selectors */ + QPID_COMMON_EXTERN void reconfigure(const std::vector<std::string>& selectors); + + /** Add a statement. */ + QPID_COMMON_EXTERN void add(Statement& s); + + /** Log a message. */ + QPID_COMMON_EXTERN void log(const Statement&, const std::string&); + + /** Add an output destination for messages */ + QPID_COMMON_EXTERN void output(std::auto_ptr<Output> out); + + /** Set a prefix for all messages */ + QPID_COMMON_EXTERN void setPrefix(const std::string& prefix); + + /** Reset the logger. */ + QPID_COMMON_EXTERN void clear(); + + /** Get the options used to configure the logger. */ + QPID_COMMON_INLINE_EXTERN const Options& getOptions() const { return options; } + + /** Get the hires timestamp setting */ + QPID_COMMON_EXTERN bool getHiresTimestamp(); + + /** Set the hires timestamp setting */ + QPID_COMMON_EXTERN void setHiresTimestamp(bool setting); + + private: + typedef boost::ptr_vector<Output> Outputs; + typedef std::set<Statement*> Statements; + + sys::Mutex lock; + inline void enable_unlocked(Statement* s); + + Statements statements; + Outputs outputs; + Selector selector; + int flags; + std::string prefix; + Options options; +}; + +}} // namespace qpid::log + + +#endif /*!QPID_LOG_LOGGER_H*/ diff --git a/qpid/cpp/src/qpid/log/Options.cpp b/qpid/cpp/src/qpid/log/Options.cpp new file mode 100644 index 0000000000..f816124b4e --- /dev/null +++ b/qpid/cpp/src/qpid/log/Options.cpp @@ -0,0 +1,148 @@ +/* + * + * 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/Options.h" +#include "qpid/log/SinkOptions.h" +#include "qpid/log/Statement.h" +#include "qpid/Options.h" +#include <map> +#include <string> +#include <algorithm> + +namespace qpid { +namespace log { + +Options::Options(const std::string& argv0_, const std::string& name_) : + qpid::Options(name_), + argv0(argv0_), + name(name_), + time(true), + level(true), + thread(false), + source(false), + function(false), + hiresTs(false), + category(true), + trace(false), + sinkOptions (SinkOptions::create(argv0_)) +{ + selectors.push_back("notice+"); + + 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 "+getLevels()+"\n" + "PATTERN is a logging category name, or a namespace-qualified " + "function name or name fragment. " + "Logging category names are: \n\t "+getCategories()+"\n" + "For example:\n" + "\t'--log-enable warning+'\n" + "logs all warning, error and critical messages.\n" + "\t'--log-enable trace+:Broker'\n" + "logs all category 'Broker' 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()) + ("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 "+getLevels()+"\n" + "PATTERN is a logging category name, or a namespace-qualified " + "function name or name fragment. " + "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" + "\t'--log-disable trace:Broker'\n" + "disables all category 'Broker' 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()) + ("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") + ("log-hires-timestamp", optValue(hiresTs,"yes|no"), "Use hi-resolution timestamps in log messages") + ("log-category", optValue(category,"yes|no"), "Include category in log messages") + ("log-prefix", optValue(prefix,"STRING"), "Prefix to prepend to all log messages") + ; + add(*sinkOptions); +} + +Options::Options(const Options &o) : + qpid::Options(o.name), + argv0(o.argv0), + name(o.name), + selectors(o.selectors), + deselectors(o.deselectors), + time(o.time), + level(o.level), + thread(o.thread), + source(o.source), + function(o.function), + hiresTs(o.hiresTs), + category(o.category), + trace(o.trace), + prefix(o.prefix), + sinkOptions (SinkOptions::create(o.argv0)) +{ + *sinkOptions = *o.sinkOptions; +} + +Options& Options::operator=(const Options& x) { + if (this != &x) { + argv0 = x.argv0; + name = x.name; + selectors = x.selectors; + deselectors = x.deselectors; + time = x.time; + level= x.level; + thread = x.thread; + source = x.source; + function = x.function; + hiresTs = x.hiresTs; + category = x.category; + trace = x.trace; + prefix = x.prefix; + *sinkOptions = *x.sinkOptions; + } + 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 new file mode 100644 index 0000000000..ed534168e8 --- /dev/null +++ b/qpid/cpp/src/qpid/log/Options.h @@ -0,0 +1,57 @@ +#ifndef QPID_LOG_OPTIONS_H +#define QPID_LOG_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/Options.h" +#include "qpid/CommonImportExport.h" +#include "qpid/log/SinkOptions.h" +#include <iosfwd> +#include <memory> + +namespace qpid { +namespace log { + +/** Logging options for config parser. */ +struct Options : public qpid::Options { + /** Pass argv[0] for use in syslog output */ + QPID_COMMON_EXTERN Options(const std::string& argv0_=std::string(), + const std::string& name_="Logging options"); + QPID_COMMON_EXTERN Options(const Options &); + + QPID_COMMON_EXTERN Options& operator=(const Options&); + + std::string argv0; + std::string name; + std::vector<std::string> selectors; + std::vector<std::string> deselectors; + bool time, level, thread, source, function, hiresTs, category; + bool trace; + std::string prefix; + 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/OstreamOutput.cpp b/qpid/cpp/src/qpid/log/OstreamOutput.cpp new file mode 100644 index 0000000000..9b6ec1f8aa --- /dev/null +++ b/qpid/cpp/src/qpid/log/OstreamOutput.cpp @@ -0,0 +1,41 @@ +/* + * + * 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/OstreamOutput.h" +#include <stdexcept> + +using namespace std; + +namespace qpid { +namespace log { + +OstreamOutput::OstreamOutput(std::ostream& o) : out(&o) {} + +OstreamOutput::OstreamOutput(const std::string& file) + : out(new ofstream(file.c_str(), ios_base::out | ios_base::app)), + mine(out) +{ + if (!out->good()) + throw std::runtime_error("Can't open log file: "+file); +} + +void OstreamOutput::log(const Statement&, const std::string& m) { + *out << m << flush; +} + +}} // namespace qpid::log diff --git a/qpid/cpp/src/qpid/log/OstreamOutput.h b/qpid/cpp/src/qpid/log/OstreamOutput.h new file mode 100644 index 0000000000..12fd4ce425 --- /dev/null +++ b/qpid/cpp/src/qpid/log/OstreamOutput.h @@ -0,0 +1,41 @@ +#ifndef QPID_LOG_OSTREAMOUTPUT_H +#define QPID_LOG_OSTREAMOUTPUT_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 "qpid/log/Logger.h" +#include <boost/scoped_ptr.hpp> +#include <fstream> +#include <ostream> + +namespace qpid { +namespace log { + +/** + * OstreamOutput is a reusable logging sink that directs logging to a C++ + * ostream. + */ +class OstreamOutput : public qpid::log::Logger::Output { +public: + QPID_COMMON_EXTERN OstreamOutput(std::ostream& o); + QPID_COMMON_EXTERN OstreamOutput(const std::string& file); + + virtual void log(const Statement&, const std::string& m); + +private: + std::ostream* out; + boost::scoped_ptr<std::ostream> mine; +}; + +}} // namespace qpid::log + +#endif /*!QPID_LOG_OSTREAMOUTPUT_H*/ diff --git a/qpid/cpp/src/qpid/log/Selector.cpp b/qpid/cpp/src/qpid/log/Selector.cpp new file mode 100644 index 0000000000..a517cb34f4 --- /dev/null +++ b/qpid/cpp/src/qpid/log/Selector.cpp @@ -0,0 +1,237 @@ +/* + * + * 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/Selector.h" +#include "qpid/log/Options.h" +#include <boost/bind.hpp> +#include <algorithm> +#include <string.h> + +namespace qpid { +namespace log { + +using namespace std; + +const char LOG_SYMBOL_DISABLE ('!'); +const char LOG_SYMBOL_SEPERATOR(':'); +const char LOG_SYMBOL_AND_ABOVE('+'); +const char LOG_SYMBOL_AND_BELOW('-'); + +// +// Parse an enable or disable entry into usable fields. +// Throws if 'level' field is not recognized. +// +SelectorElement::SelectorElement(const std::string cliEntry) : + level(qpid::log::debug), + category(qpid::log::unspecified), + isDisable(false), + isCategory(false), + isLevelAndAbove(false), + isLevelAndBelow(false) +{ + if (cliEntry.empty()) + return; + std::string working(cliEntry); + if (LOG_SYMBOL_DISABLE == working[0]) { + isDisable = true; + working = working.substr(1); + } + size_t c=working.find(LOG_SYMBOL_SEPERATOR); + if (c==string::npos) { + levelStr=working; + } else { + levelStr=working.substr(0,c); + patternStr=working.substr(c+1); + } + if (!levelStr.empty()) { + if (levelStr[levelStr.size()-1]==LOG_SYMBOL_AND_ABOVE) { + isLevelAndAbove = true; + levelStr = levelStr.substr(0, levelStr.size()-1); + } else if (levelStr[levelStr.size()-1]==LOG_SYMBOL_AND_BELOW) { + isLevelAndBelow = true; + levelStr = levelStr.substr(0, levelStr.size()-1); + } + } + level = LevelTraits::level(levelStr); // throws if bad level name + isCategory = CategoryTraits::isCategory(patternStr); + if (isCategory) { + category = CategoryTraits::category(patternStr); + } +} + +// Empty selector +Selector::Selector() { + reset(); +} + + +// Selector from options +Selector::Selector(const Options& opt){ + reset(); + for_each(opt.selectors.begin(), opt.selectors.end(), + boost::bind(&Selector::enable, this, _1)); + for_each(opt.deselectors.begin(), opt.deselectors.end(), + boost::bind(&Selector::disable, this, _1)); +} + + +// Selector from single level +Selector::Selector(Level l, const std::string& s) { + reset(); + enable(l, s); +} + + +// Selector from single enable +Selector::Selector(const std::string& selector) { + reset(); + enable(selector); +} + + +/** + * Process a single CLI --log-enable option + */ +void Selector::enable(const string& enableStr) { + if (enableStr.empty()) + return; + SelectorElement se(enableStr); + if (se.isDisable) { + // Disable statements are allowed in an enable string as a convenient + // way to process management strings that have enable/disable mixed. + disable(enableStr); + } else if (se.isLevelAndAbove) { + for (int lvl = se.level; lvl < LevelTraits::COUNT; ++lvl) { + if (se.isCategory) { + enableFlags[lvl][se.category] = true; + } else { + enable(Level(lvl), se.patternStr); + } + } + } else if (se.isLevelAndBelow) { + for (int lvl = se.level; lvl >= 0; --lvl) { + if (se.isCategory) { + enableFlags[lvl][se.category] = true; + } else { + enable(Level(lvl), se.patternStr); + } + } + } else { + if (se.isCategory) { + enableFlags[se.level][se.category] = true; + } else { + enable(se.level, se.patternStr); + } + } +} + +void Selector::disable(const string& disableStr) { + if (disableStr.empty()) + return; + SelectorElement se(disableStr); + if (se.isLevelAndAbove) { + for (int lvl = se.level; lvl < LevelTraits::COUNT; ++lvl) { + if (se.isCategory) { + disableFlags[lvl][se.category] = true; + } else { + disable(Level(lvl), se.patternStr); + } + } + } else if (se.isLevelAndBelow) { + for (int lvl = se.level; lvl >= 0; --lvl) { + if (se.isCategory) { + disableFlags[lvl][se.category] = true; + } else { + disable(Level(lvl), se.patternStr); + } + } + } else { + if (se.isCategory) { + disableFlags[se.level][se.category] = true; + } else { + disable(se.level, se.patternStr); + } + } +} + + +/** +* Enable/disable messages with level in levels where the file +* name contains substring. +*/ +void Selector::enable(Level level, const std::string& substring) { + enabledFunctions[level].push_back(substring); +} + + +void Selector::disable(Level level, const std::string& substring) { + disabledFunctions[level].push_back(substring); +} + + +void Selector::reset() { + // Initialize fields in a Selector that are not automatically set + for (int lt = 0; lt < LevelTraits::COUNT; ++lt) + for (int ct = 0; ct < CategoryTraits::COUNT; ++ct) + enableFlags[lt][ct] = disableFlags[lt][ct] = false; +} + + +bool Selector::lookupFuncName(Level level, const char* function, FunctionNameTable& table) { + const char* functionEnd = function+::strlen(function); + for (std::vector<std::string>::iterator i=table[level].begin(); + i != table[level].end(); + ++i) + { + if (std::search(function, functionEnd, i->begin(), i->end()) != functionEnd) + return true; + } + return false; +} + + +bool Selector::isEnabled(Level level, const char* function) { + return lookupFuncName(level, function, enabledFunctions); +} + +bool Selector::isDisabled(Level level, const char* function) { + return lookupFuncName(level, function, disabledFunctions); +} + +// +// isEnabled +// +// Determines if all the fields in this Selector enable or disable a +// level/function/category set from an actual QPID_LOG Statement. +// +bool Selector::isEnabled(Level level, const char* function, Category category) { + if (level==critical) + return true; // critical cannot be disabled + if (isDisabled(level, function)) + return false; // Disabled by function name + if (disableFlags[level][category]) + return false; // Disabled by category name + if (isEnabled(level, function)) + return true; // Enabled by function name + if (enableFlags[level][category]) + return true; // Enabled by category name + else + return false; // Unspecified defaults to disabled +} + +}} // namespace qpid::log diff --git a/qpid/cpp/src/qpid/log/Selector.h b/qpid/cpp/src/qpid/log/Selector.h new file mode 100644 index 0000000000..1d025e9646 --- /dev/null +++ b/qpid/cpp/src/qpid/log/Selector.h @@ -0,0 +1,99 @@ +#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 "qpid/log/Statement.h" +#include "qpid/CommonImportExport.h" +#include <vector> + +namespace qpid { +namespace log { +struct Options; + +/** + * SelectorElement parses a cli/mgmt enable/disable entry into usable fields + * where cliEntry = [!]LEVEL[+-][:PATTERN] + */ +struct SelectorElement { + QPID_COMMON_EXTERN SelectorElement(const std::string cliEntry); + std::string levelStr; + std::string patternStr; + Level level; + Category category; + bool isDisable; + bool isCategory; + bool isLevelAndAbove; + bool isLevelAndBelow; +}; + +/** + * A selector identifies the set of log messages to enable. + * + * Thread object unsafe, pass-by-value type. + */ +class Selector { + public: + /** Empty selector selects nothing */ + QPID_COMMON_EXTERN Selector(); + + /** Set selector from Options */ + QPID_COMMON_EXTERN Selector(const Options&); + + /** Equavlient to: Selector s; s.enable(l, s) */ + QPID_COMMON_EXTERN Selector(Level l, const std::string& s=std::string()); + + /** Selector from string */ + QPID_COMMON_EXTERN Selector(const std::string& selector); + + /** push option settings into runtime lookup structs */ + QPID_COMMON_EXTERN void enable(const std::string& enableStr); + QPID_COMMON_EXTERN void disable(const std::string& disableStr); + + /** + * Enable/disable messages with level in levels where the file + * name contains substring. Empty string matches all. + */ + QPID_COMMON_EXTERN void enable(Level level, const std::string& substring=std::string()); + QPID_COMMON_EXTERN void disable(Level level, const std::string& substring=std::string()); + + /** Tests to determine if function names are in enable/disable tables */ + QPID_COMMON_EXTERN bool isEnabled(Level level, const char* function); + QPID_COMMON_EXTERN bool isDisabled(Level level, const char* function); + + /** Test to determine if log Statement is enabled */ + QPID_COMMON_EXTERN bool isEnabled(Level level, const char* function, Category category); + + private: + typedef std::vector<std::string> FunctionNameTable [LevelTraits::COUNT]; + FunctionNameTable enabledFunctions; // log function names explicitly enabled + FunctionNameTable disabledFunctions; // log function names explicitly disabled + bool enableFlags[LevelTraits::COUNT][CategoryTraits::COUNT]; + bool disableFlags[LevelTraits::COUNT][CategoryTraits::COUNT]; + + bool lookupFuncName(Level level, const char* function, FunctionNameTable& table); + /** Reset the category enable flags */ + QPID_COMMON_EXTERN void reset(); +}; + + +}} // namespace qpid::log + + +#endif /*!SELECTOR_H*/ diff --git a/qpid/cpp/src/qpid/log/SinkOptions.h b/qpid/cpp/src/qpid/log/SinkOptions.h new file mode 100644 index 0000000000..7ec2cfbc17 --- /dev/null +++ b/qpid/cpp/src/qpid/log/SinkOptions.h @@ -0,0 +1,64 @@ +#ifndef QPID_LOG_SINKOPTIONS_H +#define QPID_LOG_SINKOPTIONS_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/Options.h" +#include <string> + +namespace qpid { +namespace log { + +class Logger; + +/** + * Logging sink options. + * + * Most logging sink options will be platform-specific, even if some are + * duplicated. The range of platforms to which this code may be ported + * can't be assumed to all have C++ iostreams or files. Thus, this class + * is primarily for implementing in a platform-specific way. + */ +struct SinkOptions : public qpid::Options { + + // Create a platform's SinkOptions. Pass argv0 as the program name, + // useful for syslog-type logging. + static SinkOptions *create(const std::string& argv0=std::string()); + + SinkOptions(const std::string& name="Logging sink options") + : qpid::Options(name) + {} + virtual ~SinkOptions() {} + + virtual SinkOptions& operator=(const SinkOptions&) = 0; + + // This allows the caller to indicate that there's no normal outputs + // available. For example, when running as a daemon. In these cases, the + // platform's "syslog"-type output should replace the default stderr + // unless some other sink has been selected. + virtual void detached(void) = 0; + + // The Logger acting on these options calls setup() to request any + // Sinks be set up and fed back to the logger. + virtual void setup(Logger *logger) = 0; +}; + +}} // 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 new file mode 100644 index 0000000000..86b069fd91 --- /dev/null +++ b/qpid/cpp/src/qpid/log/Statement.cpp @@ -0,0 +1,218 @@ +/* + * + * 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 "qpid/log/Logger.h" +#include <boost/bind.hpp> +#include <stdexcept> +#include <algorithm> +#include <list> +#include <ctype.h> + +namespace qpid { +namespace log { + +namespace { +struct NonPrint { bool operator()(unsigned char c) { return !isprint(c) && !isspace(c); } }; + +const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + +std::string quote(const std::string& str) { + NonPrint nonPrint; + size_t n = std::count_if(str.begin(), str.end(), nonPrint); + if (n==0) return str; + std::string ret; + ret.reserve(str.size()+3*n); // Avoid extra allocations. + for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) { + if (nonPrint(*i)) { + ret.push_back('\\'); + ret.push_back('x'); + ret.push_back(hex[((*i) >> 4)&0xf]); + ret.push_back(hex[(*i) & 0xf]); + } + else ret.push_back(*i); + } + return ret; +} +} + +// +// Instance of name hints +// +class CategoryFileNameHints { +public: + CategoryFileNameHints(){ + hintList.push_back(std::make_pair("AsynchIo", network)); + hintList.push_back(std::make_pair("TCP", network)); + hintList.push_back(std::make_pair("epoll", network)); + hintList.push_back(std::make_pair("Pollable", network)); + hintList.push_back(std::make_pair("Socket", network)); + + hintList.push_back(std::make_pair("Sasl", security)); + hintList.push_back(std::make_pair("Ssl", security)); + hintList.push_back(std::make_pair("Acl", security)); + hintList.push_back(std::make_pair("acl", security)); + hintList.push_back(std::make_pair("cyrus", security)); + + hintList.push_back(std::make_pair("amqp_", protocol)); + hintList.push_back(std::make_pair("framing", protocol)); + + hintList.push_back(std::make_pair("management", management)); + hintList.push_back(std::make_pair("qmf", management)); + hintList.push_back(std::make_pair("console", management)); + hintList.push_back(std::make_pair("Management", management)); + + hintList.push_back(std::make_pair("cluster", ha)); + hintList.push_back(std::make_pair("qpid/ha", ha)); + hintList.push_back(std::make_pair("qpid\\ha", ha)); + hintList.push_back(std::make_pair("replication", ha)); + hintList.push_back(std::make_pair("ClusterSafe", ha)); + + hintList.push_back(std::make_pair("broker", broker)); + hintList.push_back(std::make_pair("SessionState",broker)); + hintList.push_back(std::make_pair("DataDir", broker)); + hintList.push_back(std::make_pair("qpidd", broker)); + hintList.push_back(std::make_pair("xml", broker)); + hintList.push_back(std::make_pair("QpidBroker", broker)); + + hintList.push_back(std::make_pair("store", store)); + + hintList.push_back(std::make_pair("assert", system)); + hintList.push_back(std::make_pair("Exception", system)); + hintList.push_back(std::make_pair("sys", system)); + hintList.push_back(std::make_pair("SCM", system)); + + hintList.push_back(std::make_pair("tests", test)); + + hintList.push_back(std::make_pair("messaging", messaging)); + hintList.push_back(std::make_pair("types", messaging)); + + hintList.push_back(std::make_pair("client", client)); + } + + static Category categoryOf(const char*const fName); + +private: + std::list<std::pair<const char* const, Category> > hintList; +}; + +static CategoryFileNameHints filenameHints; + +Category CategoryFileNameHints::categoryOf(const char* const fName) { + for (std::list<std::pair<const char* const, Category> >::iterator + it = filenameHints.hintList.begin(); + it != filenameHints.hintList.end(); + ++it) { + if (strstr(fName, (const char* const)it->first) != 0) { + return it->second; + } + } + return unspecified; +} + + +void Statement::categorize(Statement& s) { + // given a statement and it's category + // if the category is Unspecified then try to find a + // better category based on the path and file name. + if (s.category == log::unspecified) { + s.category = CategoryFileNameHints::categoryOf(s.file); + } else { + // already has a category so leave it alone + } +} + + +void Statement::log(const std::string& message) { + Logger::instance().log(*this, quote(message)); +} + + +Statement::Initializer::Initializer(Statement& s) : statement(s) { + // QPID-3891: + // From the given BOOST_CURRENT_FUNCTION name extract only the + // namespace-qualified-functionName, skipping return and calling args. + // Given: + // <possible return value type> qpid::name::space::Function(args) + // Return: + // "qpid::name::space::Function". + if (s.function) { + const char* end = s.function + strlen(s.function); + const char* fEnd = std::find(s.function, end, '('); + typedef std::reverse_iterator<const char*> Reverse; + const char* fBegin = find(Reverse(fEnd), Reverse(s.function), ' ').base(); + size_t n = fEnd - fBegin; + char* name = new char[n+1]; + std::copy(fBegin, fEnd, name); + name[n] = '\0'; + s.function = name; + } + Statement::categorize(s); + Logger::instance().add(s); +} + +Statement::Initializer::~Initializer() { + delete[] const_cast<char*>(statement.function); + statement.function = 0; +} + + +namespace { +const char* names[LevelTraits::COUNT] = { + "trace", "debug", "info", "notice", "warning", "error", "critical" +}; + +const char* catNames[CategoryTraits::COUNT] = { + "Security", "Broker", "Management", "Protocol", "System", "HA", "Messaging", + "Store", "Network", "Test", "Client", "Application", "Model", "Unspecified" +}; + +} // 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 std::runtime_error(std::string("Invalid log level name: ")+name); +} + +const char* LevelTraits::name(Level l) { + return names[l]; +} + +bool CategoryTraits::isCategory(const std::string& name) { + for (int i =0; i < CategoryTraits::COUNT; ++i) { + if (strcmp(catNames[i], name.c_str())==0) + return true; + } + return false; +} + +Category CategoryTraits::category(const char* name) { + for (int i =0; i < CategoryTraits::COUNT; ++i) { + if (strcmp(catNames[i], name)==0) + return Category(i); + } + throw std::runtime_error(std::string("Invalid log category name: ")+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 new file mode 100644 index 0000000000..410ffaaa3d --- /dev/null +++ b/qpid/cpp/src/qpid/log/Statement.h @@ -0,0 +1,244 @@ +#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/Msg.h" +#include "qpid/CommonImportExport.h" +#include <boost/current_function.hpp> + +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); +}; + +/** Formal message categories + * https://issues.apache.org/jira/browse/QPID-3902 + * + * Category Source code directory + * -------- --------------------- + * Security acl ssl gssapi sasl cyrus + * Broker broker + * Management agent console qmf + * Protocol amqp_0_10 framing + * System log sys types xml thread mutex fork pipe time ... + * HA cluster ha replication + * Messaging messaging + * Client client + * 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, external_application, model, unspecified }; +struct CategoryTraits { + static const int COUNT=unspecified+1; + + /** Test if given name is a Category name + */ + static bool isCategory(const std::string& name); + + /** Get category from string name + * @exception if name invalid. + */ + static Category category(const char* name); + + /** Get category from string name. + * @exception if name invalid. + */ + static Category category(const std::string& name) { + return category(name.c_str()); + } + + /** String name of category */ + static const char* name(Category); +}; + + /** POD struct representing a logging statement in source code. */ +struct Statement { + bool enabled; + const char* file; + int line; + const char* function; + Level level; + Category category; + + QPID_COMMON_EXTERN void log(const std::string& message); + QPID_COMMON_EXTERN static void categorize(Statement& s); + + struct Initializer { + QPID_COMMON_EXTERN Initializer(Statement& s); + QPID_COMMON_EXTERN ~Initializer(); + Statement& statement; + }; +}; + +///@internal static initializer for a Statement. +#define QPID_LOG_STATEMENT_INIT_CAT(LEVEL, CATEGORY) \ +{ 0, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, (::qpid::log::LEVEL), \ +(::qpid::log::CATEGORY) } + + +///@internal static initializer for a Statement with unspecified category +#define QPID_LOG_STATEMENT_INIT(LEVEL) \ +QPID_LOG_STATEMENT_INIT_CAT ( LEVEL , unspecified ) + +/** + * Like QPID_LOG but computes an additional boolean test expression + * to determine if the message should be logged. Evaluation of both + * the test and message expressions occurs only if the requested log level + * is enabled. + *@param LEVEL severity Level for message, should be one of: + * debug, info, notice, warning, error, critical. NB no qpid::log:: prefix. + *@param TEST message is logged only if expression TEST evaluates to true. + *@param MESSAGE any object with an @eostream operator<<, or a sequence + * like of ostreamable objects separated by @e<<. + */ +#define QPID_LOG_IF(LEVEL, TEST, MESSAGE) \ + do { \ + using ::qpid::log::Statement; \ + static Statement stmt_= QPID_LOG_STATEMENT_INIT(LEVEL); \ + static Statement::Initializer init_(stmt_); \ + if (stmt_.enabled && (TEST)) \ + stmt_.log(::qpid::Msg() << MESSAGE); \ + } while(0) + +/** + * Line QPID_LOG_IF but with the additional specification of a category. + * @param CATEGORY message category. + */ +#define QPID_LOG_IF_CAT(LEVEL, CATEGORY, TEST, MESSAGE) \ + do { \ + using ::qpid::log::Statement; \ + static Statement stmt_= QPID_LOG_STATEMENT_INIT_CAT(LEVEL, CATEGORY); \ + static Statement::Initializer init_(stmt_); \ + if (stmt_.enabled && (TEST)) \ + stmt_.log(::qpid::Msg() << MESSAGE); \ + } while(0) + +/** + * FLAG must be a boolean variable. Assigns FLAG to true iff logging + * is enabled for LEVEL in the calling context. Use when extra + * support code is needed to generate log messages, to ensure that it + * is only run if the logging level is enabled. + * e.g. + * bool logWarning; + * QPID_LOG_TEST(warning, logWarning); + * if (logWarning) { do stuff needed for warning log messages } + */ +#define QPID_LOG_TEST(LEVEL, FLAG) \ + do { \ + using ::qpid::log::Statement; \ + static Statement stmt_= QPID_LOG_STATEMENT_INIT(LEVEL); \ + static Statement::Initializer init_(stmt_); \ + FLAG = stmt_.enabled; \ + } while(0) + + /** + * FLAG must be a boolean variable. Assigns FLAG to true iff logging + * is enabled for LEVEL in the calling context. Use when extra + * support code is needed to generate log messages, to ensure that it + * is only run if the logging level is enabled. + * e.g. + * bool logWarning; + * QPID_LOG_TEST_CAT(warning, System, logWarning); + * if (logWarning) { do stuff needed for warning log messages } + */ + #define QPID_LOG_TEST_CAT(LEVEL, CATEGORY, FLAG) \ + do { \ + using ::qpid::log::Statement; \ + static Statement stmt_= QPID_LOG_STATEMENT_INIT_CAT(LEVEL, CATEGORY); \ + static Statement::Initializer init_(stmt_); \ + FLAG = stmt_.enabled; \ + } while(0) + +/** + * 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 + * Using QPID_LOG implies a category of Unspecified. + * + * 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) QPID_LOG_IF(LEVEL, true, MESSAGE); + +/** + * Macro for log statements. Example of use: + * @code + * QPID_LOG_CAT(debug, System, "There are " << foocount << " foos in the bar."); + * QPID_LOG_CAT(error, System, boost::format("Dohickey %s exploded") % dohicky.name()); + * @endcode + * Using QPID_LOG_CAT requires the specification of a category. + * + * 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 CATEGORY basic Category for the message. + *@param MESSAGE any object with an @eostream operator<<, or a sequence + * like of ostreamable objects separated by @e<<. + */ +#define QPID_LOG_CAT(LEVEL, CATEGORY, MESSAGE) QPID_LOG_IF_CAT(LEVEL, CATEGORY, true, MESSAGE); + +}} // namespace qpid::log + + + + +#endif /*!STATEMENT_H*/ + diff --git a/qpid/cpp/src/qpid/log/posix/SinkOptions.cpp b/qpid/cpp/src/qpid/log/posix/SinkOptions.cpp new file mode 100644 index 0000000000..210022551e --- /dev/null +++ b/qpid/cpp/src/qpid/log/posix/SinkOptions.cpp @@ -0,0 +1,222 @@ +/* + * + * 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/posix/SinkOptions.h" +#include "qpid/log/SinkOptions.h" +#include "qpid/log/Logger.h" +#include "qpid/log/OstreamOutput.h" +#include "qpid/OptionsTemplates.h" +#include "qpid/memory.h" +#include "qpid/Exception.h" + +#include <iostream> +#include <map> +#include <string> +#include <syslog.h> + +#include <boost/lexical_cast.hpp> + +using std::string; +using qpid::Exception; + +namespace qpid { + +template po::value_semantic* create_value(log::posix::SyslogFacility& val, const std::string& arg); + +namespace log { +namespace posix { + +namespace { + +// SyslogFacilities maps from syslog values to the text equivalents. +class SyslogFacilities { +public: + typedef std::map<string, int> ByName; + typedef std::map<int, string> ByValue; + + SyslogFacilities() { + struct NameValue { const char* name; int value; }; + NameValue nameValue[] = { + { "AUTH", LOG_AUTH }, +#ifdef HAVE_LOG_AUTHPRIV + { "AUTHPRIV", LOG_AUTHPRIV }, +#endif + { "CRON", LOG_CRON }, + { "DAEMON", LOG_DAEMON }, +#ifdef HAVE_LOG_FTP + { "FTP", LOG_FTP }, +#endif + { "KERN", LOG_KERN }, + { "LOCAL0", LOG_LOCAL0 }, + { "LOCAL1", LOG_LOCAL1 }, + { "LOCAL2", LOG_LOCAL2 }, + { "LOCAL3", LOG_LOCAL3 }, + { "LOCAL4", LOG_LOCAL4 }, + { "LOCAL5", LOG_LOCAL5 }, + { "LOCAL6", LOG_LOCAL6 }, + { "LOCAL7", LOG_LOCAL7 }, + { "LPR", LOG_LPR }, + { "MAIL", LOG_MAIL }, + { "NEWS", LOG_NEWS }, + { "SYSLOG", LOG_SYSLOG }, + { "USER", LOG_USER }, + { "UUCP", LOG_UUCP } + }; + for (size_t i = 0; i < sizeof(nameValue)/sizeof(nameValue[0]); ++i) { + byName.insert(ByName::value_type(nameValue[i].name, nameValue[i].value)); + // Recognise with and without LOG_ prefix e.g.: AUTH and LOG_AUTH + byName.insert(ByName::value_type(string("LOG_")+nameValue[i].name, nameValue[i].value)); + byValue.insert(ByValue::value_type(nameValue[i].value, string("LOG_")+nameValue[i].name)); + } + } + + int value(const string& name) const { + string key(name); + std::transform(key.begin(), key.end(), key.begin(), ::toupper); + ByName::const_iterator i = byName.find(key); + if (i == byName.end()) + throw Exception("Not a valid syslog facility: " + name); + return i->second; + } + + string name(int value) const { + ByValue::const_iterator i = byValue.find(value); + if (i == byValue.end()) + throw Exception("Not a valid syslog value: " + boost::lexical_cast<string>(value)); + return i->second; + } + + private: + ByName byName; + ByValue byValue; +}; + +// 'priorities' maps qpid log levels to syslog priorities. They are in +// order of qpid log levels and must map to: +// "trace", "debug", "info", "notice", "warning", "error", "critical" +static int priorities[qpid::log::LevelTraits::COUNT] = { + LOG_DEBUG, LOG_DEBUG, LOG_INFO, LOG_NOTICE, + LOG_WARNING, LOG_ERR, LOG_CRIT +}; + +std::string basename(const std::string path) { + size_t i = path.find_last_of('/'); + return path.substr((i == std::string::npos) ? 0 : i+1); +} + +} // namespace + +std::ostream& operator<<(std::ostream& o, const SyslogFacility& f) { + return o << SyslogFacilities().name(f.value); +} + +std::istream& operator>>(std::istream& i, SyslogFacility& f) { + std::string name; + i >> name; + f.value = SyslogFacilities().value(name); + return i; +} + +class SyslogOutput : public qpid::log::Logger::Output { +public: + SyslogOutput(const std::string& logName, const SyslogFacility& logFacility) + : name(logName), facility(logFacility.value) + { + ::openlog(name.c_str(), LOG_PID, facility); + } + + virtual ~SyslogOutput() { + ::closelog(); + } + + virtual void log(const Statement& s, const std::string& m) + { + syslog(priorities[s.level], "%s", m.c_str()); + } + +private: + std::string name; + int facility; +}; + +SinkOptions::SinkOptions(const std::string& argv0) + : qpid::log::SinkOptions(), + logToStderr(true), + logToStdout(false), + logToSyslog(false), + syslogName(basename(argv0)), + syslogFacility(LOG_DAEMON) { + + addOptions() + ("log-to-stderr", optValue(logToStderr, "yes|no"), "Send logging output to stderr") + ("log-to-stdout", optValue(logToStdout, "yes|no"), "Send logging output to stdout") + ("log-to-file", optValue(logFile, "FILE"), "Send log output to FILE.") + ("log-to-syslog", optValue(logToSyslog, "yes|no"), "Send logging output to syslog;\n\tcustomize using --syslog-name and --syslog-facility") + ("syslog-name", optValue(syslogName, "NAME"), "Name to use in syslog messages") + ("syslog-facility", optValue(syslogFacility,"LOG_XXX"), "Facility to use in syslog messages") + ; + +} + +qpid::log::SinkOptions& SinkOptions::operator=(const qpid::log::SinkOptions& rhs) { + const SinkOptions *prhs = dynamic_cast<const SinkOptions*>(&rhs); + if (this != prhs) { + logToStderr = prhs->logToStderr; + logToStdout = prhs->logToStdout; + logToSyslog = prhs->logToSyslog; + logFile = prhs->logFile; + syslogName = prhs->syslogName; + syslogFacility.value = prhs->syslogFacility.value; + } + return *this; +} + +void SinkOptions::detached(void) { + if (logToStderr && !logToStdout && !logToSyslog && logFile.empty()) { + logToStderr = false; + logToSyslog = true; + } +} + +// The Logger acting on these options calls setup() to request any +// Sinks be set up and fed back to the logger. +void SinkOptions::setup(qpid::log::Logger *logger) { + if (logToStderr) + logger->output(make_auto_ptr<qpid::log::Logger::Output> + (new qpid::log::OstreamOutput(std::clog))); + if (logToStdout) + logger->output(make_auto_ptr<qpid::log::Logger::Output> + (new qpid::log::OstreamOutput(std::cout))); + + if (logFile.length() > 0) + logger->output(make_auto_ptr<qpid::log::Logger::Output> + (new qpid::log::OstreamOutput(logFile))); + + if (logToSyslog) + logger->output(make_auto_ptr<qpid::log::Logger::Output> + (new SyslogOutput(syslogName, syslogFacility))); + +} + +} // namespace qpid::log::posix + +SinkOptions* SinkOptions::create(const std::string& argv0) { + return new qpid::log::posix::SinkOptions (argv0); +} + +}} // namespace qpid::log diff --git a/qpid/cpp/src/qpid/log/posix/SinkOptions.h b/qpid/cpp/src/qpid/log/posix/SinkOptions.h new file mode 100644 index 0000000000..d929c29025 --- /dev/null +++ b/qpid/cpp/src/qpid/log/posix/SinkOptions.h @@ -0,0 +1,64 @@ +#ifndef QPID_LOG_POSIX_SINKOPTIONS_H +#define QPID_LOG_POSIX_SINKOPTIONS_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/SinkOptions.h" +#include <string> + +namespace qpid { +namespace log { +namespace posix { + +/** + * Provides a type that can be passed to << and >> operators to convert + * syslog facility values to/from strings. + */ +struct SyslogFacility { + int value; + SyslogFacility(int i=0) : value(i) {} +}; + +struct SinkOptions : public qpid::log::SinkOptions { + SinkOptions(const std::string& argv0); + virtual ~SinkOptions() {} + + virtual qpid::log::SinkOptions& operator=(const qpid::log::SinkOptions& rhs); + + // This allows the caller to indicate that there's no normal outputs + // available. For example, when running as a daemon. In these cases, the + // platform's "syslog"-type output should replace the default stderr + // unless some other sink has been selected. + virtual void detached(void); + + // The Logger acting on these options calls setup() to request any + // Sinks be set up and fed back to the logger. + virtual void setup(qpid::log::Logger *logger); + + bool logToStderr; + bool logToStdout; + bool logToSyslog; + std::string logFile; + std::string syslogName; + SyslogFacility syslogFacility; +}; + +}}} // namespace qpid::log::posix + +#endif /*!QPID_LOG_POSIX_SINKOPTIONS_H*/ diff --git a/qpid/cpp/src/qpid/log/windows/SinkOptions.cpp b/qpid/cpp/src/qpid/log/windows/SinkOptions.cpp new file mode 100644 index 0000000000..0c74bea64e --- /dev/null +++ b/qpid/cpp/src/qpid/log/windows/SinkOptions.cpp @@ -0,0 +1,148 @@ +/* + * + * 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/windows/SinkOptions.h" +#include "qpid/log/SinkOptions.h" +#include "qpid/log/Logger.h" +#include "qpid/log/OstreamOutput.h" +#include "qpid/memory.h" +#include "qpid/Exception.h" +#include <iostream> +#include <map> +#include <string> + +#include <windows.h> + +using qpid::Exception; + +namespace qpid { +namespace log { +namespace windows { + +namespace { + +// 'eventTypes' maps qpid log levels to Windows event types. They are in +// order of qpid log levels and must map to: +// "trace", "debug", "info", "notice", "warning", "error", "critical" +static int eventTypes[qpid::log::LevelTraits::COUNT] = { + EVENTLOG_INFORMATION_TYPE, /* trace */ + EVENTLOG_INFORMATION_TYPE, /* debug */ + EVENTLOG_INFORMATION_TYPE, /* info */ + EVENTLOG_INFORMATION_TYPE, /* notice */ + EVENTLOG_WARNING_TYPE, /* warning */ + EVENTLOG_ERROR_TYPE, /* error */ + EVENTLOG_ERROR_TYPE /* critical */ +}; + +} // namespace + +class EventLogOutput : public qpid::log::Logger::Output { +public: + EventLogOutput(const std::string& /*sourceName*/) : logHandle(0) + { + logHandle = OpenEventLog(0, "Application"); + } + + virtual ~EventLogOutput() { + if (logHandle) + CloseEventLog(logHandle); + } + + virtual void log(const Statement& s, const std::string& m) + { + if (logHandle) { + const char *msg = m.c_str(); + ReportEvent(logHandle, + eventTypes[s.level], + 0, /* category unused */ + 0, /* event id */ + 0, /* user security id */ + 1, /* number of strings */ + 0, /* no event-specific data */ + &msg, + 0); + } + } + +private: + HANDLE logHandle; +}; + +SinkOptions::SinkOptions(const std::string& /*argv0*/) + : qpid::log::SinkOptions(), + logToStderr(true), + logToStdout(false), + logToEventLog(false), + eventSource("Application") +{ + addOptions() + ("log-to-stderr", optValue(logToStderr, "yes|no"), "Send logging output to stderr") + ("log-to-stdout", optValue(logToStdout, "yes|no"), "Send logging output to stdout") + ("log-to-file", optValue(logFile, "FILE"), "Send log output to FILE.") + ("log-to-eventlog", optValue(logToEventLog, "yes|no"), "Send logging output to event log;\n\tcustomize using --syslog-name and --syslog-facility") + ("eventlog-source-name", optValue(eventSource, "Application"), "Event source to log to") + ; + +} + +qpid::log::SinkOptions& SinkOptions::operator=(const qpid::log::SinkOptions& rhs) { + const SinkOptions *prhs = dynamic_cast<const SinkOptions*>(&rhs); + if (this != prhs) { + logToStderr = prhs->logToStderr; + logToStdout = prhs->logToStdout; + logToEventLog = prhs->logToEventLog; + eventSource = prhs->eventSource; + logFile = prhs->logFile; + } + return *this; +} + +void SinkOptions::detached(void) { + if (logToStderr && !logToStdout && !logToEventLog) { + logToStderr = false; + logToEventLog = true; + } +} + +// The Logger acting on these options calls setup() to request any +// Sinks be set up and fed back to the logger. +void SinkOptions::setup(qpid::log::Logger *logger) { + if (logToStderr) + logger->output(make_auto_ptr<qpid::log::Logger::Output> + (new qpid::log::OstreamOutput(std::clog))); + if (logToStdout) + logger->output(make_auto_ptr<qpid::log::Logger::Output> + (new qpid::log::OstreamOutput(std::cout))); + + if (logFile.length() > 0) + logger->output(make_auto_ptr<qpid::log::Logger::Output> + (new qpid::log::OstreamOutput(logFile))); + + if (logToEventLog) + logger->output(make_auto_ptr<qpid::log::Logger::Output> + (new EventLogOutput(eventSource))); + +} + +} // namespace windows + +SinkOptions* SinkOptions::create(const std::string& argv0) { + return new qpid::log::windows::SinkOptions (argv0); +} + +}} // namespace qpid::log diff --git a/qpid/cpp/src/qpid/log/windows/SinkOptions.h b/qpid/cpp/src/qpid/log/windows/SinkOptions.h new file mode 100644 index 0000000000..f270c504a2 --- /dev/null +++ b/qpid/cpp/src/qpid/log/windows/SinkOptions.h @@ -0,0 +1,54 @@ +#ifndef QPID_LOG_WINDOWS_SINKOPTIONS_H +#define QPID_LOG_WINDOWS_SINKOPTIONS_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/SinkOptions.h" +#include <string> + +namespace qpid { +namespace log { +namespace windows { + +struct QPID_COMMON_CLASS_EXTERN SinkOptions : public qpid::log::SinkOptions { + QPID_COMMON_EXTERN SinkOptions(const std::string& argv0); + virtual ~SinkOptions() {} + + QPID_COMMON_EXTERN virtual qpid::log::SinkOptions& operator=(const qpid::log::SinkOptions& rhs); + + // This allows the caller to indicate that there's no normal outputs + // available. For example, when running as a service. In these cases, the + // platform's "syslog"-type output should replace the default stderr + // unless some other sink has been selected. + QPID_COMMON_EXTERN virtual void detached(void); + + // The Logger acting on these options calls setup() to request any + // Sinks be set up and fed back to the logger. + QPID_COMMON_EXTERN virtual void setup(qpid::log::Logger *logger); + + bool logToStderr; + bool logToStdout; + bool logToEventLog; + std::string eventSource; + std::string logFile; +}; + +}}} // namespace qpid::log::windows + +#endif /*!QPID_LOG_WINDOWS_SINKOPTIONS_H*/ |