Program Listing for File client.hpp

Return to documentation for file (rcppsw/er/plugin/log4cxx/client.hpp)

#pragma once

/*******************************************************************************
 * Includes
 ******************************************************************************/
#include <typeindex>

#include "rcppsw/er/er.hpp"
#include "rcppsw/er/plugin/plugin.hpp"
#include "rcppsw/er/macros.hpp"
#include "rcppsw/abi/abi.hpp"

/*******************************************************************************
 * Macros
 ******************************************************************************/
#if (RCPPSW_ERL == RCPPSW_ERL_NONE)

#define ER_CLIENT_INIT(name) rer::client<typename std::remove_reference<decltype(*this)>::type>()
#define ER_LOGGING_INIT(fname)
#define ER_LOGFILE_SET(logger, fname)
#define ER_NDC_PUSH(s)
#define ER_NDC_POP(...)
#define ER_MDC_ADD(key, value)
#define ER_MDC_RM(key)
#define ER_ENV_VERIFY(...)

#elif (RCPPSW_ERL == RCPPSW_ERL_FATAL)

#define ER_CLIENT_INIT(name) rer::client<typename std::remove_reference<decltype(*this)>::type>()
#define ER_LOGGING_INIT(fname)
#define ER_LOGFILE_SET(logger, fname)
#define ER_NDC_PUSH(s)
#define ER_NDC_POP(...)
#define ER_MDC_ADD(key, value)
#define ER_MDC_RM(key)
#define ER_ENV_VERIFY(...)


#elif (RCPPSW_ERL == RCPPSW_ERL_ALL)

#define ER_CLIENT_INIT()                                                \
  rcppsw::er::client<                                                   \
                     typename std::remove_reference_t<decltype(*this)>  \
                     >                                                  \
  (rcppsw::abi::demangle(std::type_index(typeid(*this)).name()))

#define ER_LOGGING_INIT(fname) \
  rcppsw::er::client<          \
      typename std::remove_reference_t<decltype(*this)>>::logging_init(fname)

#define ER_LOGFILE_SET(logger, fname)                                             \
  rcppsw::er::client<                                                             \
      typename std::remove_reference_t<decltype(*this)>>::logfile_set(logger, \
                                                                          fname)

#define ER_NDC_PUSH(s) \
  rcppsw::er::client<  \
      typename std::remove_reference_t<decltype(*this)>>::ndc_push(s)

#define ER_NDC_POP(...) \
  rcppsw::er::client<   \
      typename std::remove_reference_t<decltype(*this)>>::ndc_pop()

#define ER_MDC_ADD(key, value)                  \
  rer::client<  \
                typename std::remove_reference_t<decltype(*this)>>::mdc_add(key, value)

#define ER_MDC_RM(key) \
  rer::client<   \
      typename std::remove_reference_t<decltype(*this)>>::mdc_rm(key)

#define ER_ENV_VERIFY(...)                                              \
  rcppsw::er::client<                                                   \
                           typename std::remove_reference_t<decltype(*this)>>::env_verify()

#endif

/*******************************************************************************
 * Namespaces/Decls
 ******************************************************************************/
namespace rcppsw::er {

/*******************************************************************************
 * Class Definitions
 ******************************************************************************/
#if (RCPPSW_ERL == RCPPSW_ERL_ALL)
template <typename T>
class client {
 public:
  static void logging_init(const std::string& fpath) {
    /*
     * Multiple initializations will cause duplicate messages to show up in
     * logfiles.
     */
    if (!m_initialized) {
      log4cxx::xml::DOMConfigurator::configure(fpath);
      m_initialized = true;
    }
  }

  static void logfile_set(const log4cxx::LoggerPtr& logger,
                          const std::string& name) {
    for (auto& a : logger->getAllAppenders()) {
      if (a->getName() == name) {
        return;
      }
    } /* for(&a..) */
#if defined(RCPPSW_ER_OLD_LOG4CXX)
    log4cxx::LayoutPtr layout = new log4cxx::PatternLayout(kFileLayout);
        log4cxx::AppenderPtr appender =
            new log4cxx::FileAppender(layout, name, false);
        appender->setName(name);
        logger->addAppender(appender);
#else
    auto layout = std::make_shared<log4cxx::PatternLayout>(kFileLayout);
    auto appender = std::make_shared<log4cxx::FileAppender>(layout,
                                                            name,
                                                            false);
    appender->setName(name);
    logger->addAppender(appender);
#endif
  }

  static void ndc_push(const std::string& s) { log4cxx::NDC::push(s); }

  static void ndc_pop(void) { log4cxx::NDC::pop(); }

  static void mdc_add(const std::string& key,
                       const std::string& value) {
    log4cxx::MDC::put(key, value);
  }

  static void mdc_rm(const std::string& key) {
    log4cxx::MDC::remove(key);
  }

  explicit client(const char* abiname)
      : m_logger(log4cxx::Logger::getLogger(abi_name_to_logger_name(abiname))) {
    /*
     * DON'T add the appender here--results in multiple copies of some messages
     * for reasons I can't understand. Doing it in the config file works though.
     */
  }

  virtual ~client(void) = default;

  client(const client&) = default;
  client& operator=(const client&) = default;

  void logfile_set(const std::string& name) {
#if defined(RCPPSW_ER_OLD_LOG4CXX)
    log4cxx::LayoutPtr layout = new log4cxx::PatternLayout(kFileLayout);
    log4cxx::AppenderPtr appender = new log4cxx::FileAppender(layout, name);
#else
    auto layout = std::make_shared<log4cxx::PatternLayout>(kFileLayout);
    auto appender = std::make_shared<log4cxx::FileAppender>(layout, name);
#endif
    logger()->addAppender(appender);
  }

  std::string logger_name(void) const {
    std::string name;
    m_logger->getName(name);
    return name;
  }

  log4cxx::LoggerPtr logger(void) const { return m_logger; }

  void env_verify(void) {
    if (const char* env_p = std::getenv("LOG4CXX_CONFIGURATION")) {
      ER_LOGGING_INIT(std::string(env_p));
    } else {
      std::cerr << "LOG4CXX_CONFIGURATION not defined" << std::endl;
      std::exit(EXIT_FAILURE);
    }
  }


 private:
  static std::string abi_name_to_logger_name(const char* name) {
    std::string tmp = name;
    auto index1 = tmp.find("::");

    /* replace all occurences of '::' with '.' */
    while (index1 != tmp.npos) {
      tmp = tmp.replace(index1, 2, ".");
      index1 = tmp.find("::");
    } /* while() */

    /* remove all <..> in the typeid */
    index1 = tmp.find("<");
    while (index1 != tmp.npos) {
      auto index2 = tmp.find(">");
      tmp = tmp.erase(index1, index2 - index1 + 1);
      index1 = tmp.find("<");
    } /* while() */
    return tmp;
  }
  /* clang-format off */
  static inline const std::string kConsoleLayout = "%X{time} %x [%-5p] %c - %m%n";
  static inline const std::string kFileLayout = "%X{time} %x [%-5p] %c %l - %m%n";

  static inline bool       m_initialized{false};

  log4cxx::LoggerPtr       m_logger{};
  /* clang-format on */
};

#else
template<typename T>
class client {
 public:
  client(void) = default;

  /* So that client objects can be constructed outside of class contexts */
  explicit client(const std::string& ) {}
  virtual ~client(void) = default;
};
#endif

} /* namespace rcppsw::er */