首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++ syslog包装器

C++ syslog包装器
EN

Code Review用户
提问于 2018-01-08 11:38:43
回答 1查看 9.4K关注 0票数 6

我想用日志代码实现一些事情。

  • 代码是用于嵌入式设备的。代码必须只适用于Linux。可以使用C++11。
  • Syslog守护进程正在设备上运行。因此,我想包装syAdd.1-.h,让syslog来处理实际的日志记录。
  • 在编译时得到更好的检查。我为这件事加了个泡泡。日志记录代码可能永远不会崩溃,并且不应该在运行时抛出异常。
  • 代码被分成模块。我希望为每个模块命名记录器,这样我就可以为不同的模块设置不同的严重性,并在运行时更改它们。
  • 在配置文件中定义记录器的严重性。记录器应该在代码中定义,因此不能使用配置文件创建新的记录器。我为此添加了一些init内容,但这非常简单,需要更多的工作。
  • 这样做的目的是通过使用枚举值作为索引来获得O(1)中记录器的严重性,所以不要在数组或向量上循环等等。
  • 当严重性不确定时,不应进行字符串格式设置。这就是为什么我在日志记录语句周围放置一个宏的原因。例如: log(complexObject.to_string()) --如果在日志函数中检查严重程度,就会将cpu浪费在to_string()上,然后对其不做任何操作。
  • 我真正不喜欢的一件事是使用MAX_INDEX_Logger。不使用它需要大量的宏,坦白地说,我不明白。但我可以试试..。
  • 另一件我不喜欢的事情是宏ADD_LOGGER的使用,但它是目前所必需的,因为早晚我会把记录器放在一个错误的索引上,并且数据结构与枚举不匹配。
  • 这是C++代码,但我没有看到创建记录器对象或类似的任何好处?我玩这个,但它使一切变得更加复杂,没有真正的好处。但我可能错了。

请轻点,这是代码XD的第一稿。

Slog.hpp

代码语言:javascript
复制
#ifndef SLOG_ENUMS_HPP
#define SLOG_ENUMS_HPP

#include 
#include 

namespace slog
{
  // Make enums to guarantee typesafety.

  /*
   * Enum to define the available loggers.
   * IF YOU ADD A NEW LOGGER ALSO ADD IT TO THE initLoggerConversionStructures
   * FUNCTION WITH THE ADD_LOGGER MACRO IN SLOG.CPP FILE!!
   */
  enum Logger
  {
    UNKNOWN,
    TEST,
    MAIN,
    LOG,
    MODULE1,
    MODULE2,
    MODULE3,
    MODULE4,
    MODULE5,
    MODULE6,
    MODULE7,
    MODULE8,
    MAX_INDEX_Logger //Add new entries before this one
  };

  // Severity levels are mapped to those in syslog.h
  enum Severity
  {
    EMERG = LOG_EMERG,
    ALERT = LOG_ALERT,
    CRIT = LOG_CRIT,
    ERR = LOG_ERR,
    WARNING = LOG_WARNING,
    NOTICE = LOG_NOTICE,
    INFO = LOG_INFO,
    DEBUG = LOG_DEBUG
  };

  // Facilities are mapped to those in syslog.h
  enum Facility
  {
    KERN = LOG_KERN,
    USER = LOG_USER,
    MAIL = LOG_MAIL,
    DAEMON = LOG_DAEMON,
    AUTH = LOG_AUTH,
    CRON = LOG_CRON,
    AUTHPRIV = LOG_AUTHPRIV,
    LOCAL0 = LOG_LOCAL0,
    LOCAL1 = LOG_LOCAL1,
    LOCAL2 = LOG_LOCAL2,
    LOCAL3 = LOG_LOCAL3,
    LOCAL4 = LOG_LOCAL4,
    LOCAL5 = LOG_LOCAL5,
    LOCAL6 = LOG_LOCAL6,
    LOCAL7 = LOG_LOCAL7,
  };

  /*
   * Init logging framework.
   * You need to call this in the main thread before anything else to configure
   * the logger conversion structures and initialze the severity for each logger.
   */
  void init(const std::string& configFile, Facility facility = Facility::LOCAL0);

