首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在没有Singleton的情况下实现方便的日志记录?

如何在没有Singleton的情况下实现方便的日志记录?
EN

Stack Overflow用户
提问于 2011-12-01 06:19:21
回答 4查看 23.4K关注 0票数 40

我目前的实现,简化如下:

代码语言:javascript
复制
#include <string>
#include <memory>

class Log
{
  public:
    ~Log() {
      // closing file-descriptors, etc...
    }
    static void LogMsg( const std::string& msg )
    {
      static std::unique_ptr<Log> g_singleton;
      if ( !g_singleton.get() )
        g_singleton.reset( new Log );
      g_singleton->logMsg( msg );
    }
  private:
    Log() { }
    void logMsg( const std::string& msg ) {
      // do work
    }
};

总的来说,我对这项实施感到满意,因为:

happy

  • relatively

  • 惰性实例化意味着我不付钱,除非我使用

  • unique_ptr意味着自动清理,所以val研是简单易懂的

然而,负面因素是:

在我的脑海中,unit-testing

  • dissonance引入了一个伪全局(有点代码气味)

,这对

  • 单身汉是不利的。

下面是我针对那些成功地从C++代码中清除所有单身者的开发人员提出的问题:

  • What类的非单例实现--您是否使用与日志一样简单和可访问的接口用于应用程序范围的logging?
  • Is::LogMsg()调用above?

如果可能的话,我想避免在我的代码中传递一个日志实例--注意:我是在问,因为,如果有一个好的、合理的选择,我也想从我的代码中清除所有的单身者。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-12-01 07:57:43

首先:没有必要使用std::unique_ptr

代码语言:javascript
复制
void Log::LogMsg(std::string const& s) {
  static Log L;
  L.log(s);
}

生成完全相同的延迟初始化和清理语义,而不引入所有语法噪声(以及冗余测试)。

现在已经不碍事了..。

你的课非常简单。您可能希望构建一个稍微复杂一些的版本,对于日志消息的典型要求是:

  • timestamp
  • level
  • file
  • line
  • function
  • process名称/线程id (如果相关)

在消息本身的顶部。

因此,完全可以想象有几个具有不同参数的对象:

代码语言:javascript
复制
// LogSink is a backend consuming preformatted messages
// there can be several different instances depending on where
// to send the data
class Logger {
public:
  Logger(Level l, LogSink& ls);

  void operator()(std::string const& message,
                  char const* function,
                  char const* file,
                  int line);

private:
  Level _level;
  LogSink& _sink;
};

为了方便起见,通常将访问包装在宏中:

代码语言:javascript
复制
#define LOG(Logger_, Message_)                  \
  Logger_(                                      \
    static_cast<std::ostringstream&>(           \
      std::ostringstream().flush() << Message_  \
    ).str(),                                    \
    __FUNCTION__,                               \
    __FILE__,                                   \
    __LINE__                                    \
  );

现在,我们可以创建一个简单的详细记录器:

代码语言:javascript
复制
Logger& Debug() {
  static Logger logger(Level::Debug, Console);
  return logger;
}

#ifdef NDEBUG
#  define LOG_DEBUG(_) do {} while(0)
#else
#  define LOG_DEBUG(Message_) LOG(Debug(), Message_)
#endif

并方便地使用:

代码语言:javascript
复制
int foo(int a, int b) {
  int result = a + b;

  LOG_DEBUG("a = " << a << ", b = " << b << " --> result = " << result)
  return result;
}

这种咆哮的目的是什么?并不是所有的全球需求都是唯一的。单子的唯一性通常是无用的。

注意:如果涉及到std::ostringstream的一点魔力吓到了你,这是正常的,

票数 45
EN

Stack Overflow用户

发布于 2011-12-01 08:15:27

我想提出一个简单、务实的解决方案:

您需要一个全局可访问的解决方案。在大多数情况下,我试图避免全球化,但对于伐木者,让我们面对它,这通常是不切实际的。

因此,我们确实需要----才能在全球范围内访问。

但是,我们不希望独生子女所施加的附加“只能有一个”限制。您的一些单元测试可能希望实例化它们自己的私有记录器。其他人可能会想要取代全球记录器。

所以让它成为一个全球性的。一个简单的简单全局变量。

诚然,这仍然不能完全解决单元测试的问题,但是我们不能总是拥有我们想要的一切。;)

正如注释中所指出的,您需要考虑全局值的初始化顺序,在C++中,该顺序部分是未定义的。

在我的代码中,这通常不是一个问题,因为我很少有一个以上的全局(我的记录器),而且我严格遵守的规则,不允许全局依赖于彼此

但这是你必须考虑的,至少。

票数 13
EN

Stack Overflow用户

发布于 2011-12-01 07:30:42

我非常喜欢下面的界面,因为它使用流。当然,您可以向其添加通道、时间和线程信息。另一个可能的扩展是使用__FILE____LINE__宏并将其作为参数添加到构造函数中。如果您不喜欢流语法,甚至可以添加一个可变模板函数。如果要存储某些配置,可以将它们添加到一些静态变量中。

代码语言:javascript
复制
#include <iostream>
#include <sstream>

class LogLine {
public:
    LogLine(std::ostream& out = std::cout) : m_Out(out) {}
    ~LogLine() {
        m_Stream << "\n";
        m_Out << m_Stream.rdbuf();
        m_Out.flush();
    }
    template <class T>
    LogLine& operator<<(const T& thing) { m_Stream << thing; return *this; }
private:
    std::stringstream m_Stream;
    std::ostream& m_Out;
    //static LogFilter...
};

int main(int argc, char *argv[])
{
    LogLine() << "LogLine " << 4 << " the win....";
    return 0;
}
票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8337300

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档