Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions include/ygm/comm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,31 @@ class comm {
m_logger.set_log_level(level);
}

/**
* @brief Get the log level currently used in YGM
*
* @return Current log level
*/
log_level get_log_level() { return m_logger.get_log_level(); }

/**
* @brief Set the logger target to use in YGM
*
* @param target Logger target to use. Possible values are
* ygm::logger_target::file, ygm::logger_target::stdout, and
* ygm::logger_target::stderr
*/
void set_logger_target(const ygm::logger_target target) {
m_logger.set_logger_target(target);
}

/**
* @brief Get the logger target currently used in YGM
*
* @return Current logger target
*/
logger_target get_logger_target() { return m_logger.get_logger_target(); }

/**
* @brief Add a message to the YGM logs
*
Expand All @@ -278,9 +303,33 @@ class comm {
m_logger.log(level, args...);
}

/**
* @brief Add a message to the YGM logs written to multiple targets
*
* @tparam Args... Variadic types to add to log
* @param targets Vector of targets to write logs to
* @param Minimum log level for logging message
* @args Variadic arguments add to log
*/
template <typename... Args>
void log(const std::vector<logger_target> &targets,
const ygm::log_level level, Args &&...args) const {
m_logger.log(targets, level, args...);
}

/**
* @brief Set the log location to use when logging to files
*
* @param s Log location
*/
template <typename StringType>
void set_log_location(const StringType &s);

/**
* @brief Set the log location to use when logging to files
*
* @param p Log location
*/
void set_log_location(std::filesystem::path p);

