C++日志包装器设计

Leo*_*Leo 5 c++ logging

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

因此,我需要一些足够灵活的日志包装器来充分利用任何underling日志库的功能.

有关这种包装设计的任何建议吗?

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

Tom*_*err 2

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

横切关注点的维护成本总是很高,所以让事情变得更加复杂会让你讨厌生活。

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

void logDebug(const std::string &msg);
void logWarning(const std::string &msg);
void logError(const std::string &msg);
Run Code Online (Sandbox Code Playgroud)

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

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

这不会强制您的日志记录实现(甚至日志记录实现所遵循的接口!)进入任何内容,因此您可以随时更改它。

更新:

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

将其排除在日志消息规范之外。低级代码不应该告诉飞行卡车您或您的经理是谁。

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

如果它总是打开,那么只有一个实例上下文(可能是一个全局变量)可能是合适的。当您登录时,设置上下文并忘记它。如果没有设置,请以极端的偏见来抛出。如果在未设置时无法抛出,那么它并不总是打开。

void setLoggingContext("X:");
Run Code Online (Sandbox Code Playgroud)

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

LoggingTag tag("X:");
Run Code Online (Sandbox Code Playgroud)

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

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

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

void baz() {
  logWarning("baz");
}
Run Code Online (Sandbox Code Playgroud)

无论哪种方式,这都不会影响您向日志添加消息的方式。该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>();
}
Run Code Online (Sandbox Code Playgroud)

这不会强迫typeid(T).name()某人在不愿意的情况下使用,而是给您带来了便利。