应使用 enum class 定义日志级别以避免隐式转换:enum class loglevel { debug = 0, info = 1, warn = 2, error = 3 };使用时需 static_cast 显式转换,确保类型安全,还可重载 operator 支持流输出。

日志级别怎么定义才不踩 enum 隐式转换的坑
直接用 int 或裸 enum 定义级别,容易在比较或传参时被编译器悄悄转成整数,导致逻辑错乱(比如 if (level == 3) 看似合理,实则绕过类型检查)。推荐用 enum class 强制作用域和类型安全:
enum class LogLevel {
DEBUG = 0,
INFO = 1,
WARN = 2,
ERROR = 3
};
使用时必须显式转换:static_cast<int>(level)</int>,虽然多写几个字,但能避免低级误判。如果要支持流输出,可重载 operator,但别为了省事把 <code>enum class 改回裸 enum。
如何让 std::cout 输出带时间戳和级别前缀
每次手动拼接 [2024-06-12 14:23:05][INFO] 太啰嗦,也难统一格式。建议封装一个轻量输出函数,用 std::chrono 获取毫秒级时间,再组合字符串:
void log(LogLevel level, const std::string& msg) {
auto now = std::chrono::system_clock::now();
auto time_t = std::chrono::system_clock::to_time_t(now);
auto ms = std::chrono::duration_cast<:chrono::milliseconds>(
now.time_since_epoch()) % 1000;
std::string level_str;
switch(level) {
case LogLevel::DEBUG: level_str = "DEBUG"; break;
case LogLevel::INFO: level_str = "INFO"; break;
case LogLevel::WARN: level_str = "WARN"; break;
case LogLevel::ERROR: level_str = "ERROR"; break;
}
std::cout
<p>注意两点:一是 <code>std::put_time</code> 要配合 <code>std::localtime</code>(线程不安全,单线程够用;多线程需用 <code>std::gmtime_r</code> + 自己格式化);二是 <code>std::setw</code> 和 <code>std::setfill</code> 必须在同一次输出中生效,否则毫秒可能显示为 <code>5</code> 而非 <code>005</code>。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2823" title="C++14"><img
src="https://img.php.cn/upload/manual/001/431/639/6ac8b33c327c4749.png" alt="C++14" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="overflowclass">C++14</a>
<p class="overflowclass">C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。</p>
</div>
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<h3>怎样控制哪些级别实际输出(比如只打 INFO 及以上)</h3>
<p>不能靠 if 套一堆 <code>if (level >= LogLevel::INFO)</code> 分散在各处——维护成本高且易漏。应在日志入口统一过滤:</p>
<ul>
<li>定义全局变量 <code>LogLevel g_log_level = LogLevel::INFO</code>,启动时从环境变量或配置读取</li>
<li>log 函数开头加判断:<code>if (static_cast<int>(level) (g_log_level)) return;</int></code>
</li>
<li>避免用 <code>switch</code> 或 <code>map</code> 做级别映射——纯整数比较最快,无额外开销</li>
</ul>
<p>如果需要运行时动态调高/调低级别(比如调试时临时开 DEBUG),就别把 <code>g_log_level</code> 声明为 <code>const</code>,也别用宏(<code>#define LOG_LEVEL INFO</code>)硬编码,否则改完得重编译。</p>
<h3>为什么别急着引入 <code>spdlog</code> 或 <code>glog</code>
</h3>
<p>简单命令行工具、教学示例、嵌入式小项目里,第三方日志库反而增加依赖、编译时间、二进制体积,还可能引入你不关心的特性(如异步队列、文件轮转、网络发送)。手写上面几十行代码,已能覆盖:分级、时间戳、运行时开关、线程安全(单 cout 本身是线程安全的,但多线程混打会乱序,真需要顺序得加锁)。</p>
<p>真正该换库的信号只有一个:你开始反复修改自己的 log 函数,或者发现 <code>std::cout</code> 在重定向到文件后性能明显下降(此时再考虑缓冲、异步、文件分片等专业能力)。</p></:chrono::milliseconds>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










