libmoost
/home/mhx/git/github/libmoost/src/service/appender.cpp
Go to the documentation of this file.
00001 /* vim:set ts=3 sw=3 sts=3 et: */
00028 #include "../../include/moost/terminal_format.hpp"
00029 #include "../../include/moost/service/appender.h"
00030 
00031 #include <boost/algorithm/string/split.hpp>
00032 #include <boost/algorithm/string/classification.hpp>
00033 
00034 #include <log4cxx/appenderskeleton.h>
00035 #include <log4cxx/patternlayout.h>
00036 #include <log4cxx/logger.h>
00037 #include <log4cxx/helpers/transcoder.h>
00038 
00039 using namespace moost::service;
00040 
00041 stream_writer_iface::~stream_writer_iface()
00042 {
00043 }
00044 
00045 appender_iface::~appender_iface()
00046 {
00047 }
00048 
00049 appender_factory_iface::~appender_factory_iface()
00050 {
00051 }
00052 
00053 //---------------------------------------------------------------------
00054 
00055 class null_appender : public appender_iface
00056 {
00057 public:
00058    null_appender();
00059 
00060    virtual bool handle_command(std::string&, const std::string&, const std::string&);
00061    virtual std::string show_help() const;
00062    virtual bool attach();
00063    virtual bool detach();
00064 };
00065 
00066 null_appender::null_appender()
00067 {
00068 }
00069 
00070 bool null_appender::handle_command(std::string&, const std::string&, const std::string&)
00071 {
00072    return false;
00073 }
00074 
00075 std::string null_appender::show_help() const
00076 {
00077    return "";
00078 }
00079 
00080 bool null_appender::attach()
00081 {
00082    return true;
00083 }
00084 
00085 bool null_appender::detach()
00086 {
00087    return true;
00088 }
00089 
00090 //---------------------------------------------------------------------
00091 
00092 class log4cxx_appender : public moost::service::appender_iface
00093 {
00094 private:
00095    class impl : public log4cxx::AppenderSkeleton
00096    {
00097    public:
00098       impl(stream_writer_ptr sw)
00099         : m_writer(sw)
00100       {
00101       }
00102 
00103    protected:
00104       virtual void append(const log4cxx::spi::LoggingEventPtr &event, log4cxx::helpers::Pool &pool)
00105       {
00106          log4cxx::LogString ls;
00107          getLayout()->format(ls, event, pool);
00108          std::string s;
00109          log4cxx::helpers::Transcoder::encode(ls, s);
00110          m_writer->write(s.c_str(), s.length());
00111       }
00112 
00113       virtual void close()
00114       {
00115       }
00116 
00117       virtual bool requiresLayout() const
00118       {
00119          return true;
00120       }
00121 
00122    private:
00123       stream_writer_ptr m_writer;
00124    };
00125 
00126 public:
00127    log4cxx_appender(stream_writer_ptr sw, log4cxx::LevelPtr& level)
00128       : m_app(new impl(sw))
00129       , m_layout(new log4cxx::PatternLayout)
00130    {
00131       set_conversion_pattern("[%d{yyyy-MMM-dd HH:mm:ss}|" + moost::terminal_format::color("%c", moost::C_CYAN) + "](%p) %m%n");
00132       m_app->setLayout(m_layout);
00133       m_app->setThreshold(level);
00134    }
00135 
00136    virtual bool handle_command(std::string& rv, const std::string& cmd, const std::string& args)
00137    {
00138       if (cmd == "level")
00139       {
00140          log_level(rv, args);
00141       }
00142       else if (cmd == "pattern")
00143       {
00144          log_pattern(rv, args);
00145       }
00146       else
00147       {
00148          return false;
00149       }
00150 
00151       return true;
00152    }
00153 
00154    virtual std::string show_help() const
00155    {
00156       std::string level;
00157 
00158       m_app->getThreshold()->toString(level);
00159 
00160       return "- level             show all logging levels\r\n"
00161              "- level off|fatal|error|warn|info|debug|trace|all\r\n"
00162              "                    set console log level <" + level + ">\r\n"
00163              "- level root|<appender name>\r\n"
00164              "        [off|fatal|error|warn|info|debug|trace|all]\r\n"
00165              "                    show/set root or appender log level\r\n"
00166              "- pattern [<fmt>]   show/set log pattern\r\n";
00167    }
00168 
00169    virtual bool attach()
00170    {
00171       log4cxx::Logger::getRootLogger()->addAppender(m_app);
00172       return true;
00173    }
00174 
00175    virtual bool detach()
00176    {
00177       log4cxx::Logger::getRootLogger()->removeAppender(m_app);
00178       return true;
00179    }
00180 
00181 private:
00182    bool string_to_level(const std::string& level, log4cxx::LevelPtr& new_level) const
00183    {
00184       log4cxx::LevelPtr invalid = new log4cxx::Level(-1, LOG4CXX_STR("INVALID"), 7);
00185       new_level = log4cxx::Level::toLevel(level, invalid);
00186       return new_level != invalid;
00187    }
00188 
00189    std::string level_to_string(const log4cxx::LevelPtr& level) const
00190    {
00191       if (level)
00192       {
00193          std::string str;
00194          level->toString(str);
00195          return str;
00196       }
00197 
00198       return "<unknown>";
00199    }
00200 
00201    // getThreshold() is non-const so we require a non-const skeleton pointer
00202    std::string get_appender_level(log4cxx::AppenderSkeleton* skel) const
00203    {
00204       return level_to_string(skel->getThreshold());
00205    }
00206 
00207    std::string get_logger_level(const log4cxx::Logger* logger) const
00208    {
00209       return level_to_string(logger->getLevel());
00210    }
00211 
00212    bool log_level(std::string& rv, const std::string& args)
00213    {
00214       std::ostringstream oss;
00215 
00216       if (args.empty())
00217       {
00218          log4cxx::LoggerPtr root = log4cxx::Logger::getRootLogger();
00219 
00220          oss << "console log level is [" << get_appender_level(m_app) << "]\r\n";
00221          oss << "root logger level is [" << get_logger_level(root) << "]\r\n";
00222 
00223          log4cxx::AppenderList appenders = root->getAllAppenders();
00224 
00225          for (log4cxx::AppenderList::iterator it = appenders.begin(); it != appenders.end(); ++it)
00226          {
00227             log4cxx::AppenderSkeleton* skel = dynamic_cast<log4cxx::AppenderSkeleton *>(it->operator->());
00228 
00229             if (skel && skel != m_app && !skel->getName().empty())
00230             {
00231                std::string name;
00232                log4cxx::helpers::Transcoder::encode(skel->getName(), name);
00233                oss << name << " appender level is [" << get_appender_level(skel) << "]\r\n";
00234             }
00235          }
00236       }
00237       else
00238       {
00239          std::vector<std::string> argv;
00240          boost::algorithm::split(argv, args, boost::algorithm::is_any_of(" \t"));
00241          log4cxx::LevelPtr new_level;
00242          bool write = false;
00243 
00244          switch (argv.size())
00245          {
00246             case 1:   // write console log level OR read root logger / named appender log level
00247                if (string_to_level(argv[0], new_level))
00248                {
00249                   // write console log level
00250 
00251                   m_app->setThreshold(new_level);
00252 
00253                   rv = "console log level set to [" + get_appender_level(m_app) + "]\r\n";
00254                   return true;
00255                }
00256                break;
00257 
00258             case 2:   // write named logger log level
00259                if (!string_to_level(argv[1], new_level))
00260                {
00261                   rv = "invalid log level: " + argv[1] + "\r\n";
00262                   return true;
00263                }
00264 
00265                write = true;
00266 
00267                break;
00268 
00269             default:
00270                rv = "invalid number of arguments\r\n";
00271                return true;
00272          }
00273 
00274          std::string action(write ? "set to" : "is");
00275 
00276          if (argv[0] == "root")
00277          {
00278             log4cxx::LoggerPtr root = log4cxx::Logger::getRootLogger();
00279 
00280             if (write)
00281             {
00282                root->setLevel(new_level);
00283             }
00284 
00285             oss << "root logger level " << action << " [" + get_logger_level(root) << "]\r\n";
00286          }
00287          else
00288          {
00289             log4cxx::LogString name;
00290             log4cxx::helpers::Transcoder::decode(argv[0], name);
00291             log4cxx::LoggerPtr root = log4cxx::Logger::getRootLogger();
00292             log4cxx::AppenderPtr app = root->getAppender(name);
00293             log4cxx::AppenderSkeleton* skel = dynamic_cast<log4cxx::AppenderSkeleton *>(app.operator->());
00294 
00295             if (!skel)
00296             {
00297                rv = "no such appender: " + argv[0] + "\r\n";
00298                return true;
00299             }
00300 
00301             if (write)
00302             {
00303                skel->setThreshold(new_level);
00304             }
00305 
00306             oss << name << " appender level " << action << " [" << get_appender_level(skel) << "]\r\n";
00307          }
00308       }
00309 
00310       rv = oss.str();
00311 
00312       return true;
00313    }
00314 
00315    bool log_pattern(std::string& rv, const std::string& args)
00316    {
00317       if (!args.empty())
00318       {
00319          set_conversion_pattern(args);
00320       }
00321 
00322       rv = "conversion pattern set to [" + get_conversion_pattern() + "]\r\n";
00323 
00324       return true;
00325    }
00326 
00327    void set_conversion_pattern(const std::string& pattern)
00328    {
00329       log4cxx::LogString ls_pattern;
00330       log4cxx::helpers::Transcoder::decode(pattern, ls_pattern);
00331       m_layout->setConversionPattern(ls_pattern);
00332    }
00333 
00334    std::string get_conversion_pattern() const
00335    {
00336       std::string pattern;
00337       log4cxx::helpers::Transcoder::encode(m_layout->getConversionPattern(), pattern);
00338       return pattern;
00339    }
00340 
00341    log4cxx::helpers::ObjectPtrT<impl> m_app;
00342    log4cxx::helpers::ObjectPtrT<log4cxx::PatternLayout> m_layout;
00343 };
00344 
00345 //---------------------------------------------------------------------
00346 
00347 log4cxx_appender_factory::log4cxx_appender_factory(const std::string& level)
00348    : m_default_level(log4cxx::Level::toLevel(level, log4cxx::Level::getWarn()))
00349 {
00350 }
00351 
00352 appender_ptr log4cxx_appender_factory::create(stream_writer_ptr sw)
00353 {
00354    return boost::shared_ptr<appender_iface>(new log4cxx_appender(sw, m_default_level));
00355 }
00356 
00357 //---------------------------------------------------------------------
00358 
00359 null_appender_factory::null_appender_factory()
00360 {
00361 }
00362 
00363 appender_ptr null_appender_factory::create(stream_writer_ptr)
00364 {
00365    return boost::shared_ptr<appender_iface>(new null_appender);
00366 }