  /*
   * Call to openlog from syslog.h with some default arguments.
   * This function can be used to change the facility.
   */
  void openSyslog(Facility facility = Facility::LOCAL0);

  /*
   * Translate string to logger enum.
   * This function is case sensitive.
   * Returns UNKNOWN by default.
   */
  Logger strToLogger(const std::string& logger);

  // Translate logger enum to string.
  std::string loggerToStr(Logger logger);

  /*
   * Translate string to severity enum.
   * This function is case sensitive.
   * Returns EMERG by default.
   */
  Severity strToSeverity(const std::string& severityStr);

  // Translate severity enum to string.
  std::string severityToStr(Severity severity);

  // Return severity for specified logger.
  Severity getSeverity(Logger logger);

  // Set severity for specified logger.
  void setSeverity(Logger logger, Severity severity);

  // Return if the logger should log for given severity.
  bool isLoggerEnabled(Logger logger, Severity severity);

  // Print loggers for debugging purpose.
  void printLoggers();

  /*
   * Generic logging function. Should not be used directly.
   * Is called through Macros.
   */
  template
  void __log(const char* file, const char* func, uint32_t line, Logger logger, Severity severity, const char* fmt, Args... args)
  {
      std::string msg = loggerToStr(logger).append(": [%s:%s:%i] ").append(fmt);
      ::syslog(severity, msg.c_str(), file, func, line, args...);
  };
} // namespace slog

/*
 * Determine the filename without full path.
 * Because it is constexpr with __FILE__ this is determined at compile time.
 */
static constexpr const char* past_last_slash(const char* str, const char* last_slash)
{
    return
        *str == '\0' ? last_slash :
        *str == '/'  ? past_last_slash(str + 1, str + 1) :
                       past_last_slash(str + 1, last_slash);
}

static constexpr const char* past_last_slash(const char* str)
{
    return past_last_slash(str, str);
}

/*
 * Put it in a nice macro that creates a temp function. this forces the compiler to optimize the
 * funtion away else you might get a function returning a constant.
 */
#define __SHORT_FILE__ ({constexpr const char* sf__ {past_last_slash(__FILE__)}; sf__;})

