代码之家  ›  专栏  ›  技术社区  ›  PeterK

C++中使用标准流的灵活记录器类

  •  5
  • PeterK  · 技术社区  · 14 年前

    我想创建一个灵活的logger类。我希望它能够输出数据到一个文件或标准输出。另外,我想使用流。课程应该看起来像:

    class Logger
    {
    private:
       std::ostream m_out; // or ofstream, iostream? i don't know
    public:
    
       void useFile( std::string fname);
       void useStdOut();
    
       void log( symbol_id si, int val );
       void log( symbol_id si, std::string str );
       //etc..
    };
    

    这个 symbol_id 是枚举并定义格式。我想要实现的是能够轻松地从标准输出切换到文件,反之亦然(这就是 use* m_out 只是写作 m_out << "something";

    我知道有很多方法可以解决这个问题 (使用 if's FILE* fprintf 等等,但我相信一定有办法用C++流来实现这个。但我似乎找不到办法。有人能帮帮我吗?

    4 回复  |  直到 14 年前
        1
  •  8
  •   the_mandrill    14 年前

    我以前解决这个问题的方法是 Logger 一个抽象基类并创建独立的 FileLogger OutStreamLogger 班级。然后创建一个 CompositeLogger 实现 记录器 接口,仅输出所有记录器:

    CompositeLogger compLogger;
    compLogger.Add(new FileLogger("output.txt"));
    compLogger.Add(new StreamLogger(std::cout));
    ...
    compLogger.log(...);
    

    std::ostream 并添加一个额外的标志来跟踪是否需要在清理时删除它:

    private:
      std::ostream*   m_out;
      bool            m_OwnsStream;
    
    Logger() {
       m_Out=&std::cout; // defaults to stdout
       m_OwnsStream=false;
    }
    void useFile(std::string filename) {
      m_Out=new std::ofstream(filename);
      m_OwnsStream=true;
    }
    ~Logger() {
      if (m_OwnStream) 
        delete m_Out; 
    }
    

    显然你还需要更多的检查 useFile useStdOut 以防止内存泄漏。

        2
  •  9
  •   utnapistim    14 年前

    std::o*stream C++中的类继承自STD::这意味着您应该根据std::of流指针或引用编写接口:

    class Logger
    {
        std::ostream *m_out; // use pointer so you can change it at any point
        bool          m_owner;
    public:
        // constructor is trivial (and ommited)
        virtual ~Logger()
        {
            setStream(0, false);
        }
        void setStream( std::ostream* stream, bool owner )
        {
            if(m_owner)
                delete m_out;
            m_out = stream;
            m_owner = owner;
        }
        template<typename T>
        Logger& operator << (const T& object)
        {
            if(!m_out)
                throw std::runtime_error("No stream set for Logger class");
            (*m_out) << object;
            return *this;
        }
    };
    
    // usage:
    Logger logger;
    logger.setStream( &std::cout, false ); // do not delete std::cout when finished
    logger << "This will be logged to std::cout" << std::endl;
    // ...
    logger.setStream( 
        new std::ofstream("myfile.log", std::ios_base::ate|std::ios_base::app), 
        true ); // delete the file stream when Logger goes out of scope
    logger << "This will be appended to myfile.log" << std::endl;
    
        3
  •  4
  •   Craig Wright    14 年前

    Dobbs博士发表了一篇文章,我用它作为日志记录的灵感。值得一读。 http://www.drdobbs.com/cpp/201804215

    看起来最近也发表了另一篇文章,但我没有读过。 http://www.drdobbs.com/cpp/225700666

        4
  •  2
  •   rturrado    14 年前

    我认为策略模式在概念上更适合这个问题。

    我们也可以为文件记录器使用不同的文件。

    class Logger
    {
    protected:
        ostream* m_os;
    public:
        void Log(const string& _s)
        {
            (*m_os) << _s;
            m_os->flush();
        }
    };
    
    class FileLogger : public Logger
    {
        string m_filename;
    public:
        explicit FileLogger(const string& _s)
        : m_filename(_s)
        {
            m_os = new ofstream(m_filename.c_str());
        }
        ~FileLogger()
        {
            if (m_os)
            {
                ofstream* of = static_cast<ofstream*>(m_os);
                of->close();
                delete m_os;
            }
        }
    };
    
    class StdOutLogger : public Logger
    {
    public:
        StdOutLogger()
        {
            m_os = &std::cout;    
        }
    };
    
    class Context
    {
        Logger* m_logger;
    public:
        explicit Context(Logger* _l)  {m_logger = _l;}
        void SetLogger(Logger* _l)    {m_logger = _l;}
        void Log(const string& _s)
        {
            if (m_logger)
            {
                m_logger->Log(_s);
            }
        }
    };
    
    int main()
    {
        string filename("log.txt");
    
        Logger*  fileLogger   = new FileLogger(filename);
        Logger*  stdOutLogger = new StdOutLogger();
        Context* context      = new Context(fileLogger);
    
        context->Log("this log out to file\n");
        context->SetLogger(stdOutLogger);
        context->Log("this log out to standard output\n");
    }