4

I have basic logging process. When an error occured in the program, it has to been logging to a .txt file. I use following code for this:

#include <fstream> 

fileName = "logs/error_log.txt";
ofstream myfile;
myfile.open (fileName,fstream::app);
myfile << serialized_string << endl;
myfile.close();

When an error occured it goes to error_log.txt file successfully. But when program crashed and is restarted afterward, new logs are not logged as append. As expected the way I am using creates a new file which has same name existed file and write on it. Can someone explain me how should I write older logs as well?

Edit: These are steps I have faced: I am using raspbian and I compile with following:

g++ main.cpp -lwiringPi -lpthread -lcurl -o test

That is the whole function.

int putLog(const char* process, int logType, string logData) {
  isLoggerBusy = true;
  string fileName;
  std::string color;
  switch (logType) {
    case 0:
      fileName = "logs/error_log.txt";
      // color = "\033[0;31m";
      break;

    case 1:
      fileName = "logs/info_log.txt";
      //    color = "\033[0;36m";
      break;

    case 2:
      fileName = "logs/state_log.txt";
      //    color = "\033[1;33m";
      break;
  }

  if (process == "WebSocket") {
    color = "\033[1;32m";
  }

  json j = {
      {"Process", process}, {"Time", currentDateTime()}, {"Log", logData}};

  string serialized_string = j.dump();
  fix_utf8_string(serialized_string);

  ofstream myfile;
  myfile.open(fileName, fstream::app);
  cout << color << serialized_string << '\n';
  myfile << serialized_string << endl;
  myfile.close();
  isLoggerBusy = false;
  cout << "\033[0m" << endl;
  return 0;
}
  • I started the program. It write downs these lines to the state_logs.txt

{"Log":"Incoming Message{\"Action\":\"Heartbeat\",\"Data\":null}","Process":"WebSocket","Time":"2018-08-16.14:53:52"}

{"Log":"GSM Setup Finished","Process":"SMSService","Time":"2018-08-16.14:54:13"}

  • Stopped the program with CTRL-C and control the state_logs.txt and I can see now two lines there.
  • Restart the program and interrupt with CTRC-C again in 10 seconds (before a new line logging.)
  • I check the state_logs.txt again and now I can not see nothing. Re-did this process but this time waiting a bit more before interrupt program(just a bit more to get only one line of log.). So now I can see only one and timestamps has been changed.
Marek R
  • 23,155
  • 5
  • 37
  • 107