// Private member functions
Expand Down
7 changes: 5 additions & 2 deletions include/ygm/detail/comm.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ struct comm::header_t {
inline comm::comm(int *argc, char ***argv)
: pimpl_if(std::make_shared<detail::mpi_init_finalize>(argc, argv)),
m_layout(MPI_COMM_WORLD),
m_router(m_layout, config.routing) {
m_router(m_layout, config.routing),
m_logger(m_layout.size()) {
// pimpl_if = std::make_shared<detail::mpi_init_finalize>(argc, argv);
comm_setup(MPI_COMM_WORLD);
}
Expand All @@ -48,7 +49,9 @@ inline comm::comm(int *argc, char ***argv)
* @return Constructed ygm::comm object
*/
inline comm::comm(MPI_Comm mcomm)
: m_layout(mcomm), m_router(m_layout, config.routing) {
: m_layout(mcomm),
m_router(m_layout, config.routing),
m_logger(m_layout.size()) {
pimpl_if.reset();
int flag(0);
YGM_ASSERT_MPI(MPI_Initialized(&flag));
Expand Down
127 changes: 113 additions & 14 deletions include/ygm/detail/logger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@

#pragma once

#include "spdlog/pattern_formatter.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/stdout_sinks.h"
#include "spdlog/spdlog.h"

#include <filesystem>
#include <iostream>

namespace ygm {

Expand All @@ -21,56 +24,152 @@ enum class log_level {
debug = 5
};

enum class logger_target { file, stdout, stderr };

namespace detail {

static std::vector<spdlog::level::level_enum> ygm_level_to_spdlog_level{
spdlog::level::level_enum::off, spdlog::level::level_enum::critical,
spdlog::level::level_enum::err, spdlog::level::level_enum::warn,
spdlog::level::level_enum::info, spdlog::level::level_enum::debug};

class rank_formatter_flag : public spdlog::custom_flag_formatter {
public:
rank_formatter_flag(const int rank) : m_rank(rank) {
m_rank_msg = std::string("Rank ") + std::to_string(m_rank);
}

void format(const spdlog::details::log_msg &, const std::tm &,
spdlog::memory_buf_t &dest) override {
dest.append(m_rank_msg.data(), m_rank_msg.data() + m_rank_msg.size());
}

std::unique_ptr<custom_flag_formatter> clone() const override {
return spdlog::details::make_unique<rank_formatter_flag>(m_rank);
}

private:
int m_rank;
std::string m_rank_msg;
};

/**
* @brief Simple logger for applications using YGM
*/
class logger {
public:
using rank_logger_t = spdlog::logger;
using rank_file_sink_t = spdlog::sinks::basic_file_sink_st;
using rank_cout_sink_t = spdlog::sinks::stdout_sink_st;
using rank_cerr_sink_t = spdlog::sinks::stderr_sink_st;

logger() : logger(std::filesystem::path("./log/")) {}
logger(const int rank) : logger(rank, std::filesystem::path("./log/")) {}

logger(const std::filesystem::path &path) : m_path(path) {
logger(const int rank, const std::filesystem::path &path)
: m_logger_target(logger_target::file),
m_cout_logger("ygm_cout_logger", std::make_shared<rank_cout_sink_t>()),
m_cerr_logger("ygm_cerr_logger", std::make_shared<rank_cerr_sink_t>()),
m_path(path) {
if (std::filesystem::is_directory(path)) {
m_path += "/ygm_logs";
}

// Set custom logging message format for stdout and stderr to include MPI
// rank
auto stdout_formatter = std::make_unique<spdlog::pattern_formatter>();
stdout_formatter->add_flag<rank_formatter_flag>('k', rank).set_pattern(
"[%Y-%m-%d %H:%M:%S.%e] [%n] [%l] [%k] %v");
m_cout_logger.set_formatter(std::move(stdout_formatter));

auto stderr_formatter = std::make_unique<spdlog::pattern_formatter>();
stderr_formatter->add_flag<rank_formatter_flag>('k', rank).set_pattern(
"[%Y-%m-%d %H:%M:%S.%e] [%n] [%l] [%k] %v");
m_cerr_logger.set_formatter(std::move(stderr_formatter));

// We will control logging levels
m_cout_logger.set_level(spdlog::level::trace);
m_cerr_logger.set_level(spdlog::level::trace);
}

void set_path(const std::filesystem::path p) {
m_path = p;

if (m_logger_ptr) {
m_logger_ptr.reset();
if (m_file_logger_ptr) {
m_file_logger_ptr.reset();
}
}

std::filesystem::path get_path() { return m_path; }

void set_log_level(const log_level level) { m_log_level = level; }

log_level get_log_level() { return m_log_level; }
log_level get_log_level() const { return m_log_level; }

void set_logger_target(const logger_target target) {
m_logger_target = target;
}

logger_target get_logger_target() const { return m_logger_target; }

template <typename... Args>
void log(const std::vector<logger_target> &targets, const log_level level,
Args &&...args) const {
for (const auto target : targets) {
log_impl(target, level, std::forward<Args>(args)...);
}
}

template <typename... Args>
void log(const log_level level, Args &&...args) const {
log(std::vector({m_logger_target}), level, std::forward<Args>(args)...);
}

/*
* @brief Force a flush of file-backed logs
*/
void flush() {
if (m_file_logger_ptr) {
m_file_logger_ptr->flush();
}
}

private:
template <typename... Args>
void log_impl(logger_target t, const log_level level, Args &&...args) const {
if (level > m_log_level) {
return;
}

if (not m_logger_ptr) {
std::filesystem::create_directories(m_path.parent_path());

m_logger_ptr = std::make_unique<spdlog::logger>(
"ygm_logger",
std::make_shared<rank_file_sink_t>(m_path.c_str(), false));
switch (t) {
case logger_target::file:
if (not m_file_logger_ptr) {
std::filesystem::create_directories(m_path.parent_path());

m_file_logger_ptr = std::make_unique<spdlog::logger>(
"ygm_file_logger",
std::make_shared<rank_file_sink_t>(m_path.c_str(), false));
m_file_logger_ptr->set_level(spdlog::level::trace);
}
m_file_logger_ptr->log(
ygm_level_to_spdlog_level[static_cast<size_t>(level)], args...);
break;

case logger_target::stdout:
m_cout_logger.log(ygm_level_to_spdlog_level[static_cast<size_t>(level)],
args...);
break;

case logger_target::stderr:
m_cerr_logger.log(ygm_level_to_spdlog_level[static_cast<size_t>(level)],
args...);
break;
}
m_logger_ptr->info(args...);
}

private:
mutable std::unique_ptr<rank_logger_t> m_logger_ptr;
logger_target m_logger_target;
mutable std::unique_ptr<rank_logger_t> m_file_logger_ptr;
mutable rank_logger_t m_cout_logger;
mutable rank_logger_t m_cerr_logger;

log_level m_log_level = log_level::off;

Expand Down
22 changes: 18 additions & 4 deletions test/test_logger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ int main() {
std::filesystem::path p("./test_log");
file_cleanup c(p);

ygm::detail::logger l(p);
ygm::detail::logger l(0, p);

YGM_ASSERT_RELEASE(std::filesystem::exists(l.get_path()) == false);

Expand All @@ -32,15 +32,17 @@ int main() {

l.set_log_level(ygm::log_level::info);
l.log(ygm::log_level::info, "Do log");
l.flush();
YGM_ASSERT_RELEASE(std::filesystem::exists(l.get_path()) == true);
YGM_ASSERT_RELEASE(std::filesystem::is_empty(l.get_path()) == false);
}

// Test logging level ordering
{
std::filesystem::path p("./test_log");
file_cleanup c(p);

ygm::detail::logger l(p);
ygm::detail::logger l(0, p);

YGM_ASSERT_RELEASE(std::filesystem::exists(l.get_path()) == false);

Expand All @@ -58,7 +60,7 @@ int main() {
std::filesystem::path p("./test_log");
file_cleanup c(p);

ygm::detail::logger l(p);
ygm::detail::logger l(0, p);

YGM_ASSERT_RELEASE(std::filesystem::exists(l.get_path()) == false);

Expand Down Expand Up @@ -86,7 +88,7 @@ int main() {
std::filesystem::path p("./test_log");
file_cleanup c(p);

ygm::detail::logger l(p);
ygm::detail::logger l(0, p);

YGM_ASSERT_RELEASE(std::filesystem::exists(l.get_path()) == false);

Expand All @@ -109,5 +111,17 @@ int main() {
YGM_ASSERT_RELEASE(std::filesystem::is_empty(p) == false);
}

// Test log files do not get created when logging to cout
{
std::filesystem::path p("./test_log");

ygm::detail::logger l(8, p);

l.set_logger_target(ygm::logger_target::stdout);
l.set_log_level(ygm::log_level::debug);
l.log(ygm::log_level::warn, "Log to stdout");
YGM_ASSERT_RELEASE(std::filesystem::exists(l.get_path()) == false);
}

return 0;
}