我想用日志代码实现一些事情。
请轻点,这是代码XD的第一稿。
#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#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是在编译时计算的。
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__);
}这给出了下面的汇编程序
.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
发布于 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编程语言,但由于宏类型不安全,因此强烈反对使用宏。除了使用宏之外,还有许多替代方法。对于定义常量,有const和constexpr,对于处理许多不同类型的函数,是templates。在C++中,没有很好的理由使用宏。
<#>Missing头文件标准库函数std::isspace(int ch)需要头文件
#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中的值,以便可以使用单个字节或单词来测试它们是否启用。这是一种节省空间的特性,在进行嵌入式编程时需要这样做。
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")
}
}类使用类可能比创建命名空间更简单。实现的名称空间基本上是一个类。该类将为其所有成员提供一个命名空间。类中定义的变量不需要声明为静态变量,因为它们在类中。
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))
{
...
}不使用类的最佳原因是在嵌入式系统中编程时缺少内存资源,软件工程师负责跟踪内存使用情况,在使用类时可能很困难。
https://codereview.stackexchange.com/questions/184575
复制相似问题