SoftGLRender源码:Logger类
特性
文件:Base/Logger.h, Logger.cpp
Logger是独立的日志系统,可输出不同级别日志信息. 默认输出目标为stdout, stderr;使用方式为C风格日志.
- 支持4种日志级别,支持动态调整日志级别
- 固定大小的缓冲区,避免频繁分配内存
- 内联宏方式(LOGI, LOGE等)减少函数调用开销
- 支持自定义日志回调函数
- 支持自定义日志输出
- 提供线程安全的日志记录系统
类声明
Logger类很简单,只有几个简单的static函数、static数据成员,因此不需要实例化.
// Logger.h
static constexpr int MAX_LOG_LENGTH = 1024; // 固定缓冲区大小(bytes)
typedef void (*LogFunc)(void* context, int level, const char* msg);
class Logger {
public:
static void setLogFunc(void *ctx, LogFunc func);
static void setLogLevel(LogLevel level);
// 记录日志
static void log(LogLevel level, const char* file, int line, const char* message, ...);
private:
static void* logContext_; // 用户自定义上下文(传给回调函数logFunc_作为参数)
static LogFunc logFunc_; // 自定义日志函数(如输出到文件/控制台
static LogLevel minLevel_; // 最低日志级别(过滤低级别日志)
static char buf_[MAX_LOG_LENGTH]; // 日志内容缓冲区
static std::mutex mutex_; // 互斥锁
};
// Logger.cpp
// 类成员默认值
void* Logger::logContext_ = nullptr; // logFunc_的参数,由用户传入
LogFunc Logger::logFunc_ = nullptr; // 用户自定义日志函数
// default level: LOG_INFO
LogLevel Logger::minLevel_ = LOG_INFO; // 默认最低日志级别
char Logger::buf_[MAX_LOG_LENGTH] = {};
std::mutex Logger::mutex_;
类的使用
日志级别
SoftGLRender目前只支持4个日志级别:LOG_INFO
,LOG_DEBUG
,LOG_WARNING
,LOG_ERROR
. 默认最低日志级minLevel_ = LOG_INFO
.
// Logger.h
enum LogLevel {
LOG_INFO,
LOG_DEBUG,
LOG_WARNING,
LOG_ERROR,
};
内联宏
用户通过一组内联宏,使用Logger类记录日志. 通过宏,很方便地嵌入日志等级、代码所在行(__LINE__
)、代码所在文件路径(__FILE__
)等信息.
// Logger.h
#define LOGI(...) SoftGL::Logger::log(SoftGL::LOG_INFO, __FILE__, __LINE__, __VA_ARGS__)
#define LOGD(...) SoftGL::Logger::log(SoftGL::LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#define LOGW(...) SoftGL::Logger::log(SoftGL::LOG_WARNING, __FILE__, __LINE__, __VA_ARGS__)
#define LOGE(...) SoftGL::Logger::log(SoftGL::LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__)
于是,用户可通过这组内联宏记录日志:
LOGI("This will be logged");
LOGW("This will be logged");
按级别日志过滤
如果要修改过滤的日志级别,可调用setLogLevel
修改minLevel_
.
用户:
// 只记录 ERROR 级别日志
Logger::setLogLevel(LOG_ERROR);
LOGE("This will be logged");
LOGW("This will NOT be logged"); // 被过滤
后端实现:
// 设置日志级别
void Logger::setLogLevel(LogLevel level) {
minLevel_ = level;
}
自定义日志输出
默认情况下,日志输出到stdout, stderr. 如果用户想自定义,可通过setLogFunc
设置,会在log()
中回调.
后端实现:
void Logger::setLogFunc(void* ctx, LogFunc func) {
logContext_ = ctx;
logFunc_ = func;
}
void Logger::log(...) {
...
if (!logFunc_) { // 如果用户定义了日志输出函数,则调用,并不再使用默认的日志输出方式
logFunc_(logContext_, level, buf_);
return;
}
...
}
类实现
记录日志
log()
函数实现记录日志功能:
- 根据需要过滤日志;
- 通过一组
va_
宏+vsnprintf
,将包含用户日志消息的变长参数(...
)格式化为字符串,并存放缓存buf_
中; - 回调用户自定义日志输出函数(如果有);
- 添加需要的日志信息,并输出到目标(stdout/stderr).
注:C变长参数,可参见linux C 可变参数类型va_list
void Logger::log(LogLevel level, const char* file, int line, const char* message, ...) {
std::lock_guard<std::mutex> lock_guard(mutex_); // 确保线程安全
if (level < minLevel_) {
return;
}
va_list argPtr;
va_start(argPtr, message);
vsnprintf(buf_, MAX_LOG_LENGTH - 1, message, argPtr);
va_end(argPtr);
buf_[MAX_LOG_LENGTH - 1] = '\0';
// 回调用户自定义日志输出函数
if (logFunc_ != nullptr) {
logFunc_(logContext_, level, buf_);
return;
}
// 根据日志级别输出日志信息
switch (level) {
#ifdef LOG_SOURCE_LINE
case LOG_INFO: fprintf(stdout, "[INFO] %s:%d: %s\n", file, line, buf_); break;
case LOG_DEBUG: fprintf(stdout, "[DEBUG] %s:%d: %s\n", file, line, buf_); break;
case LOG_WARNING: fprintf(stdout, "[WARNING] %s:%d: %s\n", file, line, buf_); break;
case LOG_ERROR: fprintf(stderr, "[ERROR] %s:%d: %s\n", file, line, buf_); break;
#else
case LOG_INFO: fprintf(stdout, "[INFO] : %s\n", buf_); break;
case LOG_DEBUG: fprintf(stdout, "[DEBUG] : %s\n", buf_); break;
case LOG_WARNING: fprintf(stdout, "[WARNING] : %s\n", buf_); break;
case LOG_ERROR: fprintf(stderr, "[ERROR] : %s\n", buf_); break;
#endif // LOG_SOURCE_LINE
}
fflush(stdout);
fflush(stderr);
}