librascal icon indicating copy to clipboard operation
librascal copied to clipboard

Implemement a small logging framework

Open Luthaf opened this issue 5 years ago • 9 comments

Instead of using std::cout/std::cerr directly, we should implement a small logging framework like

#include "rascal/logging.hh"

rascal::info("starting computation");

auto foo = some_function_call();
rascal::debug("this value is now ", foo);

if (bad) {
    rascal::warning("your simulation is going to explode!");
}

This has the following benefits:

  • remove <iostream> from the headers rascal pulls in, cutting down compile time
  • the users can enable/disable debug info for a specific run
  • the user can override where the logs are sent to (for example, jupyter notebook do not show the C++ stdout messages)

Luthaf avatar Dec 09 '19 15:12 Luthaf

The API could look like this

#include <sstream>
#include <string>

namespace rascal {

/// Possible logging levels
enum class LogLevel { Debug, Info, Warn, Error };

/// Send a single log message at the given level
void log(LogLevel level, const std::string &message);

template <typename... Args> void debug(Args &&... args) {
  std::stringstream stream;
  operator<<(stream, args...);
  log(LogLevel::Debug, stream.str());
}

template <typename... Args> void info(Args &&... args) {
  std::stringstream stream;
  operator<<(stream, args...);
  log(LogLevel::Info, stream.str());
}

template <typename... Args> void warning(Args &&... args) {
  std::stringstream stream;
  operator<<(stream, args...);
  log(LogLevel::Warn, stream.str());
}

template <typename... Args> void error(Args &&... args) {
  std::stringstream stream;
  operator<<(stream, args...);
  log(LogLevel::Error, stream.str());
}

using logging_callback =
    std::function<void(LogLevel level, const std::string &message)>;

/// Send all logging messages to the given callback
void log_to(logging_callback callback);

} // namespace rascal

Here, the variadic templates allow to send warnings like rascal::warn("this value is now ", foo, " shoud be ", bar);, and rascal::log_to allows users to redirect logging messages.

Luthaf avatar Dec 09 '19 16:12 Luthaf

the user can override where the logs are sent to (for example, jupyter notebook do not show the C++ stdout messages)

Not to diminish the value of such functionality but to do that in python you can use the ostream_redirect provided by pybind11 like:

with ostream_redirect():
   my_cpp_function_that_uses_cout_or_cerr()

felixmusil avatar Dec 09 '19 16:12 felixmusil

Can we automatically do ostream_redirect for all calls to C++ functions, or does the user need to manually specify it for every function call?

Luthaf avatar Dec 09 '19 17:12 Luthaf

Just to chip in: I like the idea in general. Seems like a clean way to do this. Even add different levels of verbosity

But I also tend to agree with Felix - if we can redirect output, we can leave out one level of complexity?

I suggest we discuss this on Wednesday?

mastricker avatar Dec 10 '19 09:12 mastricker

But I also tend to agree with Felix - if we can redirect output, we can leave out one level of complexity?

I don't know how ostream_redirect works. Can we use it to redirect output to a file? To a string? To LAMMPS logging facilities? Or does it only redirect to Python sys.stdout/sys.stderr?

Luthaf avatar Dec 10 '19 10:12 Luthaf

Yeah, that's what I mean - if we can. I put it in the meeting minutes.

mastricker avatar Dec 10 '19 10:12 mastricker

The real benefit of such a framework, in my view, isn't redirection (which is fairly easily done in Python) but rather logging levels, which I know I've had to implement from scratch for the gradient tester. I agree that it would be nice to simplify, centralize, and standardize this ad-hoc solution for the whole of the library.

max-veit avatar Dec 10 '19 11:12 max-veit

I do see a point, especially in logging levels. And I do not see an easy way to do that with the redirect. Let's discuss tomorrow.

mastricker avatar Dec 10 '19 12:12 mastricker

Can we automatically do ostream_redirect for all calls to C++ functions, or does the user need to manually specify it for every function call?

ostream_redirect make the bridge between the c++ outputs with the python output so cout goes into sys.stdout and the same for cerr. Note that errors thrown in c++ are already displayed in python and without the need for ostream_redirect.

felixmusil avatar Dec 10 '19 13:12 felixmusil