PageRenderTime 57ms CodeModel.GetById 2ms app.highlight 51ms RepoModel.GetById 1ms app.codeStats 1ms

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