librascal
librascal copied to clipboard
Implemement a small logging framework
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)
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.
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()
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?
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?
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?
Yeah, that's what I mean - if we can. I put it in the meeting minutes.
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.
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.
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.