Program Listing for File LogTee.hpp

Return to documentation for file (include/dish2/debug/LogTee.hpp)

#pragma once
#ifndef DISH2_DEBUG_LOGTEE_HPP_INCLUDE
#define DISH2_DEBUG_LOGTEE_HPP_INCLUDE

#include <algorithm>
#include <fstream>
#include <iostream>
#include <utility>

#include "../record/make_filename/make_log_filename.hpp"
#include "../record/make_filename/make_meta_path.hpp"
#include "../utility/mkdir_exists_ok.hpp"
#include "../utility/pare_keyname_filename_impl.hpp"

#include "make_log_entry_boilerplate.hpp"

namespace dish2 {

// adapted from https://stackoverflow.com/a/1761027
class LogTee : public std::ostream
{

  struct ComposeBuffer: public std::streambuf {

    void add_buffer( std::streambuf* buf ) { bufs.push_back(buf); }

    virtual int overflow( const int c ) {
      std::for_each(
        std::begin( bufs ), std::end( bufs ),
        [c]( auto& buf ){ buf->sputc( c ); }
      );
      return c;
    }

    emp::vector<std::streambuf*> bufs;

  };

  ComposeBuffer buffer;
  std::ofstream logfile_out;

  void link_stream( std::ostream& out ) {
    out.flush();
    buffer.add_buffer( out.rdbuf() );
  }

  public:

  LogTee() : std::ostream(nullptr) {

    std::ostream::rdbuf(&buffer);

    dish2::mkdir_exists_ok( "outmeta" );
    const std::string out_filename = dish2::pare_keyname_filename_impl(
      dish2::make_log_filename(),
      dish2::make_meta_path()
    );
    const std::string out_path = dish2::make_meta_path( out_filename );

    logfile_out = std::ofstream( out_path, std::ios_base::app );

    link_stream( logfile_out );
    link_stream( std::clog );

    *this << dish2::make_log_entry_boilerplate();
    *this << "set up logfile at " << out_path << '\n' << '\n';

  }

};

} // namespace dish2

#endif // #ifndef DISH2_DEBUG_LOGTEE_HPP_INCLUDE