diff options
author | Stephen D. Huston <shuston@apache.org> | 2008-10-15 22:33:34 +0000 |
---|---|---|
committer | Stephen D. Huston <shuston@apache.org> | 2008-10-15 22:33:34 +0000 |
commit | 708790b9914b97404974860f0b801b720dd7a0ec (patch) | |
tree | 7f192e792e4c27d7930a64b2387203b2349dff30 /cpp | |
parent | 8cdf7df6f7690b0fc789ad2a9735299953384daf (diff) | |
download | qpid-python-708790b9914b97404974860f0b801b720dd7a0ec.tar.gz |
Split logging options into portable options and sink-related options that are platform-specific. Re-did sink options for Posix as discussed on qpid-dev (no more --log-output, but more specific --log-to-<target> options. Allows addition of Windows options without further reorg of Posix code.
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@705083 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'cpp')
-rw-r--r-- | cpp/docs/man/qpidd.x | 6 | ||||
-rw-r--r-- | cpp/src/Makefile.am | 48 | ||||
-rw-r--r-- | cpp/src/qpid/Plugin.h | 2 | ||||
-rw-r--r-- | cpp/src/qpid/log/Logger.cpp | 67 | ||||
-rw-r--r-- | cpp/src/qpid/log/Logger.h | 37 | ||||
-rw-r--r-- | cpp/src/qpid/log/Options.cpp | 129 | ||||
-rw-r--r-- | cpp/src/qpid/log/Options.h | 31 | ||||
-rw-r--r-- | cpp/src/qpid/log/OstreamOutput.cpp | 41 | ||||
-rw-r--r-- | cpp/src/qpid/log/OstreamOutput.h | 41 | ||||
-rw-r--r-- | cpp/src/qpid/log/SinkOptions.h | 64 | ||||
-rw-r--r-- | cpp/src/qpid/log/posix/SinkOptions.cpp | 211 | ||||
-rw-r--r-- | cpp/src/qpid/log/posix/SinkOptions.h | 64 | ||||
-rw-r--r-- | cpp/src/qpidd.cpp | 6 | ||||
-rw-r--r-- | cpp/src/tests/logging.cpp | 37 | ||||
-rwxr-xr-x | cpp/src/tests/start_broker | 2 | ||||
-rwxr-xr-x | cpp/src/tests/start_cluster | 4 | ||||
-rwxr-xr-x | cpp/src/tests/start_cluster_hosts | 2 |
17 files changed, 542 insertions, 250 deletions
diff --git a/cpp/docs/man/qpidd.x b/cpp/docs/man/qpidd.x index edbe7b3bff..9f1b465701 100644 --- a/cpp/docs/man/qpidd.x +++ b/cpp/docs/man/qpidd.x @@ -28,8 +28,8 @@ Each line is a name=value pair. Blank lines and lines beginning with # are ignor # My qpidd configuration file. port=6000 max-connections=10 - log-output=stdout - log-output=/tmp/qpidd.log + log-to-stdout=yes + log-to-file=/tmp/qpidd.log [ENVIRONMENT] .I QPID_<option> @@ -41,6 +41,6 @@ The environment variable is the option name in uppercase, prefixed with QPID_ an export QPID_PORT=6000 export QPID_MAX_CONNECTIONS=10 - export QPID_LOG_OUTPUT=/tmp/qpidd.log + export QPID_LOG_TO_FILE=/tmp/qpidd.log diff --git a/cpp/src/Makefile.am b/cpp/src/Makefile.am index 37fa6b50d6..699af084fc 100644 --- a/cpp/src/Makefile.am +++ b/cpp/src/Makefile.am @@ -68,6 +68,7 @@ sbin_PROGRAMS = qpidd qpidd_SOURCES = qpidd.cpp posix_plat_src = \ + qpid/log/posix/SinkOptions.cpp \ qpid/sys/posix/IOHandle.cpp \ qpid/sys/posix/Socket.cpp \ qpid/sys/posix/AsynchIO.cpp \ @@ -83,6 +84,7 @@ posix_plat_src = \ qpid/sys/posix/PollableCondition.cpp posix_plat_hdr = \ + qpid/log/posix/SinkOptions.h \ qpid/sys/posix/check.h \ qpid/sys/posix/Condition.h \ qpid/sys/posix/PrivatePosix.h \ @@ -117,15 +119,6 @@ include acl.mk include qmf.mk include xml.mk -# The logger library uses boost::date_time to format time. -# We have to disable the unused parameters warning to get around -# unused parameters in boost::date_time headers. So we build it -# in a convenience library to link into libqpid_common. -# -noinst_LTLIBRARIES=libLogger.la # libqpidamqp_0_10.la -libLogger_la_SOURCES=qpid/log/Logger.cpp qpid/log/Logger.h -libLogger_la_CXXFLAGS=$(AM_CXXFLAGS) -Wno-unused-parameter - if RDMA # RDMA (Infiniband) protocol code @@ -222,19 +215,28 @@ libqpidcommon_la_LIBADD = \ -lboost_program_options \ -lboost_filesystem \ -luuid \ - libLogger.la \ $(LIB_DLOPEN) \ $(LIB_CLOCK_GETTIME) libqpidcommon_la_SOURCES = \ $(rgen_framing_srcs) \ $(platform_src) \ - qpid/amqp_0_10/SessionHandler.h \ - qpid/amqp_0_10/SessionHandler.cpp \ + qpid/assert.cpp qpid/assert.h \ + qpid/pointer_to_other.h \ + qpid/DataDir.cpp \ + qpid/Exception.cpp \ + qpid/Options.cpp \ + qpid/Plugin.cpp \ + qpid/RefCountedBuffer.h \ + qpid/RefCountedBuffer.cpp \ qpid/Serializer.h \ qpid/SessionState.h \ qpid/SessionState.cpp \ qpid/SessionId.cpp \ + qpid/StringUtils.cpp \ + qpid/Url.cpp \ + qpid/amqp_0_10/SessionHandler.h \ + qpid/amqp_0_10/SessionHandler.cpp \ qpid/framing/AccumulatedAck.cpp \ qpid/framing/AMQBody.cpp \ qpid/framing/AMQMethodBody.cpp \ @@ -262,11 +264,11 @@ libqpidcommon_la_SOURCES = \ qpid/framing/Blob.cpp \ qpid/framing/MaxMethodBodySize.h \ qpid/framing/TransferContent.cpp \ - qpid/assert.cpp qpid/assert.h \ - qpid/Exception.cpp \ - qpid/Plugin.cpp \ - qpid/StringUtils.cpp \ - qpid/Url.cpp \ + qpid/log/Logger.cpp \ + qpid/log/Options.cpp \ + qpid/log/OstreamOutput.cpp \ + qpid/log/Selector.cpp \ + qpid/log/Statement.cpp \ qpid/management/Manageable.cpp \ qpid/management/ManagementObject.cpp \ qpid/sys/AggregateOutput.cpp \ @@ -275,15 +277,7 @@ libqpidcommon_la_SOURCES = \ qpid/sys/PollableCondition.h \ qpid/sys/PollableQueue.h \ qpid/sys/Runnable.cpp \ - qpid/sys/Shlib.cpp \ - qpid/DataDir.cpp \ - qpid/Options.cpp \ - qpid/log/Options.cpp \ - qpid/RefCountedBuffer.h \ - qpid/RefCountedBuffer.cpp \ - qpid/log/Selector.cpp \ - qpid/log/Statement.cpp \ - qpid/pointer_to_other.h + qpid/sys/Shlib.cpp libqpidbroker_la_LIBADD = libqpidcommon.la -luuid if HAVE_SASL @@ -578,7 +572,9 @@ nobase_include_HEADERS = \ qpid/log/Helpers.h \ qpid/log/Options.h \ qpid/log/Logger.h \ + qpid/log/OstreamOutput.h \ qpid/log/Selector.h \ + qpid/log/SinkOptions.h \ qpid/log/Statement.h \ qpid/management/Args.h \ qpid/management/Manageable.h \ diff --git a/cpp/src/qpid/Plugin.h b/cpp/src/qpid/Plugin.h index 3c7c8031bb..50d8e01227 100644 --- a/cpp/src/qpid/Plugin.h +++ b/cpp/src/qpid/Plugin.h @@ -28,7 +28,7 @@ /**@file Generic plug-in framework. */ namespace qpid { -class Options; +struct Options; /** * Plug-in base class. diff --git a/cpp/src/qpid/log/Logger.cpp b/cpp/src/qpid/log/Logger.cpp index b4f5bde186..07e4245399 100644 --- a/cpp/src/qpid/log/Logger.cpp +++ b/cpp/src/qpid/log/Logger.cpp @@ -17,18 +17,17 @@ */ #include "Logger.h" +#include "Options.h" +#include "SinkOptions.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/scoped_ptr.hpp> #include <algorithm> #include <sstream> -#include <fstream> #include <iomanip> #include <stdexcept> -#include <syslog.h> #include <time.h> @@ -43,45 +42,6 @@ inline void Logger::enable_unlocked(Statement* s) { s->enabled=selector.isEnabled(s->level, s->function); } -struct OstreamOutput : public Logger::Output { - OstreamOutput(std::ostream& o) : out(&o) {} - - OstreamOutput(const 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 log(const Statement&, const std::string& m) { - *out << m << flush; - } - - ostream* out; - boost::scoped_ptr<ostream> mine; -}; - -struct SyslogOutput : public Logger::Output { - SyslogOutput(const Options& opts) - : name(opts.syslogName), facility(opts.syslogFacility.value) - { - ::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 name; - int facility; -}; - Logger& Logger::instance() { return boost::details::pool::singleton_default<Logger>::instance(); } @@ -158,25 +118,6 @@ void Logger::output(std::auto_ptr<Output> out) { outputs.push_back(out.release()); } -void Logger::output(std::ostream& out) { - output(make_auto_ptr<Output>(new OstreamOutput(out))); -} - -void Logger::syslog(const Options& opts) { - output(make_auto_ptr<Output>(new SyslogOutput(opts))); -} - -void Logger::output(const std::string& name, const Options& opts) { - if (name=="stderr") - output(clog); - else if (name=="stdout") - output(cout); - else if (name=="syslog") - syslog(opts); - else - output(make_auto_ptr<Output>(new OstreamOutput(name))); -} - void Logger::clear() { select(Selector()); // locked format(0); // locked @@ -218,10 +159,8 @@ void Logger::configure(const Options& opts) { o.selectors.push_back("trace+"); format(o); select(Selector(o)); - void (Logger::* outputFn)(const std::string&, const Options&) = &Logger::output; - for_each(o.outputs.begin(), o.outputs.end(), - boost::bind(outputFn, this, _1, boost::cref(o))); setPrefix(opts.prefix); + options.sinkOptions->setup(this); } void Logger::setPrefix(const std::string& p) { prefix = p; } diff --git a/cpp/src/qpid/log/Logger.h b/cpp/src/qpid/log/Logger.h index 3201eac98f..539c1c851b 100644 --- a/cpp/src/qpid/log/Logger.h +++ b/cpp/src/qpid/log/Logger.h @@ -26,15 +26,25 @@ 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 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. + /** + * 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: @@ -43,7 +53,7 @@ class Logger : private boost::noncopyable { /** Receives the statemnt of origin and formatted message to log. */ virtual void log(const Statement&, const std::string&) =0; }; - + static Logger& instance(); Logger(); @@ -69,25 +79,8 @@ class Logger : private boost::noncopyable { /** 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 Options&); - - /** Add an output. - *@param name a file name or one of the special tokens: - *stdout, stderr, syslog. - */ - void output(const std::string& name, const Options&); - /** Add an output destination for messages */ - void output(std::auto_ptr<Output> out); + void output(std::auto_ptr<Output> out); /** Set a prefix for all messages */ void setPrefix(const std::string& prefix); diff --git a/cpp/src/qpid/log/Options.cpp b/cpp/src/qpid/log/Options.cpp index a850ea470d..4462099eb2 100644 --- a/cpp/src/qpid/log/Options.cpp +++ b/cpp/src/qpid/log/Options.cpp @@ -17,115 +17,38 @@ */ #include "Options.h" +#include "SinkOptions.h" #include "Statement.h" #include "qpid/Options.h" #include <map> #include <string> #include <algorithm> -#include <syslog.h> namespace qpid { namespace log { using namespace std; -namespace { - -class SyslogFacilities { - public: - typedef map<string, int> ByName; - typedef map<int, string> ByValue; - - SyslogFacilities() { - struct NameValue { const char* name; int value; }; - NameValue nameValue[] = { - { "AUTH", LOG_AUTH }, - { "AUTHPRIV", LOG_AUTHPRIV }, - { "CRON", LOG_CRON }, - { "DAEMON", LOG_DAEMON }, - { "FTP", LOG_FTP }, - { "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); - 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: " + value); - return i->second; - } - - private: - ByName byName; - ByValue byValue; -}; - -} - -ostream& operator<<(ostream& o, const SyslogFacility& f) { - return o << SyslogFacilities().name(f.value); -} - -istream& operator>>(istream& i, SyslogFacility& f) { - std::string name; - i >> name; - f.value = SyslogFacilities().value(name); - return i; -} - -namespace { -std::string basename(const std::string path) { - size_t i = path.find_last_of('/'); - return path.substr((i == std::string::npos) ? 0 : i+1); -} -} - -Options::Options(const std::string& argv0, const std::string& name) : - qpid::Options(name), - time(true), level(true), thread(false), source(false), function(false), trace(false), - syslogName(basename(argv0)), syslogFacility(LOG_DAEMON) +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), + trace(false), + sinkOptions (SinkOptions::create(argv0_)) { - outputs.push_back("stderr"); selectors.push_back("error+"); ostringstream levels; levels << LevelTraits::name(Level(0)); for (int i = 1; i < LevelTraits::COUNT; ++i) levels << " " << LevelTraits::name(Level(i)); - + addOptions() - ("log-output", optValue(outputs, "FILE"), "Send log output to FILE. " - "FILE can be a file name or one of the special values:\n" - "stderr, stdout, syslog") ("trace,t", optValue(trace), "Enables all logging" ) ("log-enable", optValue(selectors, "RULE"), ("Enables logging for selected levels and components. " @@ -143,24 +66,40 @@ Options::Options(const std::string& argv0, const std::string& name) : ("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-prefix", optValue(prefix,"STRING"), "Prefix to append to all log messages") - ("syslog-name", optValue(syslogName, "NAME"), "Name to use in syslog messages") - ("syslog-facility", optValue(syslogFacility,"LOG_XXX"), "Facility to use in syslog messages") ; + add(*sinkOptions); +} + +Options::Options(const Options &o) : + qpid::Options(o.name), + argv0(o.argv0), + name(o.name), + selectors(o.selectors), + time(o.time), + level(o.level), + thread(o.thread), + source(o.source), + function(o.function), + 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; - outputs = x.outputs; time = x.time; level= x.level; thread = x.thread; source = x.source; function = x.function; trace = x.trace; - syslogName = x.syslogName; - syslogFacility = x.syslogFacility; prefix = x.prefix; + *sinkOptions = *x.sinkOptions; } return *this; } diff --git a/cpp/src/qpid/log/Options.h b/cpp/src/qpid/log/Options.h index 3d128b5668..8a3c352d14 100644 --- a/cpp/src/qpid/log/Options.h +++ b/cpp/src/qpid/log/Options.h @@ -1,5 +1,5 @@ -#ifndef OPTIONS_H -#define OPTIONS_H +#ifndef QPID_LOG_OPTIONS_H +#define QPID_LOG_OPTIONS_H /* * @@ -19,40 +19,31 @@ * */ #include "qpid/Options.h" +#include "SinkOptions.h" #include <iosfwd> +#include <memory> namespace qpid { namespace log { -/** Provides << and >> operators to convert syslog facility values to/from strings. */ -struct SyslogFacility { - int value; - SyslogFacility(int i=0) : value(i) {} -}; - -std::ostream& operator<<(std::ostream&, const SyslogFacility&); -std::istream& operator>>(std::istream&, SyslogFacility&); - /** Logging options for config parser. */ struct Options : public qpid::Options { /** Pass argv[0] for use in syslog output */ - Options(const std::string& argv0=std::string(), - const std::string& name="Logging options"); + Options(const std::string& argv0_=std::string(), + const std::string& name_="Logging options"); + Options(const Options &); Options& operator=(const Options&); + std::string argv0; + std::string name; std::vector<std::string> selectors; - std::vector<std::string> outputs; bool time, level, thread, source, function; bool trace; - std::string syslogName; - SyslogFacility syslogFacility; std::string prefix; + std::auto_ptr<SinkOptions> sinkOptions; }; - }} // namespace qpid::log - - -#endif /*!OPTIONS_H*/ +#endif /*!QPID_LOG_OPTIONS_H*/ diff --git a/cpp/src/qpid/log/OstreamOutput.cpp b/cpp/src/qpid/log/OstreamOutput.cpp new file mode 100644 index 0000000000..c96a311a4e --- /dev/null +++ b/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 "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/cpp/src/qpid/log/OstreamOutput.h b/cpp/src/qpid/log/OstreamOutput.h new file mode 100644 index 0000000000..8bbfc8c38b --- /dev/null +++ b/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 "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: + OstreamOutput(std::ostream& o); + 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/cpp/src/qpid/log/SinkOptions.h b/cpp/src/qpid/log/SinkOptions.h new file mode 100644 index 0000000000..7ec2cfbc17 --- /dev/null +++ b/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/cpp/src/qpid/log/posix/SinkOptions.cpp b/cpp/src/qpid/log/posix/SinkOptions.cpp new file mode 100644 index 0000000000..9d51358e2e --- /dev/null +++ b/cpp/src/qpid/log/posix/SinkOptions.cpp @@ -0,0 +1,211 @@ +/* + * + * 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 "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 <syslog.h> + +using std::string; +using qpid::Exception; + +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 }, + { "AUTHPRIV", LOG_AUTHPRIV }, + { "CRON", LOG_CRON }, + { "DAEMON", LOG_DAEMON }, + { "FTP", LOG_FTP }, + { "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); + 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: " + 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 + +namespace qpid { +namespace log { +namespace posix { + +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) { + 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/cpp/src/qpid/log/posix/SinkOptions.h b/cpp/src/qpid/log/posix/SinkOptions.h new file mode 100644 index 0000000000..d929c29025 --- /dev/null +++ b/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/cpp/src/qpidd.cpp b/cpp/src/qpidd.cpp index 34d831519e..1d272dbd5c 100644 --- a/cpp/src/qpidd.cpp +++ b/cpp/src/qpidd.cpp @@ -229,10 +229,8 @@ int main(int argc, char* argv[]) // Starting the broker. if (options->daemon.daemon) { // For daemon mode replace default stderr with syslog. - if (options->log.outputs.size() == 1 && options->log.outputs[0] == "stderr") { - options->log.outputs[0] = "syslog"; - qpid::log::Logger::instance().configure(options->log); - } + options->log.sinkOptions->detached(); + qpid::log::Logger::instance().configure(options->log); // Fork the daemon QpiddDaemon d(options->daemon.piddir); d.fork(); // Broker is stared in QpiddDaemon::child() diff --git a/cpp/src/tests/logging.cpp b/cpp/src/tests/logging.cpp index 32163c0058..bfd7e52eda 100644 --- a/cpp/src/tests/logging.cpp +++ b/cpp/src/tests/logging.cpp @@ -19,8 +19,13 @@ #include "test_tools.h" #include "qpid/log/Logger.h" #include "qpid/log/Options.h" +#include "qpid/log/OstreamOutput.h" #include "qpid/memory.h" #include "qpid/Options.h" +#if defined (_WIN32) +#else +# include "qpid/log/posix/SinkOptions.h" +#endif #include <boost/test/floating_point_comparison.hpp> #include <boost/format.hpp> @@ -179,7 +184,7 @@ QPID_AUTO_TEST_CASE(testOstreamOutput) { ScopedSuppressLogging ls(l); l.select(Selector(error)); ostringstream os; - l.output(os); + l.output(qpid::make_auto_ptr<Logger::Output>(new OstreamOutput(os))); QPID_LOG(error, "foo"); QPID_LOG(error, "bar"); QPID_LOG(error, "baz"); @@ -257,19 +262,22 @@ QPID_AUTO_TEST_CASE(testOptionsParse) { "--log-enable", "error+:foo", "--log-enable", "debug:bar", "--log-enable", "info", - "--log-output", "x", - "--log-output", "y", + "--log-to-stderr", "no", + "--log-to-file", "logout", "--log-level", "yes", "--log-source", "1", "--log-thread", "true", "--log-function", "YES" }; qpid::log::Options opts(""); + qpid::log::posix::SinkOptions sinks("test"); opts.parse(ARGC(argv), const_cast<char**>(argv)); + sinks = *opts.sinkOptions; vector<string> expect=list_of("error+:foo")("debug:bar")("info"); BOOST_CHECK_EQUAL(expect, opts.selectors); - expect=list_of("x")("y"); - BOOST_CHECK_EQUAL(expect, opts.outputs); + BOOST_CHECK(!sinks.logToStderr); + BOOST_CHECK(!sinks.logToStdout); + BOOST_CHECK(sinks.logFile == "logout"); BOOST_CHECK(opts.level); BOOST_CHECK(opts.source); BOOST_CHECK(opts.function); @@ -278,9 +286,12 @@ QPID_AUTO_TEST_CASE(testOptionsParse) { QPID_AUTO_TEST_CASE(testOptionsDefault) { Options opts(""); - vector<string> expect=list_of("stderr"); - BOOST_CHECK_EQUAL(expect, opts.outputs); - expect=list_of("error+"); + qpid::log::posix::SinkOptions sinks("test"); + sinks = *opts.sinkOptions; + BOOST_CHECK(sinks.logToStderr); + BOOST_CHECK(!sinks.logToStdout); + BOOST_CHECK(sinks.logFile.length() == 0); + vector<string> expect=list_of("error+"); BOOST_CHECK_EQUAL(expect, opts.selectors); BOOST_CHECK(opts.time && opts.level); BOOST_CHECK(!(opts.source || opts.function || opts.thread)); @@ -313,7 +324,8 @@ QPID_AUTO_TEST_CASE(testLoggerStateure) { 0, "--log-time", "no", "--log-source", "yes", - "--log-output", "logging.tmp", + "--log-to-stderr", "no", + "--log-to-file", "logging.tmp", "--log-enable", "critical" }; opts.parse(ARGC(argv), const_cast<char**>(argv)); @@ -332,10 +344,13 @@ QPID_AUTO_TEST_CASE(testQuoteNonPrintable) { Logger& l=Logger::instance(); ScopedSuppressLogging ls(l); Options opts("test"); - opts.outputs.clear(); - opts.outputs.push_back("logging.tmp"); opts.time=false; + qpid::log::posix::SinkOptions *sinks = + dynamic_cast<qpid::log::posix::SinkOptions *>(opts.sinkOptions.get()); + sinks->logToStderr = false; + sinks->logFile = "logging.tmp"; l.configure(opts); + char s[] = "null\0tab\tspace newline\nret\r\x80\x99\xff"; string str(s, sizeof(s)); QPID_LOG(critical, str); diff --git a/cpp/src/tests/start_broker b/cpp/src/tests/start_broker index aabe12ad43..77ec83c5f1 100755 --- a/cpp/src/tests/start_broker +++ b/cpp/src/tests/start_broker @@ -1,4 +1,4 @@ #!/bin/sh # Start a test broker. srcdir=`dirname $0` -exec $srcdir/run_test ../qpidd --auth=no --no-module-dir --daemon --port=0 --log-output qpidd.log "$@" > qpidd.port +exec $srcdir/run_test ../qpidd --auth=no --no-module-dir --daemon --port=0 --log-to-file qpidd.log "$@" > qpidd.port diff --git a/cpp/src/tests/start_cluster b/cpp/src/tests/start_cluster index 0c1722e566..1dc3773500 100755 --- a/cpp/src/tests/start_cluster +++ b/cpp/src/tests/start_cluster @@ -16,10 +16,10 @@ OPTS="-d --load-module ../.libs/cluster.so --cluster-name=$CLUSTER --no-data-di if test "$SIZE" = "one"; then # Special case of singleton cluster, use default port. ../qpidd -q - with_ais_group ../qpidd $OPTS --log-output=cluster.log || exit 1 + with_ais_group ../qpidd $OPTS --log-to-file=cluster.log || exit 1 else for (( i=0; i<SIZE; ++i )); do - PORT=`with_ais_group ../qpidd -p0 --log-output=cluster$i.log $OPTS` || exit 1 + PORT=`with_ais_group ../qpidd -p0 --log-to-file=cluster$i.log $OPTS` || exit 1 echo $PORT >> cluster.ports done fi diff --git a/cpp/src/tests/start_cluster_hosts b/cpp/src/tests/start_cluster_hosts index f1dce9f0de..40670f8a07 100755 --- a/cpp/src/tests/start_cluster_hosts +++ b/cpp/src/tests/start_cluster_hosts @@ -37,7 +37,7 @@ CLUSTER=${*:-$CLUSTER} # Use args or env test -z "$CLUSTER" && { echo Must specify at least one host; exit 1; } -OPTS="-d $PORTOPT --load-module $LIBQPIDCLUSTER --cluster-name=$NAME --no-data-dir --auth=no --log-output=syslog --log-enable=info+" +OPTS="-d $PORTOPT --load-module $LIBQPIDCLUSTER --cluster-name=$NAME --no-data-dir --auth=no --log-to-syslog --log-enable=info+" num=0 for h in $CLUSTER; do |