您最好尽可能简化界面。完全将登录用户的界面与实际实现登录的方式分开。
横切关注点总是很难维护的,所以让事情变得更加复杂只会让您感到痛苦。
某些库只需要像这样的简单东西:
void logDebug(const std::string &msg);
void logWarning(const std::string &msg);
void logError(const std::string &msg);
他们不应该添加或指定更多的上下文信息。无论如何,没有人能够使用这些信息,所以不要过度设计它。
如果你开始向日志调用中添加更多信息,那么使用它的客户端代码就会变得更加难以重用。通常情况下,当组件在不同的抽象层次上被使用时,你会看到这种情况。特别是当一些低级代码提供的调试信息只与较高层次有关时。
这并不会强制你的日志实现(甚至是日志实现符合的接口!)做出任何改变,因此你可以随时进行更改。
更新:
就标记而言,那是一个高级问题。我猜想它不属于日志记录,但这并不重要。
将其从日志消息规范中排除。低级代码不应该关心你或你的经理是谁。
我不知道你在示例中如何指定X或Y。从我们得到的描述中,你如何做到这一点并不明显。我只是为了演示而使用字符串,但如果可能的话,你应该用一些类型安全的东西来替换它。
如果这总是开启的,那么只拥有一个实例上下文(可能是一个全局变量)可能是适当的。当你登录时,设置上下文并忘记它。如果它从未被设置过,则要极其严格地抛出异常。如果不能在没有设置时抛出异常,则它就没有总是开启。
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()
,但它会给你带来方便。