2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
11 #include "CompileInfo.h"
12 #include "ServiceBroker.h"
13 #include "filesystem/File.h"
14 #include "guilib/LocalizeStrings.h"
15 #include "settings/SettingUtils.h"
16 #include "settings/Settings.h"
17 #include "settings/SettingsComponent.h"
18 #include "settings/lib/Setting.h"
19 #include "settings/lib/SettingsManager.h"
20 #include "utils/StringUtils.h"
21 #include "utils/URIUtils.h"
26 #include <spdlog/sinks/basic_file_sink.h>
27 #include <spdlog/sinks/dist_sink.h>
28 #include <spdlog/sinks/dup_filter_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";
38 : m_platform(IPlatformLog::CreatePlatformLog()),
39 m_sinks(std::make_shared
<spdlog::sinks::dist_sink_mt
>()),
40 m_defaultLogger(CreateLogger("general")),
41 m_logLevel(LOG_LEVEL_DEBUG
),
42 m_componentLogEnabled(false),
43 m_componentLogLevels(0)
45 // add platform-specific debug sinks
46 m_platform
->AddSinks(m_sinks
);
48 // register the default logger with spdlog
49 spdlog::set_default_logger(m_defaultLogger
);
51 // set the formatting pattern globally
52 spdlog::set_pattern(LogPattern
);
54 // flush on debug logs
55 spdlog::flush_on(spdlog::level::debug
);
58 SetLogLevel(m_logLevel
);
63 spdlog::drop("general");
66 void CLog::OnSettingsLoaded()
68 const std::shared_ptr
<CSettings
> settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
69 m_componentLogEnabled
= settings
->GetBool(CSettings::SETTING_DEBUG_EXTRALOGGING
);
70 SetComponentLogLevel(settings
->GetList(CSettings::SETTING_DEBUG_SETEXTRALOGLEVEL
));
73 void CLog::OnSettingChanged(const std::shared_ptr
<const CSetting
>& setting
)
78 const std::string
& settingId
= setting
->GetId();
79 if (settingId
== CSettings::SETTING_DEBUG_EXTRALOGGING
)
80 m_componentLogEnabled
= std::static_pointer_cast
<const CSettingBool
>(setting
)->GetValue();
81 else if (settingId
== CSettings::SETTING_DEBUG_SETEXTRALOGLEVEL
)
83 CSettingUtils::GetList(std::static_pointer_cast
<const CSettingList
>(setting
)));
86 void CLog::Initialize(const std::string
& path
)
88 if (m_fileSink
!= nullptr)
91 // register setting callbacks
92 auto settingsManager
=
93 CServiceBroker::GetSettingsComponent()->GetSettings()->GetSettingsManager();
94 settingsManager
->RegisterSettingOptionsFiller("loggingcomponents",
95 SettingOptionsLoggingComponentsFiller
);
96 settingsManager
->RegisterSettingsHandler(this);
97 std::set
<std::string
> settingSet
;
98 settingSet
.insert(CSettings::SETTING_DEBUG_EXTRALOGGING
);
99 settingSet
.insert(CSettings::SETTING_DEBUG_SETEXTRALOGLEVEL
);
100 settingsManager
->RegisterCallback(this, settingSet
);
105 // put together the path to the log file(s)
106 std::string appName
= CCompileInfo::GetAppName();
107 StringUtils::ToLower(appName
);
108 const std::string filePathBase
= URIUtils::AddFileToFolder(path
, appName
);
109 const std::string filePath
= filePathBase
+ LogFileExtension
;
110 const std::string oldFilePath
= filePathBase
+ ".old" + LogFileExtension
;
112 // handle old.log by deleting an existing old.log and renaming the last log to old.log
113 XFILE::CFile::Delete(oldFilePath
);
114 XFILE::CFile::Rename(filePath
, oldFilePath
);
119 if (file
.OpenForWrite(filePath
, true))
120 file
.Write(Utf8Bom
, sizeof(Utf8Bom
));
123 // create the file sink within a duplicate filter sink
124 auto duplicateFilterSink
=
125 std::make_shared
<spdlog::sinks::dup_filter_sink_st
>(std::chrono::seconds(10));
126 auto basicFileSink
= std::make_shared
<spdlog::sinks::basic_file_sink_st
>(
127 m_platform
->GetLogFilename(filePath
), false);
128 basicFileSink
->set_pattern(LogPattern
);
129 duplicateFilterSink
->add_sink(basicFileSink
);
130 m_fileSink
= duplicateFilterSink
;
132 // add it to the existing sinks
133 m_sinks
->add_sink(m_fileSink
);
136 void CLog::UnregisterFromSettings()
138 // unregister setting callbacks
139 auto settingsManager
=
140 CServiceBroker::GetSettingsComponent()->GetSettings()->GetSettingsManager();
141 settingsManager
->UnregisterSettingOptionsFiller("loggingcomponents");
142 settingsManager
->UnregisterSettingsHandler(this);
143 settingsManager
->UnregisterCallback(this);
146 void CLog::Deinitialize()
148 if (m_fileSink
== nullptr)
152 spdlog::apply_all([](const std::shared_ptr
<spdlog::logger
>& logger
) { logger
->flush(); });
154 // flush the file sink
157 // remove and destroy the file sink
158 m_sinks
->remove_sink(m_fileSink
);
162 void CLog::SetLogLevel(int level
)
164 if (level
< LOG_LEVEL_NONE
|| level
> LOG_LEVEL_MAX
)
169 auto spdLevel
= spdlog::level::info
;
170 if (level
<= LOG_LEVEL_NONE
)
171 spdLevel
= spdlog::level::off
;
172 else if (level
>= LOG_LEVEL_DEBUG
)
173 spdLevel
= spdlog::level::trace
;
175 if (m_defaultLogger
!= nullptr && m_defaultLogger
->level() == spdLevel
)
178 spdlog::set_level(spdLevel
);
179 FormatAndLogInternal(spdlog::level::info
, "Log level changed to \"{}\"",
180 spdlog::level::to_string_view(spdLevel
));
183 bool CLog::IsLogLevelLogged(int loglevel
)
185 if (m_logLevel
>= LOG_LEVEL_DEBUG
)
187 if (m_logLevel
<= LOG_LEVEL_NONE
)
190 return (loglevel
& LOGMASK
) >= LOGINFO
;
193 bool CLog::CanLogComponent(uint32_t component
) const
195 if (!m_componentLogEnabled
|| component
== 0)
198 return ((m_componentLogLevels
& component
) == component
);
201 void CLog::SettingOptionsLoggingComponentsFiller(const SettingConstPtr
& setting
,
202 std::vector
<IntegerSettingOption
>& list
,
206 list
.emplace_back(g_localizeStrings
.Get(669), LOGSAMBA
);
207 list
.emplace_back(g_localizeStrings
.Get(670), LOGCURL
);
208 list
.emplace_back(g_localizeStrings
.Get(672), LOGFFMPEG
);
209 list
.emplace_back(g_localizeStrings
.Get(675), LOGJSONRPC
);
210 list
.emplace_back(g_localizeStrings
.Get(676), LOGAUDIO
);
211 list
.emplace_back(g_localizeStrings
.Get(680), LOGVIDEO
);
212 list
.emplace_back(g_localizeStrings
.Get(683), LOGAVTIMING
);
213 list
.emplace_back(g_localizeStrings
.Get(684), LOGWINDOWING
);
214 list
.emplace_back(g_localizeStrings
.Get(685), LOGPVR
);
215 list
.emplace_back(g_localizeStrings
.Get(686), LOGEPG
);
216 list
.emplace_back(g_localizeStrings
.Get(39117), LOGANNOUNCE
);
217 list
.emplace_back(g_localizeStrings
.Get(39124), LOGADDONS
);
219 list
.emplace_back(g_localizeStrings
.Get(674), LOGDBUS
);
221 #ifdef HAS_WEB_SERVER
222 list
.emplace_back(g_localizeStrings
.Get(681), LOGWEBSERVER
);
225 list
.emplace_back(g_localizeStrings
.Get(677), LOGAIRTUNES
);
228 list
.emplace_back(g_localizeStrings
.Get(678), LOGUPNP
);
231 list
.emplace_back(g_localizeStrings
.Get(679), LOGCEC
);
233 list
.emplace_back(g_localizeStrings
.Get(682), LOGDATABASE
);
234 #if defined(HAS_FILESYSTEM_SMB)
235 list
.emplace_back(g_localizeStrings
.Get(37050), LOGWSDISCOVERY
);
239 Logger
CLog::GetLogger(const std::string
& loggerName
)
241 auto logger
= spdlog::get(loggerName
);
242 if (logger
== nullptr)
243 logger
= CreateLogger(loggerName
);
248 CLog
& CLog::GetInstance()
250 return CServiceBroker::GetLogging();
253 spdlog::level::level_enum
CLog::MapLogLevel(int level
)
258 return spdlog::level::debug
;
260 return spdlog::level::info
;
262 return spdlog::level::warn
;
264 return spdlog::level::err
;
266 return spdlog::level::critical
;
268 return spdlog::level::off
;
274 return spdlog::level::info
;
277 Logger
CLog::CreateLogger(const std::string
& loggerName
)
280 auto logger
= std::make_shared
<spdlog::logger
>(loggerName
, m_sinks
);
282 // initialize the logger
283 spdlog::initialize_logger(logger
);
288 void CLog::SetComponentLogLevel(const std::vector
<CVariant
>& components
)
290 m_componentLogLevels
= 0;
291 for (const auto& component
: components
)
293 if (!component
.isInteger())
296 m_componentLogLevels
|= static_cast<uint32_t>(component
.asInteger());
300 void CLog::FormatLineBreaks(std::string
& message
)
302 StringUtils::Replace(message
, "\n", "\n ");