C ++日志包装器设计

时间:2011-10-25 16:49:05

标签: c++ logging

我想在我的应用程序中添加一个日志。我选择了一个日志库,但我希望能够切换到不同的库而不必更改任何使用日志记录的代码。

因此,我需要某种日志包装器,它足够灵活,可以充分利用任何基础日志库的功能。

对这种包装设计的任何建议?

编辑:我在这个包装器中必须具备的一个功能是组件标记。我希望我的算法类在其日志行之前出现“X:”,并且我的经理类出现“Y:”。如何将这些标签传播到底层日志以及如何构建组件标签命名机制是一个主要的设计问题。

3 个答案:

答案 0 :(得分:2)

最好的办法是让界面尽可能简单。完全将日志记录用户的界面与日志实际实现的方式分开。

交叉问题的维护费用总是很昂贵,所以让事情变得更复杂会让你讨厌生活。

有些图书馆只想要这样简单的东西:

void logDebug(const std::string &msg);
void logWarning(const std::string &msg);
void logError(const std::string &msg);

他们不应该添加或指定任何更多的上下文。没有人可以使用这些信息,所以不要过度设计。

如果您开始向日志记录调用添加更多信息,则会更难重用使用它的客户端代码。通常,当组件用于不同的抽象级别时,您将看到此表面。特别是当某些低级代码提供的调试信息仅与更高级别相关时。

这不会强制您的日志记录实现(甚至是日志记录实现符合的接口!)也可以强制执行任何操作,因此您可以随时更改它。

更新:

就标签而言,这是一个高级别的问题。我将推测它不属于日志,但这既不在这里也不在那里。

使其脱离日志消息规范。低级代码不应该给你或你的经理的飞行卡车。

我不知道您在示例中如何指定XY。从我们给出的描述来看,你如何做到这一点并不是很明显。我将只使用一个字符串进行演示,但是如果可能的话,你应该用类型安全的东西替换它。

如果始终打开,那么只有实例上下文(可能是全局变量)可能是合适的。登录时,设置上下文并忘记它。如果它没有被设置,则抛出极端的偏见。如果在未设置时无法抛出,则不会一直打开。

void setLoggingContext("X:");

如果这在不同的抽象层次上发生变化,我会考虑基于堆栈的RAII实现。

LoggingTag tag("X:");

当不同的堆栈帧传递不同的值时,我不确定您在场景中的要求是什么。我可以看到堆栈的顶部或底部对于不同的用例是合理的。

void foo() {
  LoggingTag tag("X:");
  logWarning("foo");
  bar();
  baz();
}

void bar() {
  LoggingTag tag("Y:");
  logWarning("bar");
  baz();
}

void baz() {
  logWarning("baz");
}

无论哪种方式,这都不会影响您向日志添加消息的方式。 baz函数没有指定LoggingTag的上下文。由于这个原因,使用logWarning无法了解标签非常重要。

如果你想根据某种类型进行标记,你可以做一些简单的事情。

struct LoggingTag {
  LoggingTag(const std::string &tag_) : tag(tag_) {}
  template<typename T>
    static LoggingTag ByType() {
      return LoggingTag(typeid(T).name());
    }
  std::string tag;
};

void foo() {
  LoggingTag tag = LogginTag::ByType<int>();
}

这不会强制某人使用typeid(T).name()如果他们不想这样做,但会给你带来便利。

答案 1 :(得分:1)

我喜欢这种方法:

class Log {
public:
    virtual logString(const std::string&)=0;
};

template <typename T>
Log& operator<<(Log& logger, const T& object) {
        std::stringstream converter;
        converter << object;
        logger.logString(converter.str());
        return logger;
}

简单快捷!您需要做的就是重新实现logString方法......

答案 2 :(得分:0)

看看zf_log图书馆。它非常小(约2000k行,编译时约10KB)和快速(参见README.md中的比较表)。它与你描述的包装器非常接近。它为您提供了一个可以在项目中使用的抽象API,并允许指定要使用的实际日志记录实现。请参阅custom_output.c示例,其中syslog用作输出工具。它也可以在库中私下使用,而不会与可能使用此库的其他代码发生冲突(有关详细信息,请参阅ZF_LOG_LIBRARY_PREFIX define)。 即使它不是你想要的,我想这可能是你的包装物的好例子。