enum LoggingDestination {
LOG_TO_NONE = 0, // 不记录日志
LOG_TO_FILE = 1 << 0, // 将日志记录到文件
LOG_TO_SYSTEM_DEBUG_LOG = 1 << 1, // 将日志记录到系统调试日志
// 这是一个组合值,表示同时记录到文件和系统调试日志。
LOG_TO_ALL = LOG_TO_FILE | LOG_TO_SYSTEM_DEBUG_LOG,
// 在Windows上(OS_WIN),默认是将日志输出到文件(LOG_DEFAULT = LOG_TO_FILE)。
// 在POSIX兼容的操作系统上(如Linux、Mac等,OS_POSIX),由于可能无法轻松定位到可执行文件的位置,
// 因此默认是将日志通过 OutputDebugString 输出到系统调试日志(LOG_DEFAULT = LOG_TO_SYSTEM_DEBUG_LOG)。
#if defined(OS_WIN)
LOG_DEFAULT = LOG_TO_FILE,
#elif defined(OS_POSIX)
LOG_DEFAULT = LOG_TO_SYSTEM_DEBUG_LOG,
#endif
};
LoggingDestination枚举类型用于指定日志记录的目标位置。
这样的设计允许在编译时通过定义不同的宏来指定日志记录的目标,提高了代码的灵活性和可移植性。同时,使用位掩码来表示不同的日志目标也允许在运行时通过位运算来动态地组合或修改日志目标。
#if defined....#elif....#endif
的方式而非#if defined ...#else...endif
就是为了以后可以更方便的扩展平台。#if defined(OS_WIN)
typedef HANDLE PlatformFile;
#elif defined(OS_POSIX)
typedef int PlatformFile;
#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)
typedef struct stat stat_wrapper_t;
#else
typedef struct stat64 stat_wrapper_t;
#endif
#endif // defined(OS_POSIX)
#undef ERROR
#define ERROR 0
// Needed for LOG_IS_ON(ERROR).
const LogSeverity BLOG_0 = BLOG_ERROR;
#endif
通过使用 Google 的 gFlags库来定义命令行参数, 在程序运行时通过命令行参数控制哪些日志应该执行
gFlags 是一个用于处理命令行标志(flags)和配置参数的库。 譬如:
# define BAIDU_VLOG_IS_ON(verbose_level, filepath) \
({ static const int* vlocal = &::logging::VLOG_UNINITIALIZED; \
const int saved_verbose_level = (verbose_level); \
(saved_verbose_level >= 0)/*VLOG(-1) is forbidden*/ && \
(*vlocal >= saved_verbose_level) && \
((vlocal != &::logging::VLOG_UNINITIALIZED) || \
(::logging::add_vlog_site(&vlocal, filepath, __LINE__, \
saved_verbose_level))); })
#else
// GNU extensions not available, so we do not support --vmodule.
// Dynamic value of FLAGS_verbose always controls the logging level.
# define BAIDU_VLOG_IS_ON(verbose_level, filepath) \
(::logging::FLAGS_v >= (verbose_level))
#endif
#define VLOG_IS_ON(verbose_level) BAIDU_VLOG_IS_ON(verbose_level, __FILE__)
DECLARE_int32(v);
extern const int VLOG_UNINITIALIZED;
DECLARE_int32(v); 这条语句声明了一个名为 v 的整数型(32位)命令行标志。 在运行时决定某个日志调用是否应该执行,这可以帮助优化性能,因为不必要的日志调用可以被避免。 宏允许你在运行时动态地控制日志记录的详细程度,而不必修改和重新编译源代码。
给出一个反面案例:
std::ostringstream os;
os<<"first log message";
os<<"second log message";
cout<<os.str();
std::ostringstream 虽然非常方便地允许我们像使用流(stream)一样拼接字符串,但每次我们向其中添加内时,它都可能会重新分配内存来存储更长的字符串。这种动态内存分配在日志记录场景中可能会成为性能瓶颈,尤其是在高频次或大量日志记录的情况下。 为了避免这种开销,可以使用一个底层缓冲区(underlying buffer)来直接存储日志内容。这个缓冲区通常是一个固定大小或动态增长的字符数组(如 char[] 或 std::vector)通过直接在这个数组上操作来避免 std::string 的内存分配。
使用底层缓冲区也需要更多的注意和谨慎,因为你需要手动管理内存和确保缓冲区不会溢出。此外,当你需要将日志内容转换为 std::string 或其他类型时,你可能需要执行额外的复制操作。
在设计日志系统时,一种常见的策略是使用一个循环缓冲区(circular buffer)或类似的机制来存储日志条目。每个日志条目可以有一个固定的最大大小,并且当缓冲区满时,较旧的日志条目可以被新的条目覆盖。这样可以确保在任意时刻,你都有一定数量的最新日志可供查看,而不会无限制地增长内存使用。
// 这个宏的主要目的是帮助开发者识别和理解哪些代码路径尚未被实现。
// 当设置为在运行时记录错误日志时,它还可以帮助在测试或生产环境中捕获这些问题。
// 这个宏用于标记尚未实现的代码路径。其实现取决于NOTIMPLEMENTED_POLICY的值:
// 0 -- 不执行任何操作(可能会被编译器优化掉)
// 1 -- 在编译时发出警告
// 2 -- 在编译时失败(这通常是通过静态断言或其他编译器特定的技巧实现的)
// 3 -- 在运行时失败(使用`DCHECK`或其他类似的运行时断言)
// 4 -- [default]:在运行时记录一个错误日志(使用`LOG(ERROR)`)
// 5 -- LOG(ERROR) at runtime, 每个调用点只记录一次
#endif // BRPC_WITH_GLOG
#ifndef NOTIMPLEMENTED_POLICY
#if defined(OS_ANDROID) && defined(OFFICIAL_BUILD)
#define NOTIMPLEMENTED_POLICY 0
#else
// Select default policy: LOG(ERROR)
#define NOTIMPLEMENTED_POLICY 4
#endif
#endif
/***********************************
* 设计巧思:
* 这样的设计使得当NOTIMPLEMENTED()宏被触发时,日志或错误信息中能够包含更多关于出错位置的上下文信息,
* 特别是当使用GCC编译时。这对于快速定位和修复问题是非常有帮助的。
* 例如,如果你在函数void MyClass::MyFunction(int a, double b)中触发了NOTIMPLEMENTED()宏,
* 并且你的代码是用GCC编译的,那么你将看到的错误信息可能类似于:
* Not implemented reached in void MyClass::MyFunction(int, double)
* 而如果你不是用GCC编译的,那么错误信息将简单地是:
* NOT IMPLEMENTED
* 这样的设计考虑了跨平台和跨编译器的兼容性,同时尽可能地提供了有用的调试信息。
* ************************************/
#if defined(COMPILER_GCC)
// __PRETTY_FUNCTION__是GCC提供的一个宏,
// 它可以展开为当前函数的签名,包括函数名、返回类型以及参数类型,这使得在调试或记录日志时能够提供更详细的信息。
#define NOTIMPLEMENTED_MSG "Not implemented reached in " << __PRETTY_FUNCTION__
#else
#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED"
#endif
#if NOTIMPLEMENTED_POLICY == 0
#define NOTIMPLEMENTED() BAIDU_EAT_STREAM_PARAMS
#elif NOTIMPLEMENTED_POLICY == 1
// TODO, figure out how to generate a warning
#define NOTIMPLEMENTED() COMPILE_ASSERT(false, NOT_IMPLEMENTED)
#elif NOTIMPLEMENTED_POLICY == 2
#define NOTIMPLEMENTED() COMPILE_ASSERT(false, NOT_IMPLEMENTED)
#elif NOTIMPLEMENTED_POLICY == 3
#define NOTIMPLEMENTED() NOTREACHED()
#elif NOTIMPLEMENTED_POLICY == 4
#define NOTIMPLEMENTED() LOG(ERROR) << NOTIMPLEMENTED_MSG
#elif NOTIMPLEMENTED_POLICY == 5
#define NOTIMPLEMENTED() do { \
static bool logged_once = false; \
LOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG; \
logged_once = true; \
} while(0); \
BAIDU_EAT_STREAM_PARAMS
#endif
#if defined(NDEBUG) && defined(OS_CHROMEOS)
#define NOTREACHED() LOG(ERROR) << "NOTREACHED() hit in " \
<< __FUNCTION__ << ". "
#else
#define NOTREACHED() DCHECK(false)
#endif
这段代码定义了一个名为NOTIMPLEMENTED()的宏,用于标记尚未实现的代码路径。它的实现受NOTIMPLEMENTED_POLICY控制,该策略定义了当NOTIMPLEMENTED()宏被触发时应该执行的操作。
以下是NOTIMPLEMENTED_POLICY各值的含义和相应的NOTIMPLEMENTED()宏的行为:
0: 什么也不做(被编译器优化掉)。 1: 在编译时发出警告。但注意,这里的COMPILE_ASSERT实际上并不在GCC中产生编译时警告,而是编译时错误(因为COMPILE_ASSERT通常用于在编译时检查某个条件是否为真)。 2: 在编译时失败(使用COMPILE_ASSERT)。 3: 在运行时失败(使用NOTREACHED(),这通常是DCHECK失败时的一个宏)。 4: 在运行时记录一个错误日志(使用LOG(ERROR)),这是默认行为。 5: 在运行时记录一个错误日志,但每个调用点只记录一次。 注意,这里还定义了一个NOTIMPLEMENTED_MSG宏,它包含当前函数名的去混淆版本(在GCC编译器下)或一个简单的字符串"NOT IMPLEMENTED"。
另外,BAIDU_EAT_STREAM_PARAMS这个宏在代码中被使用,但它在这段代码中没有定义。我猜测它可能是一个用于处理或忽略任何传递给NOTIMPLEMENTED()宏的额外参数(比如流操作符<<后面的内容)的宏。
在代码的最后,有一个条件编译块,它特定于NDEBUG(非调试模式)和OS_CHROMEOS(Chrome OS操作系统)。在这个条件下,NOTREACHED()宏被定义为一个记录日志的宏,类似于NOTIMPLEMENTED_POLICY为4时的行为,但日志消息略有不同。
但是,需要注意的是,代码中的NOTREACHED()宏在条件编译块的末尾被切断了,这可能导致编译错误。你应该确保这个宏的完整定义包含在你的代码库中。
这段代码提供了一种灵活的方式来处理未实现的代码路径,允许开发者根据他们的需要选择不同的处理策略。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。