utils#

Header: <pressio/utils.hpp>

Public namespaces: pressio, pressio::utils

Logger#

One of the main functionalities inside utils is the logger. The implementation is based on a modified version of spdlog, for example to work seamlessly with MPI.

Warning

By default, for performance reasons, the logger is disabled.

To enable logging, you need to do two things:

  • insert a define statement to set the minimum level before including the utils header

  • initialize and finalize the logger

The following snippet provides the main idea:

// set the min level
#define PRESSIO_LOG_ACTIVE_MIN_LEVEL 2

// this is needed since it contains the logging code
#include <pressio/utils.hpp>

int main()
{
  // namespace alias for readability
  namespace plog = pressio::log;

  // initialize logger (see below for details)

  // your code, use logger

  // finalize logger (see below for details)
}

The supported levels are:

#define PRESSIO_LOG_LEVEL_TRACE     0
#define PRESSIO_LOG_LEVEL_DEBUG     1
#define PRESSIO_LOG_LEVEL_INFO      2
#define PRESSIO_LOG_LEVEL_WARN      3
#define PRESSIO_LOG_LEVEL_ERROR     4
#define PRESSIO_LOG_LEVEL_CRITICAL  5
#define PRESSIO_LOG_LEVEL_OFF       6

Attention

The log statements issued for a specific level will be printed only if PRESSIO_LOG_ACTIVE_MIN_LEVEL is smaller or equal than that level. If the logger is disabled, the macros are expanded to a no-op. So it does not cost you anything to place log statements in your code, because in production mode you can just compile to no-op.


Initializing and finalizing#

To initialize, you have these choices:

// only log messages to terminal
plog::initialize(pressio::logto::terminal);

// log messages to terminal and a file named, e.g., "my_log.txt"
plog::initialize(pressio::logto::fileAndTerminal, "my_log.txt");

// only log messages to a file named, e.g., "my_log.txt"
plog::initialize(pressio::logto::file, "my_log.txt");

And to finalize:

plog::finalize();

If running with MPI#

If you are running with MPI, the logger prints to the terminal only from rank==0. However, it automatically creates a per-rank log file if you choose the file output. For example, the following code:

int main(int argc, char **argv)
{
  MPI_Init(&argc, &argv);
  int my_rank = {};
  MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

  namespace plog = pressio::log;
  plog::initialize(pressio::logto::file, "log_file.txt");
  PRESSIOLOG_INFO("print from rank {:2}", my_rank);
  plog::finalize();

  MPI_Finalize();
}

If we were to run this with N ranks, we would obtain two files log_file.txt_0, and log_file.txt_1. Currently, the logger works only for the world communicator. We will later extend the API to accept a communicator object.

Resetting the level#

If you want, you can use the define statement to set the min level, but then at runtime you can reset for a higher level (see below). Note that you cannot reset the level to something that is lower than the one you set via the define statement.

// your code
// ...
plog::setVerbosity({plog::level::info});
// ...

The loggin macros#

To actually issue log statements, you use the macros as in the following example:

int main()
{
  // initialize logger

  double a = 1.1;
  PRESSIOLOG_TRACE("my value is {:.6f}", a);
  PRESSIOLOG_DEBUG("my value is {:.6f}", a);
  PRESSIOLOG_INFO("my value is {:.6f}", a);
  PRESSIOLOG_WARN("my value is {:.6f}", a);
  PRESSIOLOG_ERROR("my value is {:.6f}", a);
  PRESSIOLOG_CRITICAL("my value is {:.6f}", 55.6);

  // finalize logger
}

where we note that you can use the {fmt} library to properly format the print statements.