当触发异常时,应该如何记录日志
How should one log when an exception is triggered?
在我最近编写的一个程序中,我想记录我的"业务逻辑"代码何时触发第三方或项目api中的异常。(澄清一下,我想记录使用API导致异常的情况。这可能比实际的throw
高很多帧,也可能比实际的catch
低很多帧(在那里可以发生异常负载的日志记录)。)我做了以下操作:
void former_function()
{
/* some code here */
try
{
/* some specific code that I know may throw, and want to log about */
}
catch( ... )
{
log( "an exception occurred when doing something with some other data" );
throw;
}
/* some code here */
}
简而言之,如果发生异常,创建一个捕获所有子句,记录错误,然后重新抛出。在我看来这是安全的。我知道一般来说,catch-all被认为是不好的,因为根本没有对异常的引用来获得任何有用的信息。然而,我只是要重新抛出它,所以没有任何损失。
现在,它本身是好的,但是一些其他程序员修改了这个程序,最终违反了上面的规定。具体来说,他们在一种情况下将大量代码放入try块中,而在另一种情况下删除了'throw'并放置了'return'。
我现在看到我的解决方案是脆弱的;它不能防止未来的修改。
我想要一个没有这些问题的更好的解决方案。
我有另一个可能的解决方案,没有上述问题,但我想知道别人怎么看。它使用RAII,特别是一个"作用域退出"对象,如果std::uncaught_exception
在构造时不为真,而在销毁时为为真,则隐式触发:
#include <ciso646> // not, and
#include <exception> // uncaught_exception
class ExceptionTriggeredLog
{
private:
std::string const m_log_message;
bool const m_was_uncaught_exception;
public:
ExceptionTriggeredLog( std::string const& r_log_message )
: m_log_message( r_log_message ),
m_was_uncaught_exception( std::uncaught_exception() )
{
}
~ExceptionTriggeredLog()
{
if( not m_was_uncaught_exception
and std::uncaught_exception() )
{
try
{
log( m_log_message );
}
catch( ... )
{
// no exceptions can leave an destructor.
// especially when std::uncaught_exception is true.
}
}
}
};
void potential_function()
{
/* some code here */
{
ExceptionTriggeredLog exception_triggered_log( "an exception occurred when doing something with some other data" );
/* some specific code that I know may throw, and want to log about */
}
/* some code here */
}
我想知道:
- 从技术上讲,这是否有效?最初它似乎工作,但我知道有一些关于使用
std::uncaught_exception
的警告。 - 是否有其他方法来完成我想要的?
注释:我已经更新了这个问题。具体地说,我:
- 在
log
函数调用周围添加了最初缺失的try
/catch
。 - 增加了施工时
std::uncaught_exception
状态的跟踪。这可以防止在另一个析构函数的'try'块中创建该对象,该析构函数作为异常堆栈展开的一部分触发。 - 修复了新的'potential_function'来创建一个命名的对象,而不是像以前那样创建一个临时的对象。
我对你的方法没有评论,但它似乎很有趣!我有另一种方法,可能也适用于你想要的,而且可能更通用。但是,它需要c++ 11中的lambdas,这在您的情况下可能是问题,也可能不是问题。
这是一个简单的函数模板,它接受一个lambda,运行它并捕获、记录和重新抛出所有异常:
template <typename F>
void try_and_log (char const * log_message, F code_block)
{
try {
code_block ();
} catch (...) {
log (log_message);
throw;
}
}
最简单的用法是:
try_and_log ("An exception was thrown here...", [&] {
this_is_the_code ();
you_want_executed ();
and_its_exceptions_logged ();
});
正如我之前所说,我不知道它如何与你自己的解决方案相比较。注意,lambda从其封闭作用域捕获所有内容,这非常方便。另外请注意,我还没有真正尝试过,所以编译错误,逻辑问题和/或核战争可能会由此导致。
我在这里看到的问题是,它不容易包装成一个宏,并期望你的同事正确地编写[=] {
和}
部分,所有的时间可能太多了!
出于换行和防白痴的目的,您可能需要两个宏:TRY_AND_LOG_BEGIN
用于发出第一行,直到lambda的开始大括号,TRY_AND_LOG_END
用于发出结束大括号和圆括号。像这样:
#define TRY_AND_LOG_BEGIN(message) try_and_log (message, [&] {
#define TRY_AND_LOG_END() })
你可以这样使用它们:
TRY_AND_LOG_BEGIN ("Exception happened!") // No semicolons here!
whatever_code_you_want ();
TRY_AND_LOG_END ();
这是——取决于你的观点——要么是净收益,要么是净损失!(我个人更喜欢直接的函数调用和lambda语法,这给了我更多的控制和透明度。
也可以将日志信息写在代码块的末尾;只需切换try_and_log
函数的两个参数
- 如何实现具有多个平台__FILE__和__LINE__信息的 C/C++ 可变参数日志记录宏?
- C++ 中混合二进制/文本日志记录的最佳做法
- 轻松日志记录++如何避免多个初始化
- 使用字符串流加速 std::cout 日志记录
- 我的游戏引擎的 spdlog 日志记录出现奇怪的"unresolved external symbol"错误
- 从动态加载的库中记录日志
- 使用 #define 进行跟踪日志记录以避免性能问题
- 如何在代码中启用/禁用 spdlog 日志记录?
- 我正在尝试用 c++ 制作一个日志记录框架,但信息没有传递给记录器的子组件,我做错了什么?
- 使用 Python、ROS 和 C++ 进行日志记录
- C++日志记录类实例标识符
- 如何在C++中重载<<线程安全日志记录的运算符?
- 在 C++17 中将无限参数传递给日志记录函数
- 配置 log4cpp 日志记录级别优先级
- C++同时记录到控制台和日志文件
- C++日志异常到文件,不同的输出
- Log4cpp重复记录日志
- 记录boost::异常,同时避免文件/行/函数和唠叨
- 当触发异常时,应该如何记录日志
- C++ 具有单独的可执行日志异常