//Macro to add filename, function and linenumber.
#define SLOG(logger, severity, fmt, ...) \
  slog::__log(__SHORT_FILE__, __FUNCTION__, __LINE__, logger, severity, fmt, ##__VA_ARGS__)

/*
 * These macro's add an if statement.
 * This is very important for conditional formatting.
 * Don't waste cpu cycles on string formatting if the logging level is not correct.
 */
#define SLOG_EMERG(logger, fmt, ...) \
    if(slog::isLoggerEnabled(logger, slog::Severity::EMERG)) \
      SLOG(logger, slog::Severity::EMERG, fmt, ##__VA_ARGS__)

#define SLOG_ALERT(logger, fmt, ...) \
    if(slog::isLoggerEnabled(logger, slog::Severity::ALERT)) \
      SLOG(logger, slog::Severity::ALERT, fmt, ##__VA_ARGS__)

#define SLOG_CRIT(logger, fmt, ...) \
    if(slog::isLoggerEnabled(logger, slog::Severity::CRIT)) \
      SLOG(logger, slog::Severity::CRIT, fmt, ##__VA_ARGS__)

#define SLOG_ERR(logger, fmt, ...) \
    if(slog::isLoggerEnabled(logger, slog::Severity::ERR)) \
      SLOG(logger, slog::Severity::ERR, fmt, ##__VA_ARGS__)

#define SLOG_WARNING(logger, fmt, ...) \
    if(slog::isLoggerEnabled(logger, slog::Severity::WARNING)) \
      SLOG(logger, slog::Severity::WARNING, fmt, ##__VA_ARGS__)

#define SLOG_NOTICE(logger, fmt, ...) \
    if(slog::isLoggerEnabled(logger, slog::Severity::NOTICE)) \
      SLOG(logger, slog::Severity::NOTICE, fmt, ##__VA_ARGS__)

#define SLOG_INFO(logger, fmt, ...) \
    if(slog::isLoggerEnabled(logger, slog::Severity::INFO)) \
      SLOG(logger, slog::Severity::INFO, fmt, ##__VA_ARGS__)

#define SLOG_DEBUG(logger, fmt, ...) \
    if(slog::isLoggerEnabled(logger, slog::Severity::DEBUG)) \
      SLOG(logger, slog::Severity::DEBUG, fmt, ##__VA_ARGS__)

#endif // SLOG_ENUMS_HPP

Slog.cpp

代码语言:javascript
复制
#include 
#include 
#include 
#include 
#include 
#include 

#include "Slog.hpp"

/*
 * Array to translate from severity enum to string. Indexed on severity enum.
 * Severity levels don't change so this is declared const.
 */
static const std::array severityToStrArray_ =
{
  "EMERG",
  "ALERT",
  "CRIT",
  "ERR",
  "WARNING",
  "NOTICE",
  "INFO",
  "DEBUG"
};


/*
 * Map to translate from string to severity enum. Indexed on string.
 * Severity levels don't change so this is declared const.
 */
static const std::unordered_map strToSeverityMap_ =
{
  {"EMERG", slog::Severity::EMERG},
  {"ALERT", slog::Severity::ALERT},
  {"CRIT", slog::Severity::CRIT},
  {"ERR", slog::Severity::ERR},
  {"WARNING", slog::Severity::WARNING},
  {"NOTICE", slog::Severity::NOTICE},
  {"INFO", slog::Severity::INFO},
  {"DEBUG", slog::Severity::DEBUG},
};


/*
 * Array to translate from logger enum to string. Indexed on logger enum.
 * This array is filled by the ADD_LOGGER Macro in the initLoggerConversionStructures()
 * function, to avoid mismatches between enum and it's string representation.
 */
static std::array loggertoStrArray_;


/*
 * Map to translate from string to logger enum. Indexed on string.
 * This map is filled by the ADD_LOGGER Macro in the initLoggerConversionStructures()
 * function, to avoid mismatches between enum and it's string representation.
 */
static std::unordered_map strToLoggerMap_;


/*
 * Macro to help fill the logger enum conversion structures.
 * Avoid mismatches between enum and it's string representation.
 */
#define ADD_LOGGER(x) \
  loggertoStrArray_[slog::Logger::x] = #x; \
  strToLoggerMap_[#x] = slog::Logger::x;


/*
 * IF YOU DEFINE A NEW LOGGER. YOU MUST ADD THE LOGGER TO THIS FUNCTION!
 * Function to fill the logger enum conversion structures.
 * Must be called as part of the init process.
 */
static void initLoggerConversionStructures()
{
  ADD_LOGGER(UNKNOWN);
  ADD_LOGGER(TEST);
  ADD_LOGGER(MAIN);
  ADD_LOGGER(MODULE1);
  ADD_LOGGER(MODULE2);
  ADD_LOGGER(MODULE3);
  ADD_LOGGER(MODULE4);
  ADD_LOGGER(MODULE5);
  ADD_LOGGER(MODULE6);
  ADD_LOGGER(MODULE7);
  ADD_LOGGER(MODULE8);
  ADD_LOGGER(LOG);
}


/*
 * Array to map a severity to a logger. Indexed on logger enum.
 */
static std::array loggerSeverityArray_;


void slog::init(const std::string& configFile, slog::Facility facility)
{
  initLoggerConversionStructures();
  openSyslog(facility);

  setSeverity(Logger::LOG, Severity::INFO);

  std::ifstream file{configFile};
  if(file.good())
  {
    SLOG_INFO(Logger::LOG, "Loading config file: %s", configFile.c_str());

    std::string line;
    while(getline(file, line))
    {
      line.erase(std::remove_if(line.begin(), line.end(), [](char x){return std::isspace(x);}), line.end());
      std::size_t i = line.find(":");
      if(i != std::string::npos)
      {
        std::string logger = line.substr(0,i);
        Logger l = slog::strToLogger(logger);
        if(l != Logger::UNKNOWN)
        {
          std::string severity = line.substr(i+1, std::string::npos);
          Severity s = strToSeverity(severity);
          loggerSeverityArray_[l] = s;
        }
      }
      else
      {
        SLOG_ERR(Logger::LOG, "Invalid line in config file: %s", line.c_str());
      }
    }
  }
  else
  {
    SLOG_EMERG(Logger::LOG, "Could not open config file: %s", configFile.c_str());
  }

  if(isLoggerEnabled(Logger::LOG, Severity::DEBUG))
  {
    for(int lInt=0; lInt(lInt);
      SLOG(Logger::LOG, Severity::DEBUG,
        "Logger %s : %s", loggerToStr(l).c_str(), severityToStr(getSeverity(l)).c_str());
    }
  }
}


void slog::openSyslog(slog::Facility facility)
{
  ::openlog(nullptr, LOG_CONS | LOG_NDELAY | LOG_PID, facility);
}


slog::Severity slog::getSeverity(slog::Logger logger)
{
  if (logger != Logger::MAX_INDEX_Logger)
  {
    return loggerSeverityArray_[logger];
  }
  else
  {
    return Severity::EMERG;
  }
}


void slog::setSeverity(slog::Logger logger, slog::Severity severity)
{
  if (logger != Logger::MAX_INDEX_Logger)
  {
    loggerSeverityArray_[logger] = severity;
  }
}


bool slog::isLoggerEnabled(slog::Logger logger, slog::Severity severity){
  if (logger != Logger::MAX_INDEX_Logger)
  {
    return (loggerSeverityArray_[logger] >= severity);
  }
  else
  {
    return false;
  }
}


slog::Severity slog::strToSeverity(const std::string& severity)
{
  try
  {
    return strToSeverityMap_.at(severity);
  }
  catch(std::out_of_range e)
  {
    SLOG_ERR(Logger::LOG, "Severity %s does not exist", severity.c_str());
    return Severity::EMERG;
  }
}


std::string slog::severityToStr(slog::Severity severity)
{
  return severityToStrArray_[severity];
}


slog::Logger slog::strToLogger(const std::string& logger)
{
  try
  {
    return strToLoggerMap_.at(logger);
  }
  catch(std::out_of_range e)
  {
    SLOG_ERR(Logger::LOG, "Logger %s does not exist", logger.c_str());
    return Logger::UNKNOWN;
  }
}


std::string slog::loggerToStr(slog::Logger logger)
{
  if (logger != Logger::MAX_INDEX_Logger)
  {
    return loggertoStrArray_[logger];
  }
  else
  {
    return "UNKNOWN";
  }
}

void slog::printLoggers()
{
    for(int lInt=0; lInt(lInt);
      printf("Logger %s: %s", loggerToStr(l).c_str(), severityToStr(getSeverity(l)).c_str());
    }

}

关于

递归

的几点注记

Constexpr是在编译时计算的。

代码语言:javascript
复制
static constexpr const char* past_last_slash(const char* str, const char* last_slash)
{
    return
        *str == '\0' ? last_slash :
        *str == '/'  ? past_last_slash(str + 1, str + 1) :
                       past_last_slash(str + 1, last_slash);
}

static constexpr const char* past_last_slash(const char* str)
{
    return past_last_slash(str, str);
}

#define __SHORT_FILE__ ({constexpr const char* sf__ {past_last_slash(__FILE__)}; sf__;})

main()
{
    printf("%s", __SHORT_FILE__);
}

这给出了下面的汇编程序

代码语言:javascript
复制
.LC0:
  .string "/tmp/compiler-explorer-compiler118024-63-codig2.fpj1v/example.cpp"
.LC1:
  .string "%s"
main:
  push rbp
  mov rbp, rsp
  sub rsp, 16
  mov QWORD PTR [rbp-8], OFFSET FLAT:.LC0+54
  mov eax, OFFSET FLAT:.LC0+54
  mov rsi, rax
  mov edi, OFFSET FLAT:.LC1
  mov eax, 0
  call printf
  mov eax, 0
  leave
  ret
__static_initialization_and_destruction_0(int, int):
  push rbp
  mov rbp, rsp
  sub rsp, 16
  mov DWORD PTR [rbp-4], edi
  mov DWORD PTR [rbp-8], esi
  cmp DWORD PTR [rbp-4], 1
  jne .L5
  cmp DWORD PTR [rbp-8], 65535
  jne .L5
  mov edi, OFFSET FLAT:std::__ioinit
  call std::ios_base::Init::Init()
  mov edx, OFFSET FLAT:__dso_handle
  mov esi, OFFSET FLAT:std::__ioinit
  mov edi, OFFSET FLAT:std::ios_base::Init::~Init()
  call __cxa_atexit
.L5:
  nop
  leave
  ret
_GLOBAL__sub_I_main:
  push rbp
  mov rbp, rsp
  mov esi, 65535
  mov edi, 1
  call __static_initialization_and_destruction_0(int, int)
  pop rbp
  ret

所以在运行时,在设备上没有递归。

不能将std::string可以用吗?std::string在一个constexpr一起使用;请参阅C33

EN

回答 1

Code Review用户

发布于 2018-01-08 19:26:43

嵌入式环境中的Recursion 这个函数static constexpr const char* past_last_slash(const char* str, const char* last_slash)是一个递归实现。递归是一种内存占优势,在嵌入式系统中,您可能非常局限于所拥有的内存。为什么不使用std::string而不是使用C类型的字符串呢?C++字符串类型std::发现_最后一位成员具有至少执行该函数一半的C1

在C++中使用Macros似乎更适合编写C而不是C++。虽然宏支持向后兼容C编程语言,但由于宏类型不安全,因此强烈反对使用宏。除了使用宏之外,还有许多替代方法。对于定义常量,有constconstexpr,对于处理许多不同类型的函数,是templates。在C++中,没有很好的理由使用宏。

<#>Missing头文件标准库函数std::isspace(int ch)需要头文件

代码语言:javascript
复制
#include 

line.erase(std::remove_if(line.begin(), line.end(), [](char x) {return std::isspace(x); }), line.end());

Enum转换还不清楚为什么要创建Facility枚举和Severity枚举。SyAdd.1-.h中的实现非常清楚。

如果您必须从syAdd.1-.h中的宏进行转换,那么您可以考虑使用性病::地图,这是另一种C++容器类型。map容器类可用于将整数映射到字符串,将整数映射到其他整数,将整数映射到函数。它还可以相互映射其他事物。

已经设计了syAdd.1-.h中的值,以便可以使用单个字节或单词来测试它们是否启用。这是一种节省空间的特性,在进行嵌入式编程时需要这样做。

代码语言:javascript
复制
static unsigned char enabledMask = 0;

static void slog::setEnable(int severity)
{
    enabledMask |= (0x01 << severity);
}

static void slog::disable(int severity)
{
    unsigned char disableMask = ~(0x01 << severity);
    enabledMask &= disableMask;
}

static bool slog::isEnabled(int severity)
{
    return (enabledMask & (0x01 << severity)) != 0;
}

static const std::map mapseverityToStr =
{
    { LOG_EMERG, "EMERG" },
    { LOG_ALERT,        "ALERT" },
    { LOG_CRIT, "CRIT" },
    { LOG_ERR, "ERR" },
    { LOG_WARNING, "WARNING" },
    { LOG_NOTICE, "NOTICE" },
    { LOG_INFO, "INFO" },
    { LOG_DEBUG, "DEBUG" }
};

static void slog::printSeverity(int severity)
{
    std::string severityStr;

    auto selectString = mapseverityToStr.find(severity);
    if (selectString != mapseverityToStr.end())
    {
        severityStr = selectString->second;
        printf("%s", severityStr.c_str());
    }
    else
    {
        printf("UNKNOW")
    }

}

使用类可能比创建命名空间更简单。实现的名称空间基本上是一个类。该类将为其所有成员提供一个命名空间。类中定义的变量不需要声明为静态变量,因为它们在类中。

代码语言:javascript
复制
class sLogWrapper
{
private:
    unsigned char enabledMask = 0;
    std::map mapseverityToStr;

public:
    sLogWrapper(std::string fileName);
    ~sLogWrapper();
    void printSeverity(int severity);
    void setEnable(int severity);
    void disable(int severity);
    bool isEnabled(int severity);

}

sLogWrapper slog(fileName);

if (slog.isEnabled(severity))
{
    ...
}

不使用类的最佳原因是在嵌入式系统中编程时缺少内存资源,软件工程师负责跟踪内存使用情况,在使用类时可能很困难。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/184575

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档