c++fileboostfilestreamboost-log

Error when reading information stored in a file created with Boost::log library


I have created an interface file, my_boost_log.h, and the related implementation file my_boost_log.cpp

The interface file contains the following lines:

#ifndef MY_BOOST_LOG_H
#define MY_BOOST_LOG_H

#define MY_LOG_DEBUG(logRecord)
  BOOST_LOG_SEV(SLogger::get(), boost::log::trivial::debug)
      << "(" << __FILE__ << ", " << __LINE__ << ") " << logRecord;

BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(
  SLogger, boost::log::sources::severity_logger<boost::log::trivial::severity_level>)

void init_library(std::string fileLog);

#endif

where "fileLog" will be the name of the file where the records will be stored. The implementation file is as follows:

#include "my_boost_log.h"

void init_library(std::string fileLog)
{
  boost::log::add_file_log(
   boost::log::keywords::file_name = logFileName,
   boost::log::keywords::rotation_size = 10 * 1024 * 1024,
   boost::log::keywords::time_based_rotation =
   boost::log::sinks::file::rotation_at_time_point(0, 0, 0),
   boost::log::keywords::format = "%TimeStamp% [%Severity%] : %Message%");


  auto core = boost::log::core::get();
  core->add_global_attribute("TimeStamp", boost::log::attributes::local_clock());
  core->add_global_attribute("Process", boost::log::attributes::current_process_name());
  core->add_global_attribute("ProcessID", boost::log::attributes::current_process_id());
  core->add_global_attribute("ThreadID", boost::log::attributes::current_thread_id());
  core->set_filter(boost::log::trivial::severity >= boost::log::trivial::trace);
}

So basically everytime I use the macro MY_LOG_DEBUG(logMessage), the argument will be stored in the file named with the string passed as parameter to the init_library() function. I would like to test this code. In other words I want to use the macro MY_LOG_DEBUG(logMessage) to store the message "logMessage" in the file and then retrieve the same message reading the file to check if they are equal. To do this I have created a file named test_my_boost_log.cpp, shown below:

#include <gtest/gtest.h>
#include "my_boost_log.h"
#include <iostream>
#include <fstream>

class MyLogTest: public ::testing::Test
{
 protected:
};


TEST_F(MyLogTest, TestLogRecord)
{
  std::fstream logFile;
  std::string logMess{"Test debug log\n"};
  LOG_INFO(logMess);
  logFile.open("test_file.log", std::ios::in);
  
  if (logFile.is_open())
  {
    std::string fileContent;
    while(getline(logFile, fileContent))
    {
      std::cout << "Content of the file: " << fileContent << std::endl;
    }
    logFile.close();
  }
  else
  {
    std::cout << "Error: cannot open the file" << std::endl;
  }

  // Remaining part to check if what's inside the file is equal to logMess variable
}

int main(int argc, char** argv)
{
 log::init("test_file.log");
 ::testing::InitGoogleTest(&argc, argv);
 return RUN_ALL_TESTS();
}

My problem here is that I am not able to read the content of the file and store it in the "fileContent" string variable to do the check I need. When I try to print it, the string is empty. I've performed different tests and from my understanding:

Is there a way to read the file content before the process termination and perform my test?


Solution

  • I've performed different tests and from my understanding:

    • the file where the log will be stored is created when the macro is used, not when init_library() is called;

    This is correct, the library writes to the file only when it processes a log record. Creating a file sink does not create a file. This is to ensure that no log file is created when the application does not emit log records.

    If you want the file to be created, you can either create it manually early in main or write an early message in log (e.g. "Application started" or alike).

    • it seems that the message is stored in the file (and then the file is closed) when the process (main) terminates

    Strictly speaking, the contents are written to the file when file stream buffers are flushed. This happens when the buffers are filled or when the application terminates. Note that during run time, the buffers may be flushed in the middle of a formatted log record, so an external application may observe split log records. It is also unspecified when the external observer will see the written log records, as flushing happens at an unknown point.

    You can perform a manual flush by calling flush on the sink or on the logging core (in the latter case, all sinks will be flushed). The sink is returned by add_file_log. You can also enable automatic flushing after every log record by passing auto_flush = true to add_file_log. In the latter case, each log record will be flushed to the file as soon as possible. However, note that flushing is expensive and may affect performance.