将异常与日志格式分离

5 c++ exception

在我的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); }
Run Code Online (Sandbox Code Playgroud)

所以在我的日志记录类中,我的log函数接受一个std::exception参数.然后,它typeid用于将参数与各种异常类进行比较,转换为适当的类型,然后调用专门用于该类型异常的日志记录函数.这基本上与另一篇文章中描述的技术相同.

我使用typeid而不是dynamic_cast因为dynamic_cast可以成功进行任何有效的向下转换,并且出于代码维护的目的,我真的不希望函数中的if语句顺序很log重要.

这是一个体面的设计吗?我这样使用感觉不对typeid,但我认为我有充分的理由去做.我没有看到很多异常处理"在野外",因为我们主要使用C,所以我没有看到太多的方法来解决这个问题.还有其他方法可以将异常与日志格式分离,我应该注意这些吗?

编辑:我决定实施的内容
我采用了访问者模式的建议,但是根据我的情况进行了调整.我想抓住std::exception,因为这些可以和我自己一样抛出,但是根据异常类型格式化日志消息.

我的每个异常类都派生自我的基Exception类并实现虚函数accept.我创建了一个ExceptionLogger类,它实现了一个ExceptionVisitor提供visit函数的接口.

所述LogFile类具有的一个实例ExceptionLogger,以及其的过载log接受一个函数std::exception参数.在log函数中,我尝试了一个dynamic_cast基本类型,Exception.如果成功,我调用accept异常函数,否则ExceptionLogger::visit(std::exception const&)直接调用函数.由于std::exception没有实现我的accept功能,我需要,dynamic_cast所以我可以确定是否可以进行更详细的记录.

我选择这样做而不是一系列if语句检查,typeid因为:

  1. 这是一个命名的设计模式,我可以引用未来的维护者
  2. 如果维护者添加了一个从我的Exception基础派生的新异常,但忘记visit为该异常实现一个新函数,我仍然会得到为基础实现的日志记录Exception- 文件,行号和函数.

    如果我实施了一系列的if声明,我会一直不得不回落到std::exception记录行为,这仅仅是打印出来的结果what,我也可以试过dynamic_castException.

    当然,在这种情况下,我仍然会更喜欢编译器错误.

小智 1

一个更简单的解决方案是在中央格式化方法中重新抛出异常(另请参阅此答案)。然后,您可以在那里捕获每个异常类型并对其进行格式化。

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 << "Unknown\n";
    }
}
Run Code Online (Sandbox Code Playgroud)