diff --git a/LogManager.cpp b/LogManager.cpp new file mode 100644 index 00000000..1bedcbe3 --- /dev/null +++ b/LogManager.cpp @@ -0,0 +1,246 @@ +#include "LogManager.h" + +#include +#include "ResourceManager.h" + +#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING +#include +namespace fs = std::experimental::filesystem; + +LogManager::LogManager() +{ +} + +LogManager* LogManager::get() +{ + static LogManager* _instance = nullptr; + static std::mutex instance_mutex; + std::lock_guard grd(instance_mutex); + + /*-------------------------------------------------*\ + | Create a new instance if one does not exist | + \*-------------------------------------------------*/ + if(!_instance) + { + _instance = new LogManager(); + } + + return _instance; +} + +void LogManager::configure(json config, const std::string &defaultDir) +{ + std::lock_guard grd(entry_mutex); + + /*-------------------------------------------------*\ + | If the log is not open, create a new log file | + \*-------------------------------------------------*/ + if(!log_stream.is_open()) + { + std::string logname = "OpenRGB_#.log"; + + /*-------------------------------------------------*\ + | If the logfile is defined in the configuration, | + | use the configured name | + \*-------------------------------------------------*/ + if(config.contains("logfile")) + { + const json& logfile_obj = config["logfile"]; + if(logfile_obj.is_string()) + { + std::string tmpname = config["logfile"]; + if(!tmpname.empty()) + { + logname = tmpname; + } + } + } + + /*-------------------------------------------------*\ + | If the # symbol is found in the log file name, | + | replace it with a timestamp | + \*-------------------------------------------------*/ + size_t oct = logname.find("#"); + if(oct != logname.npos) + { + time_t t = time(0); + struct tm* tmp = localtime(&t); + char buf[64]; + snprintf(buf, 64, "%04d%02d%02d_%02d%02d%02d", 1900 + tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); + logname.replace(oct, 1, buf); + } + + /*-------------------------------------------------*\ + | If the path is relative, use configuration dir | + \*-------------------------------------------------*/ + fs::path p = logname; + if(p.is_relative()) + { + p = defaultDir; + p.append(logname); + } + + /*-------------------------------------------------*\ + | Open the logfile | + \*-------------------------------------------------*/ + log_stream.open(p); + } + + /*-------------------------------------------------*\ + | Check loglevel configuration | + \*-------------------------------------------------*/ + if(config.contains("loglevel")) + { + const json& loglevel_obj = config["loglevel"]; + + /*-------------------------------------------------*\ + | If the log level is configured per section, set | + | loglevel for each section | + \*-------------------------------------------------*/ + if(loglevel_obj.is_object()) + { + for(size_t section = 0; section < sections.size(); ++section) + { + if(loglevel_obj.contains(sections[section])) + { + const json& val = loglevel_obj[sections[section]]; + + if(val.is_number_integer()) + { + loglevels[section] = val; + } + } + } + } + + /*-------------------------------------------------*\ + | If the log level is configured globally, set same | + | loglevel for each section | + \*-------------------------------------------------*/ + else if(loglevel_obj.is_number_integer()) + { + int l = loglevel_obj; + for(size_t section = 0; section < sections.size(); ++section) + { + loglevels[section] = l; + } + } + } + + /*-------------------------------------------------*\ + | Flush the log | + \*-------------------------------------------------*/ + _flush(); +} + +void LogManager::_flush() +{ + /*-------------------------------------------------*\ + | If the log is open, write out buffered messages | + \*-------------------------------------------------*/ + if(log_stream.is_open()) + { + for(size_t msg = 0; msg < temp_messages.size(); ++msg) + { + int sec = temp_sections[msg]; + if(temp_levels[msg] <= loglevels[sec]) + { + log_stream << temp_messages[msg] << std::endl; + } + } + + /*-------------------------------------------------*\ + | Clear temp message buffers after writing them out | + \*-------------------------------------------------*/ + temp_messages.clear(); + temp_levels.clear(); + temp_sections.clear(); + } + + /*-------------------------------------------------*\ + | Flush the stream | + \*-------------------------------------------------*/ + log_stream.flush(); +} + +void LogManager::flush() +{ + std::lock_guard grd(entry_mutex); + _flush(); +} + +void LogManager::append(int section, int level, const char* fmt, ...) +{ + std::lock_guard grd(entry_mutex); + + char buf[1024]; + + /*-------------------------------------------------*\ + | Start the variable argument list | + \*-------------------------------------------------*/ + va_list va; + va_start(va, fmt); + + /*-------------------------------------------------*\ + | Return if the log is already open | + \*-------------------------------------------------*/ + if(!log_stream.is_open() && level > loglevels[section]) + { + return; + } + + /*-------------------------------------------------*\ + | Print the section to the log entry | + \*-------------------------------------------------*/ + int off = sprintf(buf, "[%s]: ", sections[section].c_str()); + + /*-------------------------------------------------*\ + | Print the log text to the log entry | + \*-------------------------------------------------*/ + vsnprintf(buf + off, 1024 - off, fmt, va); + + /*-------------------------------------------------*\ + | Write the log entry | + \*-------------------------------------------------*/ + if(log_stream.is_open()) + { + log_stream << buf << std::endl; + } + else + { + temp_levels.push_back(level); + temp_messages.push_back(buf); + temp_sections.push_back(section); + } + + /*-------------------------------------------------*\ + | End the variable argument list | + \*-------------------------------------------------*/ + va_end(va); +} + +int LogManager::registerSection(const char* name, int loglevel) +{ + std::lock_guard grd(section_mutex); + size_t section; + + /*-------------------------------------------------*\ + | Check to see if section already exists, if so, | + | return the existing section value | + \*-------------------------------------------------*/ + for(section = 0; section < sections.size(); section++) + { + if(sections[section] == name) + { + return section; + } + } + + /*-------------------------------------------------*\ + | If section does not already exist, create it | + \*-------------------------------------------------*/ + sections.push_back(name); + loglevels.push_back(loglevel); + + return section; +} diff --git a/LogManager.h b/LogManager.h new file mode 100644 index 00000000..bdbd4ee7 --- /dev/null +++ b/LogManager.h @@ -0,0 +1,49 @@ +#ifndef LOGMANAGER_H +#define LOGMANAGER_H + +#include +#include +#include +#include "json.hpp" + +using json = nlohmann::json; + +enum +{ + LL_CRITICAL, + LL_ERROR, + LL_WARNING, + LL_NOTICE, + LL_VERBOSE, + LL_DEBUG +}; + +class LogManager +{ +private: + LogManager(); + LogManager(const LogManager&) = delete; + LogManager(LogManager&&) = delete; + ~LogManager(); + std::mutex entry_mutex; + std::mutex section_mutex; + std::vector sections; + std::vector loglevels; + std::ofstream log_stream; + std::vector temp_messages; + std::vector temp_levels; + std::vector temp_sections; + void _flush(); + +public: + static LogManager* get(); + void configure(json config, const std::string& defaultDir); + void flush(); + void append(int section, int level, const char* fmt, ...); + int registerSection(const char* name, int loglevel); +}; + +#define LogSection(name,level) LogManager::get()->registerSection(name, level) +#define LogAppend LogManager::get()->append + +#endif // LOGMANAGER_H diff --git a/OpenRGB.pro b/OpenRGB.pro index 8eb3c03f..27369844 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -116,6 +116,7 @@ INCLUDEPATH += HEADERS += \ dependencies/ColorWheel/ColorWheel.h \ dependencies/json/json.hpp \ + LogManager.h \ NetworkClient.h \ NetworkProtocol.h \ NetworkServer.h \ @@ -329,6 +330,7 @@ SOURCES += dependencies/libe131/src/e131.c \ main.cpp \ cli.cpp \ + LogManager.cpp \ NetworkClient.cpp \ NetworkServer.cpp \ PluginManager.cpp \ diff --git a/ResourceManager.cpp b/ResourceManager.cpp index f240d2c7..e5ac6e29 100644 --- a/ResourceManager.cpp +++ b/ResourceManager.cpp @@ -11,6 +11,7 @@ #include "ResourceManager.h" #include "ProfileManager.h" +#include "LogManager.h" #define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING #include @@ -59,6 +60,11 @@ ResourceManager::ResourceManager() \*-------------------------------------------------------------------------*/ settings_manager = new SettingsManager(); settings_manager->LoadSettings(GetConfigurationDirectory() + "OpenRGB.json"); + + /*-------------------------------------------------------------------------*\ + | Configure the log manager | + \*-------------------------------------------------------------------------*/ + LogManager::get()->configure(settings_manager->GetSettings("Client"), GetConfigurationDirectory()); } ResourceManager::~ResourceManager() @@ -78,6 +84,7 @@ std::vector & ResourceManager::GetI2CBusses() void ResourceManager::RegisterRGBController(RGBController *rgb_controller) { + LogAppend(LogSection("Detection", LL_DEBUG), LL_DEBUG, "Registering RGB controller: %s", rgb_controller->name.c_str()); rgb_controllers_hw.push_back(rgb_controller); DeviceListChanged(); @@ -424,6 +431,14 @@ void ResourceManager::DetectDevicesThreadFunction() unsigned int prev_count = 0; bool save_settings = false; std::vector size_used; + int detection_section; + + /*-------------------------------------------------*\ + | Create a log debug section named Detection | + \*-------------------------------------------------*/ + detection_section = LogSection("Detection", LL_DEBUG); + + LogAppend(detection_section, LL_NOTICE, "Detection started" ); size_used.resize(rgb_controllers_sizes.size()); @@ -478,6 +493,8 @@ void ResourceManager::DetectDevicesThreadFunction() /*-------------------------------------------------*\ | Detect i2c busses | \*-------------------------------------------------*/ + LogAppend(detection_section, LL_DEBUG, "Detecting I2C/SMBus busses"); + for(unsigned int i2c_bus_detector_idx = 0; i2c_bus_detector_idx < i2c_bus_detectors.size() && detection_is_required.load(); i2c_bus_detector_idx++) { i2c_bus_detectors[i2c_bus_detector_idx](busses); @@ -487,6 +504,8 @@ void ResourceManager::DetectDevicesThreadFunction() /*-------------------------------------------------*\ | Detect i2c devices | \*-------------------------------------------------*/ + LogAppend(detection_section, LL_DEBUG, "Detecting I2C/SMBus devices"); + for(unsigned int i2c_detector_idx = 0; i2c_detector_idx < i2c_device_detectors.size() && detection_is_required.load(); i2c_detector_idx++) { detection_string = i2c_device_detector_strings[i2c_detector_idx].c_str(); @@ -563,6 +582,8 @@ void ResourceManager::DetectDevicesThreadFunction() if(hid_safe_mode) { + LogAppend(detection_section, LL_NOTICE, "Detecting HID devices in safe mode"); + /*-----------------------------------------------------------------------------*\ | Loop through all available detectors. If all required information matches, | | run the detector | @@ -571,6 +592,8 @@ void ResourceManager::DetectDevicesThreadFunction() { hid_devices = hid_enumerate(hid_device_detectors[hid_detector_idx].address >> 16, hid_device_detectors[hid_detector_idx].address & 0x0000FFFF); + LogAppend(detection_section, LL_NOTICE, "Trying to run detector for [%s] (for 0x%08hx)", hid_device_detectors[hid_detector_idx].name.c_str(), hid_device_detectors[hid_detector_idx].address); + current_hid_device = hid_devices; while(current_hid_device) @@ -639,6 +662,8 @@ void ResourceManager::DetectDevicesThreadFunction() } else { + LogAppend(detection_section, LL_NOTICE, "Detecting HID devices"); + /*-------------------------------------------------*\ | Iterate through all devices in list and run | | detectors | @@ -719,6 +744,8 @@ void ResourceManager::DetectDevicesThreadFunction() /*-------------------------------------------------*\ | Detect other devices | \*-------------------------------------------------*/ + LogAppend(detection_section, LL_NOTICE, "Detecting other devices"); + for(unsigned int detector_idx = 0; detector_idx < device_detectors.size() && detection_is_required.load(); detector_idx++) { detection_string = device_detector_strings[detector_idx].c_str(); @@ -775,7 +802,6 @@ void ResourceManager::DetectDevicesThreadFunction() | Make sure that when the detection is done, | | progress bar is set to 100% | \*-------------------------------------------------*/ - detection_is_required = false; detection_percent = 100; detection_string = ""; @@ -786,10 +812,14 @@ void ResourceManager::DetectDevicesThreadFunction() if(save_settings) { + LogAppend(detection_section, LL_NOTICE, "Saving detector settings"); + settings_manager->SetSettings("Detectors", detector_settings); settings_manager->SaveSettings(); } + + LogAppend(detection_section, LL_NOTICE, "Detection completed"); } void ResourceManager::StopDeviceDetection()