/** * @file src/logging.h * @brief Declarations for logging related functions. */ #pragma once // lib includes #include #include using text_sink = boost::log::sinks::asynchronous_sink; extern boost::log::sources::severity_logger verbose; extern boost::log::sources::severity_logger debug; extern boost::log::sources::severity_logger info; extern boost::log::sources::severity_logger warning; extern boost::log::sources::severity_logger error; extern boost::log::sources::severity_logger fatal; #ifdef SUNSHINE_TESTS extern boost::log::sources::severity_logger tests; #endif #include "config.h" #include "stat_trackers.h" /** * @brief Handles the initialization and deinitialization of the logging system. */ namespace logging { class deinit_t { public: /** * @brief A destructor that restores the initial state. */ ~deinit_t(); }; /** * @brief Deinitialize the logging system. * @examples * deinit(); * @examples_end */ void deinit(); void formatter(const boost::log::record_view &view, boost::log::formatting_ostream &os); /** * @brief Initialize the logging system. * @param min_log_level The minimum log level to output. * @param log_file The log file to write to. * @return An object that will deinitialize the logging system when it goes out of scope. * @examples * log_init(2, "sunshine.log"); * @examples_end */ [[nodiscard]] std::unique_ptr init(int min_log_level, const std::string &log_file); /** * @brief Setup AV logging. * @param min_log_level The log level. */ void setup_av_logging(int min_log_level); /** * @brief Setup logging for libdisplaydevice. * @param min_log_level The log level. */ void setup_libdisplaydevice_logging(int min_log_level); /** * @brief Flush the log. * @examples * log_flush(); * @examples_end */ void log_flush(); /** * @brief Print help to stdout. * @param name The name of the program. * @examples * print_help("sunshine"); * @examples_end */ void print_help(const char *name); /** * @brief A helper class for tracking and logging numerical values across a period of time * @examples * min_max_avg_periodic_logger logger(debug, "Test time value", "ms", 5s); * logger.collect_and_log(1); * // ... * logger.collect_and_log(2); * // after 5 seconds * logger.collect_and_log(3); * // In the log: * // [2024:01:01:12:00:00]: Debug: Test time value (min/max/avg): 1ms/3ms/2.00ms * @examples_end */ template class min_max_avg_periodic_logger { public: min_max_avg_periodic_logger(boost::log::sources::severity_logger &severity, std::string_view message, std::string_view units, std::chrono::seconds interval_in_seconds = std::chrono::seconds(20)): severity(severity), message(message), units(units), interval(interval_in_seconds), enabled(config::sunshine.min_log_level <= severity.default_severity()) {} void collect_and_log(const T &value) { if (enabled) { auto print_info = [&](const T &min_value, const T &max_value, double avg_value) { auto f = stat_trackers::two_digits_after_decimal(); if constexpr (std::is_floating_point_v) { BOOST_LOG(severity.get()) << message << " (min/max/avg): " << f % min_value << units << "/" << f % max_value << units << "/" << f % avg_value << units; } else { BOOST_LOG(severity.get()) << message << " (min/max/avg): " << min_value << units << "/" << max_value << units << "/" << f % avg_value << units; } }; tracker.collect_and_callback_on_interval(value, print_info, interval); } } void collect_and_log(std::function func) { if (enabled) collect_and_log(func()); } void reset() { if (enabled) tracker.reset(); } bool is_enabled() const { return enabled; } private: std::reference_wrapper> severity; std::string message; std::string units; std::chrono::seconds interval; bool enabled; stat_trackers::min_max_avg_tracker tracker; }; /** * @brief A helper class for tracking and logging short time intervals across a period of time * @examples * time_delta_periodic_logger logger(debug, "Test duration", 5s); * logger.first_point_now(); * // ... * logger.second_point_now_and_log(); * // after 5 seconds * logger.first_point_now(); * // ... * logger.second_point_now_and_log(); * // In the log: * // [2024:01:01:12:00:00]: Debug: Test duration (min/max/avg): 1.23ms/3.21ms/2.31ms * @examples_end */ class time_delta_periodic_logger { public: time_delta_periodic_logger(boost::log::sources::severity_logger &severity, std::string_view message, std::chrono::seconds interval_in_seconds = std::chrono::seconds(20)): logger(severity, message, "ms", interval_in_seconds) {} void first_point(const std::chrono::steady_clock::time_point &point) { if (logger.is_enabled()) point1 = point; } void first_point_now() { if (logger.is_enabled()) first_point(std::chrono::steady_clock::now()); } void second_point_and_log(const std::chrono::steady_clock::time_point &point) { if (logger.is_enabled()) { logger.collect_and_log(std::chrono::duration(point - point1).count()); } } void second_point_now_and_log() { if (logger.is_enabled()) second_point_and_log(std::chrono::steady_clock::now()); } void reset() { if (logger.is_enabled()) logger.reset(); } bool is_enabled() const { return logger.is_enabled(); } private: std::chrono::steady_clock::time_point point1 = std::chrono::steady_clock::now(); min_max_avg_periodic_logger logger; }; /** * @brief Enclose string in square brackets. * @param input Input string. * @return Enclosed string. */ std::string bracket(const std::string &input); /** * @brief Enclose string in square brackets. * @param input Input string. * @return Enclosed string. */ std::wstring bracket(const std::wstring &input); } // namespace logging