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_INFOLOG_DEBUGLOG_WARNINGLOG_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()函数实现记录日志功能:

  1. 根据需要过滤日志;
  2. 通过一组va_宏+vsnprintf,将包含用户日志消息的变长参数(...)格式化为字符串,并存放缓存buf_中;
  3. 回调用户自定义日志输出函数(如果有);
  4. 添加需要的日志信息,并输出到目标(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);
}
posted @ 2025-05-18 16:53  明明1109  阅读(39)  评论(0)    收藏  举报