将异常与日志格式解耦
Decoupling exceptions from log formatting
在我的API中,我有一个从std::exception
派生的小异常层次结构。我有一个基类Exception
,它提供了错误代码、文件、行和函数。其他更具体的例外情况源自Exception
。例如,一个派生类添加了一个特定于平台的错误代码,以及一个标识返回错误代码的函数的字段。这就像system_error
的简化版本,但我不能使用C++11功能(我一直在使用VS2005,没有Boost)。
我需要用我的日志记录类来记录这些异常。我希望以某种格式记录异常。在阅读了各种在线论坛和Boost的错误和异常处理指南后,我不认为每个异常的what
函数或Exception
中的任何其他虚拟函数是格式化异常以进行日志记录的合适位置。因此,我的what
函数只返回类的名称。
在捕获异常时,我经常想捕获非常一般的异常,通常是std::exception
,并将其传递给记录器。我不想经常捕捉单个异常,因为我试图防止异常逃离API(我的API的公共部分在C中),并且可能会发生几个异常。我想避免类似以下代码:
try { /* blah */ }
catch {DerivedException const& ex) { logger.log(ex); }
...
catch {Exception const& ex) { logger.log(ex); }
因此,在我的日志类中,我的log
函数接受一个std::exception
参数。然后,它使用typeid
将参数与各种异常类进行比较,转换为适当的类型,然后调用专门用于该类型异常的日志记录函数。这基本上与这篇文章中描述的技术相同。
我使用typeid
而不是dynamic_cast
,因为dynamic_cast
可以成功进行任何有效的下变频,而且出于代码维护的目的,我真的不希望log
函数中if
语句的顺序无关紧要。
这是一个不错的设计吗?我觉得这样使用typeid
是不对的,但我认为我有充分的理由这样做。我没有见过太多"野外"的异常处理,因为我们主要使用C,所以我没有看到太多关于这个主题的方法。是否有其他方法可以将异常与其日志格式脱钩,我应该知道这些方法?
编辑:我决定实施什么
我接受了使用访问者模式的建议,但根据我的情况进行了调整。我想捕获std::exception
,因为它们可以和我自己的一样抛出,但可以根据异常类型格式化日志消息。
我的每个异常类都派生自我的基本Exception
类,并实现虚拟函数accept
。我创建了一个ExceptionLogger
类,它实现了一个提供visit
函数的ExceptionVisitor
接口。
LogFile
类有一个ExceptionLogger
的实例,还有一个采用std::exception
参数的log
函数的重载。在log
函数中,我尝试将dynamic_cast
转换为我的基类型Exception
。如果成功,我调用异常的accept
函数,否则我直接调用ExceptionLogger::visit(std::exception const&)
函数。由于std::exception
没有实现我的accept
功能,我需要dynamic_cast
,这样我就可以确定是否可以进行更详细的日志记录。
我选择这样做,而不是用一系列if
语句检查typeid
,因为:
- 这是一个命名的设计模式,我可以向未来的维护人员推荐
如果维护人员添加了一个从我的
Exception
库派生的新异常,但忘记为该异常实现一个新的visit
函数,我仍然会得到为库Exception
实现的日志记录——一个文件、行号和函数。如果我实现了一系列
if
语句,我将不得不回到std::exception
日志行为,这只是打印出what
的结果,或者我可以尝试从dynamic_cast
到Exception
。当然,在这种情况下,我仍然希望出现编译器错误。
一个更简单的解决方案是在中心格式化方法中重新抛出异常(另请参阅此答案)。然后,您可以在那里捕获每个异常类型并对其进行格式化
class Exception : public std::exception {};
class DerivedException : public Exception {};
void LogThrownException();
void DoSomething()
{
try
{
// Do something, might throw ...
}
catch (...)
{
LogThrownException();
}
}
void LogThrownException()
{
try
{
throw;
}
// Order is important to catch all derived types.
// Luckily the compiler should warn, if a type is hidden.
catch (DerivedException&)
{
std::cout << "DerivedException";
}
catch (Exception&)
{
std::cout << "Exception";
}
catch (std::exception&)
{
std::cout << "std::exception";
}
// ...
catch (...)
{
std::cout << "Unknownn";
}
}
- 如何在openssl-ecc中获取十六进制格式的私钥
- 将"打开的CV图像"中的"颜色"转换为整数格式
- 当算法需要派生类的知识时,将算法与数据解耦
- 将GUI与解析引擎解耦
- 解耦嵌套类:使用模板别名时出现"模板声明冲突"错误
- 在这种情况下如何解耦
- hash_map/unordered_map 的解耦版本
- C++或D:在没有动态调度的情况下解耦类的习惯用法
- 在解耦的设计层之间高效地传递通知
- 从接口(c++)解耦表示的含义
- 如何将客户端类与模板类解耦
- 在C++中,如何最好地解耦两个必须维护彼此引用集合的类
- 如何在c++中将pvr解压缩为原始格式8888
- 将异常与日志格式解耦
- 发布解耦嵌套c++类
- 解耦应用程序和库在CMake项目
- 以有状态策略类为代价解耦宿主类和策略类,并且不遵循Effective c++的第26条
- 如何获得解耦的多态行为
- 将线程逻辑与业务逻辑解耦
- c++回调——如何解耦回调类型