Skip to content

michalber/spdmon

Folders and files

NameName
Last commit message
Last commit date

Latest commit

084cf46 · Oct 20, 2022

History

8 Commits
Nov 5, 2021
Sep 30, 2021
Oct 16, 2022
Sep 30, 2021
Nov 5, 2021
Sep 30, 2021
Sep 30, 2021
Sep 30, 2021
Sep 30, 2021
Nov 5, 2021
Sep 30, 2021
Sep 30, 2021
Sep 30, 2021
Nov 5, 2021

Repository files navigation

spdmon

Progress monitor based on spdlog library. In just one function call visualize your loop progress!
It is simple, header-only library. Just copy spdmon.hpp file into you project and you are ready to go.
Main idea and algorithm is based on @epruesse progress monitor.

Usage

std::vector<int> vec {0,1,2,3,4,5,6,7,8,9};
for(auto [logger, val] : spdmon::LogProgress(vec))
{
    logger->info("Hi info");
    std::this_thread::sleep_for(500ms);
}
Progress logger: 100% |████████████████████████████| 250000/250000 [00:00:33 / 00:00:00]

It can also be executed with additional spdlog::logger containing many sinks.
In that case LoggerProgress will not include sinks writing logs to stdout when creating temporary logger.

std::vector<spdlog::sink_ptr> sinks;
sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>());
sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>("logfile", true));
auto combined_logger = std::make_shared<spdlog::logger>("name", begin(sinks), end(sinks));

spdmon::LoggerProgress monitor(combined_logger, "Progress", 40);

Main features of LoggerProgress:

  • Allows you to use default logger to send logs from many threads
  • Works only on Linux platform
  • Works with latest spdlog library
  • Refactor code with Google C++ Style Guide
  • Works only with stdout
  • It is not using std::recursive_mutex
  • It does not require any modification of spdlog library. Logging sink is based on spdlog::sinks::ansicolor_sink

Benchmarks

Below are some benchmarks done in Ubuntu 64 bit, Intel i5-8250U CPU @ 3.4GHz. I have compared progress monitor to native spdlog::sinks::ansicolor_sink. Used spdlog benchmark code.

Synchronous mode

[info] **************************************************************
[info] Single thread, 250,000 iterations
[info] **************************************************************
[spdlog::ansicolor_stdout_sink_mt]  [info]                Elapsed: 6.93 secs            36,083/sec
[Progress logger]                   [info]                Elapsed: 19.55 secs           12,788/sec
[Progress logger + monitor]         [info]                Elapsed: 27.51 secs            9,086/sec
[info] **************************************************************
[info] 10 threads, competing over the same logger object, 250,000 iterations
[info] **************************************************************
[spdlog::ansicolor_stdout_sink_mt]  [info]                Elapsed: 7.00 secs            35,703/sec
[Progress logger]                   [info]                Elapsed: 21.73 secs           11,507/sec
[Progress logger + monitor]         [info]                Elapsed: 29.70 secs            8,416/sec

Example usage

/*
 * Example usage code
*/

#include <thread>
#include <future>
#include "spdlog/sinks/stdout_sinks.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdmon/spdmon.hpp"

int main()
{
  /*
   * Create logger with many sinks. It can include stdout sinks
  */
  std::vector<spdlog::sink_ptr> sinks;
  sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>());
  sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>("logfile", true));
  auto combined_logger = std::make_shared<spdlog::logger>("name", begin(sinks), end(sinks));

  /*
   * Create progress logger. It checks for all stdout sinks and discard them
   * Instead of them it creates custom one, logging to stdout with progress bar
   * and makes it global default logger.
  */
  spdmon::LoggerProgress monitor(combined_logger, "Progress", 40);

  /* We can get newly created logger from progress logger to log new data */
  auto new_logger = monitor.GetLogger();
  new_logger->set_pattern("[thread %t] %+");
  new_logger->set_level(spdlog::level::trace);

  /*
   * We use lambda function to be passed to different logging threads
  */
  auto print_lambda = [&monitor, &new_logger](auto duration, int count) {
    for (int i = 0; i < count; i++)
    {
      spdlog::trace("Hi trace");
      spdlog::debug("Hi debug");
      spdlog::info("Hi info");
      new_logger->warn("Hi warn");
      new_logger->error("Hi err");
      new_logger->critical("Hi critical");

      ++monitor;

      std::this_thread::sleep_for(duration);
    }
  };

  using namespace std::chrono_literals;

  /*
   * Create few threads which will be logging data through progress logger
   * and global logger
  */
  std::vector<std::thread> threads;
  threads.push_back(std::thread{print_lambda, 250ms, 10});
  threads.push_back(std::thread{print_lambda, 750ms, 10});
  threads.push_back(std::thread{print_lambda, 350ms, 10});
  threads.push_back(std::thread{print_lambda, 500ms, 10});

  /*
   * Wait for threads to finish their jobs
  */
  for (auto &t : threads)
  {
    if (t.joinable())
      t.join();
  }

  return 0;
}

Authors

Huge thank you to Elmar Pruesse for the idea and starting this project!

Releases

No releases published

Packages

No packages published