1

I'm working on a feature which requires writing a single log file(identified by its path) in multiple processes. Previously, each process used to call printf to stream the log on terminal(standard output). Now I need to change the output destination to a file. So I tried using freopen to redirect the stdout to the file in each process.

freopen(file_path, "a", stdout); //

But it seems it doesn't work well. Some log is missing. What's the common practice to achieve this ?

B.T.W In our requirement, the user should be allowed to switch logging destination between file and standard output, so the first argument "file_path" could be tty when switched back to terminal. Is that OK to call freopen(tty, "a", stdout)?

snowfox
  • 1,978
  • 1
  • 21
  • 21
  • What about using the `syslog()` api? – alk Apr 15 '13 at 15:34
  • We use this API for other log types.. – snowfox Apr 15 '13 at 15:37
  • Then implement your own syslog; it's pretty trivial. Easy process writes strings over a socketpair to the logging/master process, which selects and reads in strings, writing each one in turn to the log. Twiddling a setting in the master process redirects logging for all children simultaneously. Hopefully you have some wrappers for I/O already. – Nicholas Wilson Apr 15 '13 at 15:40
  • Just start your program using a wrapper script which pipes all output to a file: `$ program >logfile 2>errorfile` It the user wants to see it's content coming a `tail -f logfile` helps. – alk Apr 15 '13 at 15:47
  • If accessing the file concurrently keep in mind the need for locking it to not mixup the different log entries, and following this need, the potenial performance issues. – alk Apr 15 '13 at 15:49
  • To alk: Our processes are not launched from shell. Actually, they are started along with the operating system. – snowfox Apr 15 '13 at 15:51
  • Another issue with using one file to have multiple process log to, is that you really get stuck if one of such processes dies holding the log lock. – alk Apr 15 '13 at 16:12

3 Answers3

2

You have many options:

1) the simplest approach would be for every process to simply write to the same log independently. The problem, of course, is that the file would get scrambled if any two processes wrote different messages at the same time.

2) You could instead have the processes send messages to one "master logger", which would then output messages one at a time, in the order received. The "master logger" might use sockets. If all the processes were on the same host, you might instead use a message queue or a named pipe.

3) Even simpler, you could have a system-wide semaphore to insure only one message at a time gets written.

4) Yet another approach could be to use an open-source logger such as log4j or syslog-ng

paulsm4
  • 114,292
  • 17
  • 138
  • 190
1

Writes in O_APPEND mode will do what you want as long as they are less than PIPE_BUF bytes, which is usually plenty of room (about 4k).

So, set the newly freopen()ed file to line buffered (_IOLBF below), and then make sure your writes contain a newline to flush the buffer:

freopen(file_path, "a", stdout);
setvbuf(stdout, (char *)NULL, _IOLBF, 0); // a.k.a. setlinebuf(stdout) under BSD

...

printf("Some log line\n"); // Note the newline!
Community
  • 1
  • 1
pilcrow
  • 56,591
  • 13
  • 94
  • 135
  • 1
    Note that this depends on the filesystem in question correctly supporting O_APPEND mode, which some filesystems (some versions of NFS for example) do not. Ideally in such cases, the open should fail, but in general it will succeed and simultaneous writes will lead to corruption. – Chris Dodd Apr 15 '13 at 16:32
  • Or call `fflush()` directly. – David R Tribble Apr 15 '13 at 22:54
0

Pipe your output to a file handle called, say, output using fprintf. Since file handle's are just pointers, just set output = stdout or output = yourFile. Then every fprintf(output, "sometext") winds up going wherever that handle is set at the moment. You can even have a function to dynamically redirect the output on the fly based upon user input.

Spencer Rathbun
  • 14,510
  • 6
  • 54
  • 73