/xbmc/utils/log.cpp

http://github.com/xbmc/xbmc · C++ · 288 lines · 217 code · 47 blank · 24 comment · 34 complexity · f10288d6c66b2af5a5074f5119a58141 MD5 · raw file

  1. /*
  2. * Copyright (C) 2005-2018 Team Kodi
  3. * This file is part of Kodi - https://kodi.tv
  4. *
  5. * SPDX-License-Identifier: GPL-2.0-or-later
  6. * See LICENSES/README.md for more information.
  7. */
  8. #include "log.h"
  9. #include "CompileInfo.h"
  10. #include "ServiceBroker.h"
  11. #include "filesystem/File.h"
  12. #include "guilib/LocalizeStrings.h"
  13. #if defined(TARGET_ANDROID)
  14. #include "platform/android/utils/AndroidInterfaceForCLog.h"
  15. #elif defined(TARGET_DARWIN)
  16. #include "platform/darwin/utils/DarwinInterfaceForCLog.h"
  17. #elif defined(TARGET_WINDOWS) || defined(TARGET_WIN10)
  18. #include "platform/win32/utils/Win32InterfaceForCLog.h"
  19. #else
  20. #include "platform/posix/utils/PosixInterfaceForCLog.h"
  21. #endif
  22. #include "settings/SettingUtils.h"
  23. #include "settings/Settings.h"
  24. #include "settings/SettingsComponent.h"
  25. #include "settings/lib/Setting.h"
  26. #include "settings/lib/SettingsManager.h"
  27. #include "utils/URIUtils.h"
  28. #include <cstring>
  29. #include <set>
  30. #include <spdlog/sinks/basic_file_sink.h>
  31. #include <spdlog/sinks/dist_sink.h>
  32. static constexpr unsigned char Utf8Bom[3] = {0xEF, 0xBB, 0xBF};
  33. static const std::string LogFileExtension = ".log";
  34. static const std::string LogPattern = "%Y-%m-%d %T.%e T:%-5t %7l <%n>: %v";
  35. CLog::CLog()
  36. : m_platform(IPlatformLog::CreatePlatformLog()),
  37. m_sinks(std::make_shared<spdlog::sinks::dist_sink_mt>()),
  38. m_defaultLogger(CreateLogger("general")),
  39. m_logLevel(LOG_LEVEL_DEBUG),
  40. m_componentLogEnabled(false),
  41. m_componentLogLevels(0)
  42. {
  43. // add platform-specific debug sinks
  44. m_platform->AddSinks(m_sinks);
  45. // register the default logger with spdlog
  46. spdlog::set_default_logger(m_defaultLogger);
  47. // set the formatting pattern globally
  48. spdlog::set_pattern(LogPattern);
  49. // flush on debug logs
  50. spdlog::flush_on(spdlog::level::debug);
  51. // set the log level
  52. SetLogLevel(m_logLevel);
  53. }
  54. void CLog::OnSettingsLoaded()
  55. {
  56. const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
  57. m_componentLogEnabled = settings->GetBool(CSettings::SETTING_DEBUG_EXTRALOGGING);
  58. SetComponentLogLevel(settings->GetList(CSettings::SETTING_DEBUG_SETEXTRALOGLEVEL));
  59. }
  60. void CLog::OnSettingChanged(std::shared_ptr<const CSetting> setting)
  61. {
  62. if (setting == NULL)
  63. return;
  64. const std::string& settingId = setting->GetId();
  65. if (settingId == CSettings::SETTING_DEBUG_EXTRALOGGING)
  66. m_componentLogEnabled = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
  67. else if (settingId == CSettings::SETTING_DEBUG_SETEXTRALOGLEVEL)
  68. SetComponentLogLevel(
  69. CSettingUtils::GetList(std::static_pointer_cast<const CSettingList>(setting)));
  70. }
  71. void CLog::Initialize(const std::string& path)
  72. {
  73. if (m_fileSink != nullptr)
  74. return;
  75. // register setting callbacks
  76. auto settingsManager =
  77. CServiceBroker::GetSettingsComponent()->GetSettings()->GetSettingsManager();
  78. settingsManager->RegisterSettingOptionsFiller("loggingcomponents",
  79. SettingOptionsLoggingComponentsFiller);
  80. settingsManager->RegisterSettingsHandler(this);
  81. std::set<std::string> settingSet;
  82. settingSet.insert(CSettings::SETTING_DEBUG_EXTRALOGGING);
  83. settingSet.insert(CSettings::SETTING_DEBUG_SETEXTRALOGLEVEL);
  84. settingsManager->RegisterCallback(this, settingSet);
  85. if (path.empty())
  86. return;
  87. // put together the path to the log file(s)
  88. std::string appName = CCompileInfo::GetAppName();
  89. StringUtils::ToLower(appName);
  90. const std::string filePathBase = URIUtils::AddFileToFolder(path, appName);
  91. const std::string filePath = filePathBase + LogFileExtension;
  92. const std::string oldFilePath = filePathBase + ".old" + LogFileExtension;
  93. // handle old.log by deleting an existing old.log and renaming the last log to old.log
  94. XFILE::CFile::Delete(oldFilePath);
  95. XFILE::CFile::Rename(filePath, oldFilePath);
  96. // write UTF-8 BOM
  97. {
  98. XFILE::CFile file;
  99. if (file.OpenForWrite(filePath, true))
  100. file.Write(Utf8Bom, sizeof(Utf8Bom));
  101. }
  102. // create the file sink
  103. m_fileSink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(
  104. m_platform->GetLogFilename(filePath), false);
  105. m_fileSink->set_pattern(LogPattern);
  106. // add it to the existing sinks
  107. m_sinks->add_sink(m_fileSink);
  108. }
  109. void CLog::Uninitialize()
  110. {
  111. if (m_fileSink == nullptr)
  112. return;
  113. // unregister setting callbacks
  114. auto settingsManager =
  115. CServiceBroker::GetSettingsComponent()->GetSettings()->GetSettingsManager();
  116. settingsManager->UnregisterSettingOptionsFiller("loggingcomponents");
  117. settingsManager->UnregisterSettingsHandler(this);
  118. settingsManager->UnregisterCallback(this);
  119. // flush all loggers
  120. spdlog::apply_all([](std::shared_ptr<spdlog::logger> logger) { logger->flush(); });
  121. // flush the file sink
  122. m_fileSink->flush();
  123. // remove and destroy the file sink
  124. m_sinks->remove_sink(m_fileSink);
  125. m_fileSink.reset();
  126. }
  127. void CLog::SetLogLevel(int level)
  128. {
  129. if (level < LOG_LEVEL_NONE || level > LOG_LEVEL_MAX)
  130. return;
  131. m_logLevel = level;
  132. auto spdLevel = spdlog::level::info;
  133. if (level <= LOG_LEVEL_NONE)
  134. spdLevel = spdlog::level::off;
  135. else if (level >= LOG_LEVEL_DEBUG)
  136. spdLevel = spdlog::level::trace;
  137. if (m_defaultLogger != nullptr && m_defaultLogger->level() == spdLevel)
  138. return;
  139. spdlog::set_level(spdLevel);
  140. FormatAndLogInternal(spdlog::level::info, "Log level changed to \"{}\"",
  141. spdlog::level::to_string_view(spdLevel));
  142. }
  143. bool CLog::IsLogLevelLogged(int loglevel)
  144. {
  145. if (m_logLevel >= LOG_LEVEL_DEBUG)
  146. return true;
  147. if (m_logLevel <= LOG_LEVEL_NONE)
  148. return false;
  149. return (loglevel & LOGMASK) >= LOGNOTICE;
  150. }
  151. bool CLog::CanLogComponent(uint32_t component) const
  152. {
  153. if (!m_componentLogEnabled || component == 0)
  154. return false;
  155. return ((m_componentLogLevels & component) == component);
  156. }
  157. void CLog::SettingOptionsLoggingComponentsFiller(SettingConstPtr setting,
  158. std::vector<IntegerSettingOption>& list,
  159. int& current,
  160. void* data)
  161. {
  162. list.emplace_back(g_localizeStrings.Get(669), LOGSAMBA);
  163. list.emplace_back(g_localizeStrings.Get(670), LOGCURL);
  164. list.emplace_back(g_localizeStrings.Get(672), LOGFFMPEG);
  165. list.emplace_back(g_localizeStrings.Get(675), LOGJSONRPC);
  166. list.emplace_back(g_localizeStrings.Get(676), LOGAUDIO);
  167. list.emplace_back(g_localizeStrings.Get(680), LOGVIDEO);
  168. list.emplace_back(g_localizeStrings.Get(683), LOGAVTIMING);
  169. list.emplace_back(g_localizeStrings.Get(684), LOGWINDOWING);
  170. list.emplace_back(g_localizeStrings.Get(685), LOGPVR);
  171. list.emplace_back(g_localizeStrings.Get(686), LOGEPG);
  172. list.emplace_back(g_localizeStrings.Get(39117), LOGANNOUNCE);
  173. #ifdef HAS_DBUS
  174. list.emplace_back(g_localizeStrings.Get(674), LOGDBUS);
  175. #endif
  176. #ifdef HAS_WEB_SERVER
  177. list.emplace_back(g_localizeStrings.Get(681), LOGWEBSERVER);
  178. #endif
  179. #ifdef HAS_AIRTUNES
  180. list.emplace_back(g_localizeStrings.Get(677), LOGAIRTUNES);
  181. #endif
  182. #ifdef HAS_UPNP
  183. list.emplace_back(g_localizeStrings.Get(678), LOGUPNP);
  184. #endif
  185. #ifdef HAVE_LIBCEC
  186. list.emplace_back(g_localizeStrings.Get(679), LOGCEC);
  187. #endif
  188. list.emplace_back(g_localizeStrings.Get(682), LOGDATABASE);
  189. }
  190. Logger CLog::GetLogger(const std::string& loggerName)
  191. {
  192. auto logger = spdlog::get(loggerName);
  193. if (logger == nullptr)
  194. logger = CreateLogger(loggerName);
  195. return logger;
  196. }
  197. CLog& CLog::GetInstance()
  198. {
  199. return CServiceBroker::GetLogging();
  200. }
  201. spdlog::level::level_enum CLog::MapLogLevel(int level)
  202. {
  203. switch (level)
  204. {
  205. case LOGDEBUG:
  206. return spdlog::level::debug;
  207. case LOGINFO:
  208. case LOGNOTICE:
  209. return spdlog::level::info;
  210. case LOGWARNING:
  211. return spdlog::level::warn;
  212. case LOGERROR:
  213. return spdlog::level::err;
  214. case LOGSEVERE:
  215. case LOGFATAL:
  216. return spdlog::level::critical;
  217. case LOGNONE:
  218. return spdlog::level::off;
  219. default:
  220. break;
  221. }
  222. return spdlog::level::info;
  223. }
  224. Logger CLog::CreateLogger(const std::string& loggerName)
  225. {
  226. // create the logger
  227. auto logger = std::make_shared<spdlog::logger>(loggerName, m_sinks);
  228. // initialize the logger
  229. spdlog::initialize_logger(logger);
  230. return logger;
  231. }
  232. void CLog::SetComponentLogLevel(const std::vector<CVariant>& components)
  233. {
  234. m_componentLogLevels = 0;
  235. for (const auto& component : components)
  236. {
  237. if (!component.isInteger())
  238. continue;
  239. m_componentLogLevels |= static_cast<uint32_t>(component.asInteger());
  240. }
  241. }