erondem
  • 140
  • 1
  • 2
  • 18
  • 4
    Possible duplicate of [How to append text to a text file in C++?](https://stackoverflow.com/questions/2393345/how-to-append-text-to-a-text-file-in-c) – hellow Aug 16 '18 at 11:28
  • 6
    It's not a dup, OP opens their file with `fstream::app`. – YSC Aug 16 '18 at 11:29
  • 2
    Do you check that the log is actually written before running the program again? It might be that the program crashes *before* flushing the file. So you are not actually overwriting anything, it just wasn't there in the first place. – BoBTFish Aug 16 '18 at 11:29
  • 1
    @BoBTFish Yes I can see the error_log.txt file has the loggs until crush before restarting the program. After restarting I can not see the older logs the file starts with new logs. I suspect a new file is created with the same name. – erondem Aug 16 '18 at 11:32
  • 1
    it should work. I'm guessing you are doing something else that breaks this functionality. Check if other part of code do not open that file. – Marek R Aug 16 '18 at 11:34
  • @MarekR I just tried that, it made no difference. I haven't been able to reproduce this. – BoBTFish Aug 16 '18 at 11:38
  • @erondem I can't reproduce this with the code you provided. Can you please provide a full [mcve], with as much detail as possible about how you compile and run this? – BoBTFish Aug 16 '18 at 11:38
  • It's strange - it should work. It doesn't crash in (access of) `serialized_string`? This is the only weakness in this code I can imagine. – Scheff's Cat Aug 16 '18 at 11:39
  • 3
    May be, it's worth to mention your OS and your compiler (version). – Scheff's Cat Aug 16 '18 at 11:39
  • And, you don't (accidentally) open the log file once without `std::ios::app`? – Scheff's Cat Aug 16 '18 at 11:41
  • Defiantly more context is needed. How do you handle each log entry? Do you open and close file on each log entry, or do you keep it open when application runs. Do you handle application signal to close this file gracefully in case of crash? – Marek R Aug 16 '18 at 11:43
  • Can you reproduce your issue with [this sample](https://stackoverflow.com/a/51876076/7478597)? – Scheff's Cat Aug 16 '18 at 11:46
  • I added edit to my question and tried to gave a clear explanation. – erondem Aug 16 '18 at 12:01
  • Probably off-topic but: `if(process == "WebSocket")` is not safe for `const char *process`. It just compares two pointers. Thus, it depends on how compiler collects constant strings and hence may or may not do what's expected. Though, `std::string` has overloaded the comparison operators, for `const char*` the better alternative is `std::strcmp()`. – Scheff's Cat Aug 16 '18 at 12:20
  • `isLoggerBusy` makes me suspicious: Is it an `std::atomic`? Is it a check for recursion or is it a guardian for multi-threading? In the latter case, I suspect multi-threading issues. (I once used `std::atomic` to build something like a spin-lock when I couldn't use a mutex in a specific case. However, usually a `std::mutex` is the right tool for this.) – Scheff's Cat Aug 16 '18 at 12:27
  • I suspect, depending on filesystem etc, that opening a file, even in append more can nuke the file depending on when a crash happens: https://stackoverflow.com/questions/38863874/ofstreammode-iosout-wipes-existing-file-blank-when-system-halt – doctorlove Aug 16 '18 at 13:10

1 Answers1

4

I cannot reproduce what OP describes.

I just tested on cygwin/Windows 10. (I didn't know how to make this test on an online compiler.)

testFStreamApp.cc:

#include <iostream>
#include <fstream>

int main()
{
  std::cout << "Log error...\n";
  { std::ofstream log("testFStream.log", std::ios::out | std::ios::app);
    log << "Error happened!" << std::endl;
  }
  std::cout << "Going to die...\n";
  abort();
  return 0; // should never be reached
}

Test Session:

$ g++ -std=c++11 -o testFStreamApp testFStreamApp.cc 

$ rm testFStream.log

$ for i in 1 2 3; do  
> echo "$i. start:"
> ./testFStreamApp 
> done
1. start:
Log error...
Going to die...
Aborted (core dumped)
2. start:
Log error...
Going to die...
Aborted (core dumped)
3. start:
Log error...
Going to die...
Aborted (core dumped)

$ cat <testFStream.log 
Error happened!
Error happened!
Error happened!

$

YSC pointed out that I made some silent changes. I did it assuming no relevance.

However, to erase any excuses, I tried also:

#include <iostream>
#include <fstream>

int main()
{
  std::cout << "Log error...\n";
  std::ofstream log;
  log.open("testFStream.log", std::fstream::app);
  log << "Error happened!" << std::endl;
  log.close();
  std::cout << "Going to die...\n";
  abort();
  return 0; // should never be reached
}

The output was exactly as above.


I hadn't dared to test this but doctorlove encouraged me:

#include <iostream>
#include <fstream>

int main()
{
  std::cout << "Log error...\n";
  std::ofstream log;
  log.open("testFStream.log", std::fstream::app);
  log << "Error happened!" << std::endl;
  std::cout << "Going to die...\n";
  abort();
  log.close();  
  return 0; // should never be reached
}

Even in this case, I got the same result.

At this point, I must admit that cygwin is just a wrapper around the win32 API. So, in this case, I wouldn't wonder if this behaves different on other OSes.

I'm aware that std::endl does a flush() insight. The question is how far down (into the system) the flush() is effective. (In daily work, I try to write the code in a way that it is not necessary to rely on such details...) ;-)

Scheff's Cat
  • 16,517
  • 5
  • 25
  • 45
  • What if you (1) don't specify `out`? (2) use `fstream`? (3) use `open()`? – YSC Aug 16 '18 at 11:47
  • @YSC (1) Same result. (1) + (3) Same result. I didn't understand (2). OP used `std::ofstream` as well. `std::ios::app` and `std::fstream::app` shouldn't be a difference (it's inherited, isn't it). – Scheff's Cat Aug 16 '18 at 11:50
  • @YSC (2) `std::fstream::app` - Same result. – Scheff's Cat Aug 16 '18 at 11:51
  • If you `abort` before you `close`? (SInce it's not clear from the OP where/when the crash may happen) – doctorlove Aug 16 '18 at 11:59
  • And for completeness, how do we know the abort doesn't happen *before* the file gets opened? – doctorlove Aug 16 '18 at 13:01
  • @doctorlove This isn't a recommendation for another test, is it? ;-) As I understand OP, there are messages logged but at restart of application old messages are lost. This shouldn't happen due to `std::ios::app`. – Scheff's Cat Aug 16 '18 at 13:03
  • Not at all - I now suspect the OP doesn't contain neough info to figure out what's what, espcially after your tests. have an upvote :-) – doctorlove Aug 16 '18 at 13:05