2

This is a buildup on an earlier question of mine: redirect stdout to a file

The situation is as follows: I am using an external library which provides vital functionality to my code, but which produces a large amount of logging information via printf (bad style, I know, but I can't change it, and I can't switch to another library).

I would like to redirect the output to a file, which I have managed to achieve with the help of the great StackOverflow community (see earlier link). Since then, the situation has gotten a little more complicated: Now I want to append my own logging information to the same output file, preferably using std::ofstream. The code is not and will never be parallelized, so concurrent write access is not a concern. However, please consider the following chunk of code:

#include <iostream>
#include <fstream>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

// this is just a global "backup" of the stdout fd
// so that I can restore it after the function call
int stdoutfd = dup(fileno(stdout));

int stupid_library_function(){
  // I can't change this function
  printf("hello world!\n");
  return 1;
}


int redirect_stdout(const char* fname, bool append){
  // redirect printf to a file
  fflush(stdout);
  int newstdout = append ? open(fname, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) : open(fname, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
  dup2(newstdout, fileno(stdout));
  close(newstdout);
  return fileno(stdout);
}


int restore_stdout(){
  // restore the normal behaviour of printf
  fflush(stdout);
  dup2(stdoutfd, fileno(stdout));
  return stdoutfd;
}

int main(){
  // just to test if everything is normal
  std::cout << "this is a normal printout" << std::endl;

  // create my own output stream
  std::ofstream outstream("log.txt");

  // some of my own output
  outstream << "calling stupid library function now" << "\n";
  outstream.flush();

  // redirect the output of the supid library function to the logfile
  redirect_stdout("log.txt",true);
  int val = stupid_library_function();

  // neither of the following lines causes the output of the stupid library function to appear in the log file
  fflush(stdout);
  fsync(stdoutfd);

  // restore the normal stdout
  restore_stdout();

  // some of my own output again
  outstream << "done calling stupid library function, result was " << val << "\n";
  outstream.flush();

  // close my own output stream
  outstream.close();

  // just to test if everything is back to normal
  std::cout << "this is (again) a normal printout" << std::endl;

  return 1;

}

Compiling and running this code should give you a file "log.txt", which would contain the following lines

calling stupid library function now
done calling stupid library function, result was 1

Due to my naive understanding, the file should contain three lines, the output "hello world" from the library function call between the two you find above.

My first thought were that there was a problem with flushing the stdout buffer, so I tried using "fflush" and "fsync" (see above). However, neither of those seem to have any effect in this case.

Would anybody be willing to enlighten me and show me a way of how to obtain the output

calling stupid library function now
hello world
done calling stupid library function, result was 1

with the following constraints:

  • the lines in the log file must be in the correct order
  • printf and std::cout must be restored to default behaviour when the code is done
  • the code of "stupid_library_function" must not be changed
  • the output within "main" must still be using std::ofstream
Community
  • 1
  • 1
carsten
  • 1,315
  • 2
  • 13
  • 27
  • Have you checked if the `open` in `redirect_stdout` is still succesful when `log.txt` is opened by `outstream`? – Daan Apr 30 '14 at 12:32
  • You are opening the same file twice, likely overwriting the interior usage with your one in `main()`. Ideally you'd use the same single file descriptor for everything, but I don't think there's any "official" way to do it. In your case, the safest thing would be to `.close` the ofstream before you run your 3rd party library and then call `.open` again once it's done running. – Joe Apr 30 '14 at 12:35
  • Why are you not looging to std::cout after redirecting stdout? –  Apr 30 '14 at 12:48
  • @Daan: No, I haven't checked this. How could I verify? – carsten Apr 30 '14 at 13:12
  • @Joe: This is a good suggestion, I will try it. – carsten Apr 30 '14 at 13:12
  • @DieterLücking: Because the std::ofstream in my "real" code is actually encapsulated in a messaging service, which would take a great deal of effort to change. – carsten Apr 30 '14 at 13:13
  • @carsten: you could check the return value of `open`, which should be `-1` if an error occured. After an error, you can check `errno` to (hopefully) get a little more information on why it went wrong. If you just want a quick fix, though, Joe's suggestion should do it for you. – Daan Apr 30 '14 at 16:17

0 Answers0