From 6c7735f6924883ff51f174a0e934e328318d098b Mon Sep 17 00:00:00 2001 From: Colin Patrick McCabe Date: Mon, 6 Dec 2010 22:32:42 -0800 Subject: [PATCH] logging: DoutStreambuf must handle stdout + stderr Signed-off-by: Colin McCabe --- src/common/DoutStreambuf.cc | 171 +++++++++++++++++++++++++++++----- src/common/DoutStreambuf.h | 24 ++++- src/test/TestDoutStreambuf.cc | 14 ++- 3 files changed, 180 insertions(+), 29 deletions(-) diff --git a/src/common/DoutStreambuf.cc b/src/common/DoutStreambuf.cc index ca75af11d39db..3e2a77ed3b74d 100644 --- a/src/common/DoutStreambuf.cc +++ b/src/common/DoutStreambuf.cc @@ -12,27 +12,62 @@ * */ +#include "config.h" #include "common/DoutStreambuf.h" +#include "common/Mutex.h" #include #include #include #include -#include #include +#include + +///////////////////////////// Globals ///////////////////////////// +// TODO: get rid of this lock using thread-local storage +extern Mutex _dout_lock; + +/* True if we should output high-priority messages to stderr */ +static bool use_stderr = true; + +///////////////////////////// Helper functions ///////////////////////////// +/* Complain about errors even without a logfile */ +static void primitive_log(const std::string &str) +{ + std::cerr << str; + std::cerr.flush(); + syslog(LOG_USER | LOG_NOTICE, "%s", str.c_str()); +} + +static inline bool prio_is_visible_on_stderr(int prio) +{ + return prio <= 15; +} + +static inline int dout_prio_to_syslog_prio(int prio) +{ + if (prio <= 3) + return LOG_CRIT; + if (prio <= 5) + return LOG_ERR; + if (prio <= 15) + return LOG_WARNING; + if (prio <= 30) + return LOG_NOTICE; + if (prio <= 40) + return LOG_INFO; + return LOG_DEBUG; +} +///////////////////////////// DoutStreambuf ///////////////////////////// template DoutStreambuf::DoutStreambuf() { // Initialize get pointer to zero so that underflow is called on the first read. this->setg(0, 0, 0); - // Set up the put pointer. - // Overflow is called when this buffer is filled - this->setp(obuf, obuf + OBUF_SZ - 2); - - // Zero the output buffer - memset(obuf, 0, OBUF_SZ); + // Initialize output_buffer + _clear_output_buffer(); } // This function is called when the output buffer is filled. @@ -42,22 +77,63 @@ template typename DoutStreambuf::int_type DoutStreambuf::overflow(DoutStreambuf::int_type c) { - charT* end = this->pptr(); - - // Add an eof character to the buffer if we need to. - if(!traits_ty::eq_int_type(c, traits_ty::eof())) { - *end++ = traits_ty::to_char_type(c); + { + // zero-terminate the buffer + charT* end_ptr = this->pptr(); + *end_ptr++ = '\0'; + //std::cout << "overflow with '" << obuf << "'" << std::endl; } - *end++ = '\0'; - //std::cout << "overflow with '" << obuf << "'" << std::endl; - - // int_type ilen = end - obuf; // Compute the write length. - syslog(LOG_USER | LOG_NOTICE, "%s", obuf); + // Loop over all lines in the buffer. + int prio = 100; + charT* start = obuf; + while (true) { + charT* end = strchr(start, '\n'); + if (start == end) { + // skip zero-length lines + ++start; + continue; + } + if (*start == '\1') { + // Decode some control characters + ++start; + unsigned char tmp = *((unsigned char*)start); + if (tmp != 0) { + prio = tmp; + ++start; + } + } + if (end) { + *end = '\0'; + } + if (*start == '\0') { + // empty buffer + break; + } + + // Now 'start' points to a NULL-terminated string, which we want to output + // with priority 'prio' + if (flags & DOUTSB_FLAG_SYSLOG) { + syslog(LOG_USER | dout_prio_to_syslog_prio(prio), "%s", start); + } + if (flags & DOUTSB_FLAG_STDOUT) { + puts(start); + } + if (flags & DOUTSB_FLAG_STDERR) { + if (prio_is_visible_on_stderr(prio)) { + fputs(start, stderr); + fputc('\n', stderr); + } + } + + if (!end) { + break; + } + + start = end + 1; + } - // Reset put pointers - setp(obuf, obuf + OBUF_SZ - 1); - obuf[0] = '\0'; + _clear_output_buffer(); // A value different than EOF (or traits::eof() for other traits) signals success. // If the function fails, either EOF (or traits::eof() for other traits) is returned or an @@ -65,6 +141,48 @@ DoutStreambuf::overflow(DoutStreambuf::int_type c) return traits_ty::not_eof(c); } +template +void DoutStreambuf::set_use_stderr(bool val) +{ + _dout_lock.Lock(); + use_stderr = val; + if (val) + flags |= DOUTSB_FLAG_STDERR; + else + flags &= ~DOUTSB_FLAG_STDERR; + _dout_lock.Unlock(); +} + +template +void DoutStreambuf::read_global_configuration() +{ + assert(_dout_lock.is_locked()); + flags = 0; + + if (g_conf.log_to_syslog) { + flags |= DOUTSB_FLAG_SYSLOG; + } + if (g_conf.log_to_stdout) { + flags |= DOUTSB_FLAG_STDOUT; + } + if (use_stderr) { + flags |= DOUTSB_FLAG_STDERR; + } +// if (g_conf.log_to_file) { +// if (read_log_to_file_configuration()) { +// flags |= DOUTSB_FLAG_FILE; +// } +// } +} + +template +void DoutStreambuf:: +set_flags(int flags_) +{ + assert(_dout_lock.is_locked()); + flags = flags_; +} + // This is called to flush the buffer. // This is called when we're done with the file stream (or when .flush() is called). template @@ -72,11 +190,6 @@ typename DoutStreambuf::int_type DoutStreambuf::sync() { //std::cout << "flush!" << std::endl; - - // Don't bother calling overflow if there's nothing to syslog - if (obuf[0] == '\0') - return 0; - typename DoutStreambuf::int_type ret(this->overflow(traits_ty::eof())); if (ret == traits_ty::eof()) @@ -94,5 +207,13 @@ DoutStreambuf::underflow() assert(0); } +template +void DoutStreambuf::_clear_output_buffer() +{ + // Set up the put pointer. + // Overflow is called when this buffer is filled + this->setp(obuf, obuf + OBUF_SZ - 5); +} + // Explicit template instantiation template class DoutStreambuf ; diff --git a/src/common/DoutStreambuf.h b/src/common/DoutStreambuf.h index c808b1b4a1157..4e416c07cf3ff 100644 --- a/src/common/DoutStreambuf.h +++ b/src/common/DoutStreambuf.h @@ -26,16 +26,34 @@ template > class DoutStreambuf : public std::basic_streambuf { public: + enum dout_streambuf_flags_t { + DOUTSB_FLAG_SYSLOG = 0x01, + DOUTSB_FLAG_STDOUT = 0x02, + DOUTSB_FLAG_STDERR = 0x04, + }; + typedef traits traits_ty; typedef typename traits_ty::int_type int_type; typedef typename traits_ty::pos_type pos_type; typedef typename traits_ty::off_type off_type; - // The size of the input and output buffers. + // The size of the output buffer. static const size_t OBUF_SZ = 32000; DoutStreambuf(); + // Set or clear the use_stderr bit + // If this bit is cleared, we don't bother outputting high priority messages + // on stderr any more. We should clear the bit after daemonizing, since + // stderr -> /dev/null at that point. + void set_use_stderr(bool val); + + // Set the flags based on the global configuration + void read_global_configuration(); + + // Set the flags directly (for debug use only) + void set_flags(int flags_); + protected: // Called when the buffer fills up virtual int_type overflow(int_type c); @@ -47,8 +65,12 @@ protected: virtual int_type underflow(); private: + void _clear_output_buffer(); + // Output buffer charT obuf[OBUF_SZ]; + + int flags; }; #endif diff --git a/src/test/TestDoutStreambuf.cc b/src/test/TestDoutStreambuf.cc index 758c3cfaba4e5..82b5dd2ad683c 100644 --- a/src/test/TestDoutStreambuf.cc +++ b/src/test/TestDoutStreambuf.cc @@ -25,6 +25,7 @@ #include #include #include +#include using std::cout; using std::cerr; @@ -40,7 +41,15 @@ int main(int argc, const char **argv) common_set_defaults(false); common_init(args, "ceph", true); - std::ostream oss(new DoutStreambuf); + DoutStreambuf *dos = new DoutStreambuf(); + + _dout_lock.Lock(); + dos->set_flags(DoutStreambuf::DOUTSB_FLAG_SYSLOG | + DoutStreambuf::DOUTSB_FLAG_STDOUT); + _dout_lock.Unlock(); + + std::ostream oss(dos); + syslog(LOG_USER | LOG_NOTICE, "TestDoutStreambuf: starting test\n"); oss << "I am logging to dout now!" << std::endl; @@ -63,8 +72,7 @@ int main(int argc, const char **argv) oss.flush(); oss.flush(); - oss << "But here is a blank line:" << std::endl; - oss << std::endl; + syslog(LOG_USER | LOG_NOTICE, "TestDoutStreambuf: ending test\n"); return 0; } -- 2.39.5