NOTE: The whole code but specially the documentation is not very polished - if you intent to use this let me know and I will invest further work. What do I need to use this? You have a multi-threading C++ project, you will need "boost::signals2" and you will need to read this and look through some code to fully understand what to do. You can also contact me with questions. However I will NOT explain "MPI" or "boost" or any other 3rd party technology. What is this and what should this do? This is a logger class for C++ multi-threading code e.g. using MPI. It allows you to log output "stream-like" (using "<<" or "<") but behind the scenes it provides quite complex control on where this output goes. It will generally go into file(s) and stream(s). It comes with two low level, simple logger classes called "CLogfile" and "CLogstream" and a higher level, more complex "Log" class. This is the one documented below. All three classes, "CLogfile", "CLogstream" and "Log" require a call to their "init" methods before they can be used. The "init" methods require a pointer to a class that provides a "rank" method that should return the correct processor rank. The code provides only a dummy for such a class called "MPIhelper" which you should replace with your own, working version. It does not have to be anyhow related to MPI. It is up to you what multi-threading technology you use, the "init" methods only require that the MPIhelper class has a "rank" method returning integer. NOTE: The "init" method of the "Log" class accepts a number of further options, in particular you will have to supply a filename as further parameter if you want all processes to write to the same file. Also note that in this case you would have to use "<" instead of "<<", see below. Example0: MPIhelper helper(); logger.init(&helper, "log/path"); // or for the lower level classes: clogf.init(&helper, "log/path"); clogs.init(&helper); Initialising the log classes: See code below - note that if you are using a multi-threading code the below initialization code will result in one common output stream for all processes but an individual output file for each. How is that? Well you declare an individual file stream with "std::ofstream logstream_f;" on each processor but passing "&std::cout" on each processor will result in the same common standard out. If you would want to have separate streams for each processor you would have to define a separate stream just as you do with the filestream. If the other way around you would want to have the same file on each processor you would have to define one on a specific processor only and pass it on to all other running processes. To use only the simple "CLogfile" or "CLogstream" classes do: MPIhelper helper(); logger.init(&helper, "log/path"); std::ofstream logstream_f; CLogfile logger_f(&logstream_f); logger_f.init(&helper, "log/path"); CLogstream logger_s(&std::cout); logger_s.init(&helper); To use the more complex logger class: MPIhelper helper(); logger.init(&helper, "log/path"); std::ofstream logstream_f; Log logger( &logstream_f, &std::cout); "<<"-operator: The logger will use the normal bitwise stream operator and it will accept all stream modifiers that exist. "<"-operator: The logger will use a costum stream operator that channels all output through the boost::signals2 library! This allows you to write to the same file an all processes since boost::signals2 has an own mutex! However it also means that "<" will accept only some stream modifiers like e.g. "endl". []-operator: The logger uses the []-operator to specify the output channel: the letters F, S, i & p are used therefore. F means output to file, S means output to stream. FS means output to "file & stream". "i" specifies whether to put an info string before each line which can contain the processor id and date. "p" is a bit more complex and shall be explained later. ()-operator: The logger uses the ()-operator to specify the output process id: (n) tells the logger to output _only_ on the processor with id 2. NOTE1: both above operators are "sticky", once used they will stay set and keep affecting the output even if they are not further specified. NOTE2: the info string (triggered by "i") will contain the processor id only in the stream output but not in the files - since the files are already seperated by processor id. Example1: logger[OFSi](2) << "Hello world" << std::endl; logger << "Sticky test"; "Hello world" will be written into a file and stream only on processor 2. "Sticky test" will also be written into a file and stream only on processor 2. Both will be preceeded by an info string. Now we can explain what "p" does in the []-operator: "p" can only be written directly after "F" or "S" and tells the logger to consider the ()-operator (processor id operator) _only_ for the letter they follow. Complicated? Look at this example: Example2: logger[OFpS](2) << "Hello all streams" << std::endl; logger[OFSp](2) << "Hello all files" << std::endl; "Hello all streams" will be written to stream on all processors, but into file only on processor 2. "Hello all files" will be written to file on all processors, but into stream only on processor 2. What will "OFpSp" do? Simply the same as "OFS", the two "p"s will neutralize each other. Also remember that the operators are sticky: we could change the 2nd line to "logger[OFSp]" instead of "logger[OFSp](2)" but the "(2)" would still be in effect. Example3: logger[OFi] << "Write only to file(s) with info" logger[OSpF](2) << "Write into file(s) on all processors, to stream only on processor 2, no info" logger() << "Write to stream and file(s) on all processors: 'OSpF' is still in effect, but not '(2)', thus 'p' is meaningless too"