将异常与日志格式分离

时间:2014-09-19 20:27:04

标签: c++ exception

在我的API中,我有一个小的异常层次结构,派生自std::exception。我有一个基类Exception,它提供错误代码,文件,行和函数。其他更具体的例外情况来自Exception。例如,一个派生类添加特定于平台的错误代码,以及标识哪个函数返回错误代码的字段。这就像system_error的简化版本,但我不能使用C ++ 11的功能(我不能使用VS2005,也没有使用Boost)。

我需要使用我的日志记录类记录这些异常。我希望以特定格式记录异常。在线阅读各种论坛并阅读Boost's Error and Exception Handling Guidelines后,我不认为每个例外的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将参数与各种异常类进行比较,转换为适当的类型,然后调用专门用于该类型异常的日志记录函数。这基本上与in this other post描述的技术相同。

我使用typeid代替dynamic_cast,因为dynamic_cast可以成功进行任何有效的向下广播,出于代码维护的目的,我真的不想要我{{1}的顺序1}} if函数中的语句很重要。

这是一个体面的设计吗?我这样使用log感觉不对,但我认为我有充分理由这样做。由于我们主要使用C语言,所以我还没有看到很多异常处理和#34;因为我没有看到过多的方法。是否有其他方法可以将异常与日志格式分离,我应该注意哪些?

编辑:我决定实施的内容
我采纳了使用访客模式的建议,但是根据我的情况进行了调整。我想捕获typeid,因为这些可以和我自己一样抛出,但是根据异常类型格式化日志消息。

我的每个异常类都派生自我的基础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语句,因为:

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

    如果我实施了一系列Exception语句,我将不得不回到if日志记录行为,这只是打印出{{1}的结果或者我可以尝试std::exceptionwhat

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

1 个答案:

答案 0 :(得分:1)

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

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";
    }
}