Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add callback sink and default pattern access #1

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 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
36 changes: 36 additions & 0 deletions logger.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,17 @@ class null_sink_mt : public sink {
null_sink_mt();
};

typedef void (*log_callback_t)(int lvl, const char* msg);
typedef void (*flush_callback_t)();

/**
* @brief A sink that executes a callback whenever a message is logged.
*/
class callback_sink_mt : public sink {
public:
explicit callback_sink_mt(const log_callback_t &callback, const flush_callback_t &flush = nullptr);
};

/**
* @brief Returns the default log filename for the global logger.
*
Expand All @@ -440,6 +451,13 @@ inline std::string default_log_filename()
return (filename == nullptr) ? std::string{"@_RAPIDS_LOGGER_NAMESPACE@_log.txt"} : std::string{filename};
}

/**
* @brief Returns the default log pattern for the global logger.
*
* @return std::string The default log pattern.
*/
inline std::string default_pattern() { return "[%6t][%H:%M:%S:%f][%-6l] %v"; }

/**
* @brief Get the default logger.
*
Expand All @@ -463,6 +481,24 @@ inline logger& default_logger()
return logger_;
}

/**
* @brief An object used for scoped log level setting
*
* Instances will set the logging to the level indicated on construction and
* will revert to the previous set level on destruction.
*/
struct log_level_setter {
explicit log_level_setter(level_enum level)
{
prev_level_ = default_logger().level();
default_logger().set_level(level);
}
~log_level_setter() { default_logger().set_level(prev_level_); }

private:
level_enum prev_level_;
};

// Macros for easier logging, similar to spdlog.
#if !defined(@_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_ACTIVE_LEVEL)
#define @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_ACTIVE_LEVEL @_RAPIDS_LOGGER_MACRO_PREFIX@_LOG_LEVEL_INFO
Expand Down
46 changes: 45 additions & 1 deletion logger_impl.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,13 @@
#include <spdlog/sinks/null_sink.h>
#include <spdlog/sinks/ostream_sink.h>
#include <spdlog/spdlog.h>
#include <spdlog/details/log_msg.h>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to avoid this detail include?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately the officially documented way of creating custom loggers is to define overloads like this method that require this type. We could probably remove the include and rely on getting the type transitively, but we still have to access the type from the details namespace.

#include <spdlog/sinks/base_sink.h>
#pragma GCC diagnostic pop

#include <iostream>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>

Expand Down Expand Up @@ -113,7 +117,7 @@ private:
class logger_impl {
public:
logger_impl(std::string name) : underlying{spdlog::logger{name}} {
underlying.set_pattern("[%6t][%H:%M:%S:%f][%-6l] %v");
underlying.set_pattern(default_pattern());
auto const env_logging_level =
std::getenv("@_RAPIDS_LOGGER_MACRO_PREFIX@_DEFAULT_LOGGING_LEVEL");
if (env_logging_level != nullptr) { set_level(detail::string_to_level(env_logging_level)); }
Expand All @@ -136,6 +140,43 @@ private:
spdlog::logger underlying; ///< The spdlog logger
};

// Default flush function
void default_flush() { std::cout << std::flush; }

/**
* @brief A sink that calls a callback function with log messages.
*
* We do not currently use spdlog's callback sink because it does not support
* flushing. We could contribute that function to the upstream callback_sink_mt
* to simplify this code.
*/
template <class Mutex>
class callback_sink : public spdlog::sinks::base_sink<Mutex> {
public:
explicit callback_sink(log_callback_t callback,
flush_callback_t flush = nullptr)
: _callback{callback}, _flush{flush ? flush : default_flush} {}

protected:
void sink_it_(const spdlog::details::log_msg& msg) override
{
spdlog::memory_buf_t formatted;
spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
std::string msg_string = fmt::to_string(formatted);

if (_callback) {
_callback(static_cast<int>(msg.level), msg_string.c_str());
} else {
std::cout << msg_string;
}
}

void flush_() override { _flush();}

log_callback_t _callback;
void (*_flush)();
};

} // namespace detail

// Sink vector functions
Expand Down Expand Up @@ -170,6 +211,9 @@ ostream_sink_mt::ostream_sink_mt(std::ostream& stream, bool force_flush)
null_sink_mt::null_sink_mt()
: sink{std::make_unique<detail::sink_impl>(std::make_shared<spdlog::sinks::null_sink_mt>())} {}

callback_sink_mt::callback_sink_mt(const log_callback_t &callback, const flush_callback_t &flush)
: sink{std::make_unique<detail::sink_impl>(std::make_shared<detail::callback_sink<std::mutex>>(callback, flush))} {}

// Logger methods
logger::logger(std::string name, std::string filename)
: impl{std::make_unique<detail::logger_impl>(name)}, sinks_{*this} {
Expand Down