aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormontellese <montellese@kodi.tv>2020-03-14 10:59:34 +0100
committermontellese <montellese@kodi.tv>2020-04-16 20:05:39 +0200
commit91e7c78eb4cef1a60ce384c09403541fba0c74b0 (patch)
tree9ff14a84c2784bf5a0cff702020a473382093106
parentb97635702e3ddcaa8a27178d320cd2aadf147d8a (diff)
[utils] CLog: replace custom logging implementation with spdlog
-rw-r--r--cmake/treedata/android/subdirs.txt1
-rw-r--r--cmake/treedata/darwin_embedded/subdirs.txt1
-rw-r--r--cmake/treedata/osx/subdirs.txt1
-rw-r--r--xbmc/AppParamParser.cpp2
-rw-r--r--xbmc/Application.cpp8
-rw-r--r--xbmc/ServiceBroker.cpp12
-rw-r--r--xbmc/ServiceBroker.h5
-rw-r--r--xbmc/peripherals/devices/PeripheralCecAdapter.cpp2
-rw-r--r--xbmc/platform/android/utils/AndroidInterfaceForCLog.cpp26
-rw-r--r--xbmc/platform/android/utils/AndroidInterfaceForCLog.h22
-rw-r--r--xbmc/platform/android/utils/CMakeLists.txt5
-rw-r--r--xbmc/platform/darwin/DarwinUtils.h1
-rw-r--r--xbmc/platform/darwin/DarwinUtils.mm5
-rw-r--r--xbmc/platform/darwin/tvos/XBMCController.mm2
-rw-r--r--xbmc/platform/darwin/utils/CMakeLists.txt5
-rw-r--r--xbmc/platform/darwin/utils/DarwinInterfaceForCLog.h38
-rw-r--r--xbmc/platform/darwin/utils/DarwinInterfaceForCLog.mm58
-rw-r--r--xbmc/platform/posix/utils/PosixInterfaceForCLog.cpp100
-rw-r--r--xbmc/platform/posix/utils/PosixInterfaceForCLog.h25
-rw-r--r--xbmc/platform/win32/utils/Win32InterfaceForCLog.cpp111
-rw-r--r--xbmc/platform/win32/utils/Win32InterfaceForCLog.h25
-rw-r--r--xbmc/settings/AdvancedSettings.cpp8
-rw-r--r--xbmc/test/TestBasicEnvironment.cpp3
-rw-r--r--xbmc/utils/CMakeLists.txt2
-rw-r--r--xbmc/utils/IPlatformLog.h40
-rw-r--r--xbmc/utils/log.cpp296
-rw-r--r--xbmc/utils/log.h211
-rw-r--r--xbmc/utils/logtypes.h18
-rw-r--r--xbmc/utils/test/TestRegExp.cpp9
-rw-r--r--xbmc/utils/test/Testlog.cpp72
30 files changed, 599 insertions, 515 deletions
diff --git a/cmake/treedata/android/subdirs.txt b/cmake/treedata/android/subdirs.txt
index 57c89ea843..0c4cf88360 100644
--- a/cmake/treedata/android/subdirs.txt
+++ b/cmake/treedata/android/subdirs.txt
@@ -13,6 +13,7 @@ xbmc/platform/android/network platform/android/network
xbmc/platform/android/peripherals platform/android/peripherals
xbmc/platform/android/powermanagement platform/android/powermanagement
xbmc/platform/android/storage platform/android/storage
+xbmc/platform/android/utils platform/android/utils
xbmc/platform/linux/peripherals platform/linux/peripherals
xbmc/platform/posix platform/posix
xbmc/platform/posix/filesystem platform/posix/filesystem
diff --git a/cmake/treedata/darwin_embedded/subdirs.txt b/cmake/treedata/darwin_embedded/subdirs.txt
index d2f500ed15..be1dfc8d1b 100644
--- a/cmake/treedata/darwin_embedded/subdirs.txt
+++ b/cmake/treedata/darwin_embedded/subdirs.txt
@@ -7,6 +7,7 @@ xbmc/platform/darwin/ios-common platform/ios-common
xbmc/platform/darwin/ios-common/network platform/ios-common/network
xbmc/platform/darwin/ios-common/storage platform/ios-common/storage
xbmc/platform/darwin/network platform/darwin/network
+xbmc/platform/darwin/utils platform/darwin/utils
xbmc/platform/posix posix
xbmc/platform/posix/filesystem platform/posix/filesystem
xbmc/platform/posix/utils platform/posix/utils
diff --git a/cmake/treedata/osx/subdirs.txt b/cmake/treedata/osx/subdirs.txt
index f4785abbf1..fa28aa0efb 100644
--- a/cmake/treedata/osx/subdirs.txt
+++ b/cmake/treedata/osx/subdirs.txt
@@ -7,6 +7,7 @@ xbmc/platform/darwin/osx/network platform/darwin/osx/network
xbmc/platform/darwin/osx/peripherals platform/osx/peripherals
xbmc/platform/darwin/osx/powermanagement platform/darwin/osx/powermanagement
xbmc/platform/darwin/osx/storage platform/osx/storage
+xbmc/platform/darwin/utils platform/darwin/utils
xbmc/platform/posix posix
xbmc/platform/posix/filesystem platform/posix/filesystem
xbmc/platform/posix/network platform/posix/network
diff --git a/xbmc/AppParamParser.cpp b/xbmc/AppParamParser.cpp
index 6660b0a776..60ff9d821a 100644
--- a/xbmc/AppParamParser.cpp
+++ b/xbmc/AppParamParser.cpp
@@ -97,7 +97,7 @@ void CAppParamParser::SetAdvancedSettings(CAdvancedSettings& advancedSettings) c
{
advancedSettings.m_logLevel = LOG_LEVEL_DEBUG;
advancedSettings.m_logLevelHint = LOG_LEVEL_DEBUG;
- CLog::SetLogLevel(LOG_LEVEL_DEBUG);
+ CServiceBroker::GetLogging().SetLogLevel(LOG_LEVEL_DEBUG);
}
if (!m_settingsFile.empty())
diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp
index 5cb12e347c..5539c8e859 100644
--- a/xbmc/Application.cpp
+++ b/xbmc/Application.cpp
@@ -363,6 +363,8 @@ bool CApplication::Create(const CAppParamParser &params)
m_bTestMode = params.m_testmode;
m_bStandalone = params.m_standAlone;
+ CServiceBroker::CreateLogging();
+
CServiceBroker::RegisterCPUInfo(CCPUInfo::GetCPUInfo());
m_pSettingsComponent.reset(new CSettingsComponent());
@@ -420,11 +422,7 @@ bool CApplication::Create(const CAppParamParser &params)
CopyUserDataIfNeeded("special://masterprofile/", "iOS/sources.xml", "sources.xml");
#endif
- if (!CLog::Init(CSpecialProtocol::TranslatePath("special://logpath").c_str()))
- {
- fprintf(stderr,"Could not init logging classes. Log folder error (%s)\n", CSpecialProtocol::TranslatePath("special://logpath").c_str());
- return false;
- }
+ CServiceBroker::GetLogging().Initialize(CSpecialProtocol::TranslatePath("special://logpath"));
#ifdef TARGET_POSIX //! @todo Win32 has no special://home/ mapping by default, so we
//! must create these here. Ideally this should be using special://home/ and
diff --git a/xbmc/ServiceBroker.cpp b/xbmc/ServiceBroker.cpp
index 5d60cf8d23..a23061a45c 100644
--- a/xbmc/ServiceBroker.cpp
+++ b/xbmc/ServiceBroker.cpp
@@ -11,10 +11,22 @@
#include "Application.h"
#include "profiles/ProfileManager.h"
#include "settings/SettingsComponent.h"
+#include "utils/log.h"
#include "windowing/WinSystem.h"
using namespace KODI;
+std::unique_ptr<CLog> CServiceBroker::m_logging;
+CLog& CServiceBroker::GetLogging()
+{
+ return *m_logging;
+}
+
+void CServiceBroker::CreateLogging()
+{
+ m_logging = std::make_unique<CLog>();
+}
+
// announcement
std::shared_ptr<ANNOUNCEMENT::CAnnouncementManager> CServiceBroker::m_pAnnouncementManager;
std::shared_ptr<ANNOUNCEMENT::CAnnouncementManager> CServiceBroker::GetAnnouncementManager()
diff --git a/xbmc/ServiceBroker.h b/xbmc/ServiceBroker.h
index 8f5ca91c81..8a7844da95 100644
--- a/xbmc/ServiceBroker.h
+++ b/xbmc/ServiceBroker.h
@@ -55,6 +55,7 @@ class CSettingsComponent;
class CDecoderFilterManager;
class CMediaManager;
class CCPUInfo;
+class CLog;
namespace KODI
{
@@ -78,6 +79,9 @@ namespace PERIPHERALS
class CServiceBroker
{
public:
+ static CLog& GetLogging();
+ static void CreateLogging();
+
static std::shared_ptr<ANNOUNCEMENT::CAnnouncementManager> GetAnnouncementManager();
static void RegisterAnnouncementManager(std::shared_ptr<ANNOUNCEMENT::CAnnouncementManager> announcementManager);
static void UnregisterAnnouncementManager();
@@ -139,6 +143,7 @@ public:
static void UnregisterCPUInfo();
private:
+ static std::unique_ptr<CLog> m_logging;
static std::shared_ptr<ANNOUNCEMENT::CAnnouncementManager> m_pAnnouncementManager;
static CGUIComponent* m_pGUI;
static CWinSystemBase* m_pWinSystem;
diff --git a/xbmc/peripherals/devices/PeripheralCecAdapter.cpp b/xbmc/peripherals/devices/PeripheralCecAdapter.cpp
index 039acb10af..1505aaf73f 100644
--- a/xbmc/peripherals/devices/PeripheralCecAdapter.cpp
+++ b/xbmc/peripherals/devices/PeripheralCecAdapter.cpp
@@ -1206,7 +1206,7 @@ void CPeripheralCecAdapter::CecLogMessage(void *cbParam, const cec_log_message*
break;
}
- if (iLevel >= CEC_LOG_NOTICE || (iLevel >= 0 && CLog::IsLogLevelLogged(LOGDEBUG)))
+ if (iLevel >= CEC_LOG_NOTICE || (iLevel >= 0 && CServiceBroker::GetLogging().IsLogLevelLogged(LOGDEBUG)))
CLog::Log(iLevel, LOGCEC, "%s - %s", __FUNCTION__, message->message);
}
diff --git a/xbmc/platform/android/utils/AndroidInterfaceForCLog.cpp b/xbmc/platform/android/utils/AndroidInterfaceForCLog.cpp
new file mode 100644
index 0000000000..62644a5cff
--- /dev/null
+++ b/xbmc/platform/android/utils/AndroidInterfaceForCLog.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "platform/android/utils/AndroidInterfaceForCLog.h"
+
+#include "CompileInfo.h"
+
+#include <spdlog/sinks/android_sink.h>
+#include <spdlog/sinks/dist_sink.h>
+
+std::unique_ptr<IPlatformLog> IPlatformLog::CreatePlatformLog()
+{
+ return std::make_unique<CAndroidInterfaceForCLog>();
+}
+
+void CAndroidInterfaceForCLog::AddSinks(
+ std::shared_ptr<spdlog::sinks::dist_sink<std::mutex>> distributionSink) const
+{
+ distributionSink->add_sink(
+ std::make_shared<spdlog::sinks::android_sink_mt>(CCompileInfo::GetAppName()));
+}
diff --git a/xbmc/platform/android/utils/AndroidInterfaceForCLog.h b/xbmc/platform/android/utils/AndroidInterfaceForCLog.h
new file mode 100644
index 0000000000..eabc204e79
--- /dev/null
+++ b/xbmc/platform/android/utils/AndroidInterfaceForCLog.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "platform/posix/utils/PosixInterfaceForCLog.h"
+
+class CAndroidInterfaceForCLog : public CPosixInterfaceForCLog
+{
+public:
+ CAndroidInterfaceForCLog() = default;
+ ~CAndroidInterfaceForCLog() = default;
+
+ // specialization of CPosixInterfaceForCLog
+ void AddSinks(
+ std::shared_ptr<spdlog::sinks::dist_sink<std::mutex>> distributionSink) const override;
+};
diff --git a/xbmc/platform/android/utils/CMakeLists.txt b/xbmc/platform/android/utils/CMakeLists.txt
new file mode 100644
index 0000000000..c5f53082c3
--- /dev/null
+++ b/xbmc/platform/android/utils/CMakeLists.txt
@@ -0,0 +1,5 @@
+set(SOURCES AndroidInterfaceForCLog.cpp)
+
+set(HEADERS AndroidInterfaceForCLog.h)
+
+core_add_library(platform_android_utils)
diff --git a/xbmc/platform/darwin/DarwinUtils.h b/xbmc/platform/darwin/DarwinUtils.h
index b13e596b24..4f3db3557d 100644
--- a/xbmc/platform/darwin/DarwinUtils.h
+++ b/xbmc/platform/darwin/DarwinUtils.h
@@ -27,7 +27,6 @@ public:
static const char *GetAppRootFolder(void);
static bool IsIosSandboxed(void);
static void SetScheduling(bool realtime);
- static void PrintDebugString(std::string debugString);
static bool CFStringRefToString(CFStringRef source, std::string& destination);
static bool CFStringRefToUTF8String(CFStringRef source, std::string& destination);
static const std::string& GetManufacturer(void);
diff --git a/xbmc/platform/darwin/DarwinUtils.mm b/xbmc/platform/darwin/DarwinUtils.mm
index 08e6603ca2..8f9cd57e19 100644
--- a/xbmc/platform/darwin/DarwinUtils.mm
+++ b/xbmc/platform/darwin/DarwinUtils.mm
@@ -245,11 +245,6 @@ bool CFStringRefToStringWithEncoding(CFStringRef source, std::string &destinatio
return true;
}
-void CDarwinUtils::PrintDebugString(std::string debugString)
-{
- NSLog(@"Debug Print: %s", debugString.c_str());
-}
-
bool CDarwinUtils::CFStringRefToString(CFStringRef source, std::string &destination)
{
diff --git a/xbmc/platform/darwin/tvos/XBMCController.mm b/xbmc/platform/darwin/tvos/XBMCController.mm
index d6381513f7..588db3ee9c 100644
--- a/xbmc/platform/darwin/tvos/XBMCController.mm
+++ b/xbmc/platform/darwin/tvos/XBMCController.mm
@@ -421,7 +421,7 @@ int KODI_Run(bool renderGUI)
CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_logLevel = LOG_LEVEL_NORMAL;
CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_logLevelHint = LOG_LEVEL_NORMAL;
#endif
- CLog::SetLogLevel(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_logLevel);
+ CServiceBroker::GetLogging().SetLogLevel(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_logLevel);
// not a failure if returns false, just means someone
// did the init before us.
diff --git a/xbmc/platform/darwin/utils/CMakeLists.txt b/xbmc/platform/darwin/utils/CMakeLists.txt
new file mode 100644
index 0000000000..c2717d595c
--- /dev/null
+++ b/xbmc/platform/darwin/utils/CMakeLists.txt
@@ -0,0 +1,5 @@
+set(SOURCES DarwinInterfaceForCLog.mm)
+
+set(HEADERS DarwinInterfaceForCLog.h)
+
+core_add_library(platform_darwin_utils)
diff --git a/xbmc/platform/darwin/utils/DarwinInterfaceForCLog.h b/xbmc/platform/darwin/utils/DarwinInterfaceForCLog.h
new file mode 100644
index 0000000000..b6bf541e32
--- /dev/null
+++ b/xbmc/platform/darwin/utils/DarwinInterfaceForCLog.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "platform/posix/utils/PosixInterfaceForCLog.h"
+
+#include <memory>
+#include <mutex>
+
+#include <spdlog/formatter.h>
+#include <spdlog/sinks/sink.h>
+
+class CDarwinInterfaceForCLog : public CPosixInterfaceForCLog, public spdlog::sinks::sink
+{
+public:
+ CDarwinInterfaceForCLog();
+ ~CDarwinInterfaceForCLog() = default;
+
+ // specialization of CPosixInterfaceForCLog
+ void AddSinks(
+ std::shared_ptr<spdlog::sinks::dist_sink<std::mutex>> distributionSink) const override;
+
+ // implementations of spdlog::sink
+ void log(const spdlog::details::log_msg& msg) override;
+ void flush() override;
+ void set_pattern(const std::string& pattern) override;
+ void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
+
+private:
+ std::unique_ptr<spdlog::formatter> m_formatter;
+ std::mutex m_mutex;
+};
diff --git a/xbmc/platform/darwin/utils/DarwinInterfaceForCLog.mm b/xbmc/platform/darwin/utils/DarwinInterfaceForCLog.mm
new file mode 100644
index 0000000000..0d126af932
--- /dev/null
+++ b/xbmc/platform/darwin/utils/DarwinInterfaceForCLog.mm
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "DarwinInterfaceForCLog.h"
+
+#include <string>
+
+#import <Foundation/Foundation.h>
+#include <spdlog/common.h>
+#include <spdlog/details/pattern_formatter.h>
+#include <spdlog/sinks/dist_sink.h>
+
+std::unique_ptr<IPlatformLog> IPlatformLog::CreatePlatformLog()
+{
+ return std::make_unique<CDarwinInterfaceForCLog>();
+}
+
+CDarwinInterfaceForCLog::CDarwinInterfaceForCLog()
+ : m_formatter(std::make_unique<spdlog::pattern_formatter>())
+{
+}
+
+void CDarwinInterfaceForCLog::AddSinks(
+ std::shared_ptr<spdlog::sinks::dist_sink<std::mutex>> distributionSink) const
+{
+ distributionSink->add_sink(std::make_shared<CDarwinInterfaceForCLog>());
+}
+
+void CDarwinInterfaceForCLog::log(const spdlog::details::log_msg& msg)
+{
+ std::lock_guard<std::mutex> lock(m_mutex);
+ spdlog::memory_buf_t formatted;
+ m_formatter->format(msg, formatted);
+ formatted.push_back('\0');
+ NSLog(@"%s", formatted.data());
+}
+
+void CDarwinInterfaceForCLog::flush()
+{
+ std::lock_guard<std::mutex> lock(m_mutex);
+ fflush(stderr);
+}
+
+void CDarwinInterfaceForCLog::set_pattern(const std::string& pattern)
+{
+ set_formatter(std::make_unique<spdlog::pattern_formatter>(pattern));
+}
+
+void CDarwinInterfaceForCLog::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
+{
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_formatter = std::move(sink_formatter);
+}
diff --git a/xbmc/platform/posix/utils/PosixInterfaceForCLog.cpp b/xbmc/platform/posix/utils/PosixInterfaceForCLog.cpp
index ce4c5aa5b5..68c1ddb5ef 100644
--- a/xbmc/platform/posix/utils/PosixInterfaceForCLog.cpp
+++ b/xbmc/platform/posix/utils/PosixInterfaceForCLog.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014-2018 Team Kodi
+ * Copyright (C) 2020 Team Kodi
* This file is part of Kodi - https://kodi.tv
*
* SPDX-License-Identifier: GPL-2.0-or-later
@@ -7,100 +7,10 @@
*/
#include "PosixInterfaceForCLog.h"
-#include <stdio.h>
-#include <time.h>
-#include <sys/time.h>
-#if defined(TARGET_DARWIN)
-#include "platform/darwin/DarwinUtils.h"
-#elif defined(TARGET_ANDROID)
-#include "platform/android/activity/XBMCApp.h"
-#endif // TARGET_ANDROID
-
-struct FILEWRAP : public FILE
-{};
-
-
-CPosixInterfaceForCLog::CPosixInterfaceForCLog() :
- m_file(NULL)
-{ }
-
-CPosixInterfaceForCLog::~CPosixInterfaceForCLog()
-{
- if (m_file)
- fclose(m_file);
- m_file = NULL;
-}
-
-bool CPosixInterfaceForCLog::OpenLogFile(const std::string &logFilename, const std::string &backupOldLogToFilename)
-{
- if (m_file)
- return false; // file was already opened
-
- (void)remove(backupOldLogToFilename.c_str()); // if it's failed, try to continue
- (void)rename(logFilename.c_str(), backupOldLogToFilename.c_str()); // if it's failed, try to continue
-
- m_file = (FILEWRAP*)fopen(logFilename.c_str(), "wb");
- if (!m_file)
- return false; // error, can't open log file
-
- static const unsigned char BOM[3] = { 0xEF, 0xBB, 0xBF };
- (void)fwrite(BOM, sizeof(BOM), 1, m_file); // write BOM, ignore possible errors
-
- return true;
-}
-
-void CPosixInterfaceForCLog::CloseLogFile()
-{
- if (m_file)
- {
- fclose(m_file);
- m_file = NULL;
- }
-}
-
-bool CPosixInterfaceForCLog::WriteStringToLog(const std::string &logString)
+#if !defined(TARGET_ANDROID) && !defined(TARGET_DARWIN)
+std::unique_ptr<IPlatformLog> IPlatformLog::CreatePlatformLog()
{
- if (!m_file)
- return false;
-
- const bool ret = (fwrite(logString.data(), logString.size(), 1, m_file) == 1) &&
- (fwrite("\n", 1, 1, m_file) == 1);
- (void)fflush(m_file);
-
- return ret;
-}
-
-void CPosixInterfaceForCLog::PrintDebugString(const std::string &debugString)
-{
-#ifdef _DEBUG
-#if defined(TARGET_DARWIN)
- CDarwinUtils::PrintDebugString(debugString);
-#elif defined(TARGET_ANDROID)
- //print to adb
- CXBMCApp::android_printf("Debug Print: %s", debugString.c_str());
-#endif // TARGET_ANDROID
-#endif // _DEBUG
-}
-
-void CPosixInterfaceForCLog::GetCurrentLocalTime(int& year, int& month, int& day, int &hour, int& minute, int& second, double& milliseconds)
-{
- struct tm localTime;
- struct timeval tv;
-
- if (gettimeofday(&tv, nullptr) != -1 && localtime_r(&tv.tv_sec, &localTime) != NULL)
- {
- year = localTime.tm_year + 1900;
- month = localTime.tm_mon + 1;
- day = localTime.tm_mday;
- hour = localTime.tm_hour;
- minute = localTime.tm_min;
- second = localTime.tm_sec;
- milliseconds = static_cast<double>(tv.tv_usec) / 1000;
- }
- else
- {
- year = month = day = hour = minute = second = 0;
- milliseconds = 0.0;
- }
+ return std::make_unique<CPosixInterfaceForCLog>();
}
+#endif
diff --git a/xbmc/platform/posix/utils/PosixInterfaceForCLog.h b/xbmc/platform/posix/utils/PosixInterfaceForCLog.h
index 4e0f6c6646..48a016efb2 100644
--- a/xbmc/platform/posix/utils/PosixInterfaceForCLog.h
+++ b/xbmc/platform/posix/utils/PosixInterfaceForCLog.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014-2018 Team Kodi
+ * Copyright (C) 2020 Team Kodi
* This file is part of Kodi - https://kodi.tv
*
* SPDX-License-Identifier: GPL-2.0-or-later
@@ -8,20 +8,17 @@
#pragma once
-#include <string>
+#include "utils/IPlatformLog.h"
-struct FILEWRAP; // forward declaration, wrapper for FILE
-
-class CPosixInterfaceForCLog
+class CPosixInterfaceForCLog : public IPlatformLog
{
public:
- CPosixInterfaceForCLog();
- ~CPosixInterfaceForCLog();
- bool OpenLogFile(const std::string& logFilename, const std::string& backupOldLogToFilename);
- void CloseLogFile(void);
- bool WriteStringToLog(const std::string& logString);
- void PrintDebugString(const std::string& debugString);
- static void GetCurrentLocalTime(int& year, int& month, int& day, int& hour, int& minute, int& second, double& millisecond);
-private:
- FILEWRAP* m_file;
+ CPosixInterfaceForCLog() = default;
+ virtual ~CPosixInterfaceForCLog() = default;
+
+ spdlog_filename_t GetLogFilename(const std::string& filename) const override { return filename; }
+ void AddSinks(
+ std::shared_ptr<spdlog::sinks::dist_sink<std::mutex>> distributionSink) const override
+ {
+ }
};
diff --git a/xbmc/platform/win32/utils/Win32InterfaceForCLog.cpp b/xbmc/platform/win32/utils/Win32InterfaceForCLog.cpp
index 9eea2d0f49..3c055dafa0 100644
--- a/xbmc/platform/win32/utils/Win32InterfaceForCLog.cpp
+++ b/xbmc/platform/win32/utils/Win32InterfaceForCLog.cpp
@@ -1,119 +1,34 @@
/*
- * Copyright (C) 2014-2018 Team Kodi
+ * Copyright (C) 2020 Team Kodi
* This file is part of Kodi - https://kodi.tv
*
* SPDX-License-Identifier: GPL-2.0-or-later
* See LICENSES/README.md for more information.
*/
-#ifndef TARGET_WINDOWS
+#if !defined(TARGET_WINDOWS) && !defined(TARGET_WIN10)
#error This file is for win32 platforms only
-#endif //!TARGET_WINDOWS
+#endif // !defined(TARGET_WINDOWS) && !defined(TARGET_WIN10)
#include "Win32InterfaceForCLog.h"
-#include "platform/win32/WIN32Util.h"
-#include "utils/StringUtils.h"
-#include "utils/auto_buffer.h"
-
-#include <Windows.h>
-
-CWin32InterfaceForCLog::CWin32InterfaceForCLog() :
- m_hFile(INVALID_HANDLE_VALUE)
-{ }
-
-CWin32InterfaceForCLog::~CWin32InterfaceForCLog()
-{
- if (m_hFile != INVALID_HANDLE_VALUE)
- CloseHandle(m_hFile);
-}
-
-bool CWin32InterfaceForCLog::OpenLogFile(const std::string& logFilename, const std::string& backupOldLogToFilename)
-{
- if (m_hFile != INVALID_HANDLE_VALUE)
- return false; // file was already opened
-
- std::wstring strLogFileW(CWIN32Util::ConvertPathToWin32Form(CWIN32Util::SmbToUnc(logFilename)));
- std::wstring strLogFileOldW(CWIN32Util::ConvertPathToWin32Form(CWIN32Util::SmbToUnc(backupOldLogToFilename)));
-
- if (strLogFileW.empty())
- return false;
-
- if (!strLogFileOldW.empty())
- {
- (void)DeleteFileW(strLogFileOldW.c_str()); // if it's failed, try to continue
-#ifdef TARGET_WINDOWS_STORE
- (void)MoveFileEx(strLogFileW.c_str(), strLogFileOldW.c_str(), MOVEFILE_REPLACE_EXISTING); // if it's failed, try to continue
-#else
- (void)MoveFileW(strLogFileW.c_str(), strLogFileOldW.c_str()); // if it's failed, try to continue
-#endif
- }
-#ifdef TARGET_WINDOWS_STORE
- m_hFile = CreateFile2(strLogFileW.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
- CREATE_ALWAYS, NULL);
-#else
- m_hFile = CreateFileW(strLogFileW.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL,
- CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
-#endif
-
- if (m_hFile == INVALID_HANDLE_VALUE)
- return false;
-
- static const unsigned char BOM[3] = { 0xEF, 0xBB, 0xBF };
- DWORD written;
- (void)WriteFile(m_hFile, BOM, sizeof(BOM), &written, NULL); // write BOM, ignore possible errors
- (void)FlushFileBuffers(m_hFile);
-
- return true;
-}
+#include "platform/win32/WIN32Util.h"
-void CWin32InterfaceForCLog::CloseLogFile(void)
-{
- if (m_hFile != INVALID_HANDLE_VALUE)
- {
- CloseHandle(m_hFile);
- m_hFile = INVALID_HANDLE_VALUE;
- }
-}
+#include <spdlog/sinks/dist_sink.h>
+#include <spdlog/sinks/msvc_sink.h>
-bool CWin32InterfaceForCLog::WriteStringToLog(const std::string& logString)
+std::unique_ptr<IPlatformLog> IPlatformLog::CreatePlatformLog()
{
- if (m_hFile == INVALID_HANDLE_VALUE)
- return false;
-
- std::string strData(logString);
- StringUtils::Replace(strData, "\n", "\r\n");
- strData += "\r\n";
-
- DWORD written;
- const bool ret = (WriteFile(m_hFile, strData.c_str(), strData.length(), &written, NULL) != 0) && written == strData.length();
-
- return ret;
+ return std::make_unique<CWin32InterfaceForCLog>();
}
-void CWin32InterfaceForCLog::PrintDebugString(const std::string& debugString)
+spdlog_filename_t CWin32InterfaceForCLog::GetLogFilename(const std::string& filename) const
{
-#ifdef _DEBUG
- ::OutputDebugStringW(L"Debug Print: ");
- int bufSize = MultiByteToWideChar(CP_UTF8, 0, debugString.c_str(), debugString.length(), NULL, 0);
- XUTILS::auto_buffer buf(sizeof(wchar_t) * (bufSize + 1)); // '+1' for extra safety
- if (MultiByteToWideChar(CP_UTF8, 0, debugString.c_str(), debugString.length(), (wchar_t*)buf.get(), buf.size() / sizeof(wchar_t)) == bufSize)
- ::OutputDebugStringW(std::wstring((wchar_t*)buf.get(), bufSize).c_str());
- else
- ::OutputDebugStringA(debugString.c_str());
- ::OutputDebugStringW(L"\n");
-#endif // _DEBUG
+ return CWIN32Util::ConvertPathToWin32Form(CWIN32Util::SmbToUnc(filename));
}
-void CWin32InterfaceForCLog::GetCurrentLocalTime(int& year, int& month, int& day, int& hour, int& minute, int& second, double& millisecond)
+void CWin32InterfaceForCLog::AddSinks(
+ std::shared_ptr<spdlog::sinks::dist_sink<std::mutex>> distributionSink) const
{
- KODI::TIME::SystemTime time;
- GetLocalTime(&time);
- year = time.year;
- month = time.month;
- day = time.day;
- hour = time.hour;
- minute = time.minute;
- second = time.second;
- millisecond = static_cast<double>(time.milliseconds);
+ distributionSink->add_sink(std::make_shared<spdlog::sinks::msvc_sink_mt>());
}
diff --git a/xbmc/platform/win32/utils/Win32InterfaceForCLog.h b/xbmc/platform/win32/utils/Win32InterfaceForCLog.h
index 6baf61a316..1c3b3d8ea5 100644
--- a/xbmc/platform/win32/utils/Win32InterfaceForCLog.h
+++ b/xbmc/platform/win32/utils/Win32InterfaceForCLog.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014-2018 Team Kodi
+ * Copyright (C) 2020 Team Kodi
* This file is part of Kodi - https://kodi.tv
*
* SPDX-License-Identifier: GPL-2.0-or-later
@@ -8,20 +8,19 @@
#pragma once
-#include <string>
+#if !defined(TARGET_WINDOWS) && !defined(TARGET_WIN10)
+#error This file is for win32 platforms only
+#endif // !defined(TARGET_WINDOWS) && !defined(TARGET_WIN10)
-typedef void* HANDLE; // forward declaration, to avoid inclusion of whole Windows.h
+#include "utils/IPlatformLog.h"
-class CWin32InterfaceForCLog
+class CWin32InterfaceForCLog : public IPlatformLog
{
public:
- CWin32InterfaceForCLog();
- ~CWin32InterfaceForCLog();
- bool OpenLogFile(const std::string& logFilename, const std::string& backupOldLogToFilename);
- void CloseLogFile(void);
- bool WriteStringToLog(const std::string& logString);
- void PrintDebugString(const std::string& debugString);
- static void GetCurrentLocalTime(int& year, int& month, int& day, int& hour, int& minute, int& second, double& millisecond);
-private:
- HANDLE m_hFile;
+ CWin32InterfaceForCLog() = default;
+ ~CWin32InterfaceForCLog() = default;
+
+ spdlog_filename_t GetLogFilename(const std::string& filename) const override;
+ void AddSinks(
+ std::shared_ptr<spdlog::sinks::dist_sink<std::mutex>> distributionSink) const override;
};
diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp
index bf7c76a517..54f8261135 100644
--- a/xbmc/settings/AdvancedSettings.cpp
+++ b/xbmc/settings/AdvancedSettings.cpp
@@ -68,7 +68,7 @@ void CAdvancedSettings::OnSettingsLoaded()
m_logLevel = std::min(m_logLevelHint, LOG_LEVEL_DEBUG/*LOG_LEVEL_NORMAL*/);
CLog::Log(LOGNOTICE, "Disabled debug logging due to GUI setting. Level %d.", m_logLevel);
}
- CLog::SetLogLevel(m_logLevel);
+ CServiceBroker::GetLogging().SetLogLevel(m_logLevel);
m_extraLogEnabled = settings->GetBool(CSettings::SETTING_DEBUG_EXTRALOGGING);
SetExtraLogLevel(settings->GetList(CSettings::SETTING_DEBUG_SETEXTRALOGLEVEL));
@@ -867,7 +867,7 @@ void CAdvancedSettings::ParseSettingsFile(const std::string &file)
setting->SetVisible(false);
}
m_logLevel = std::max(m_logLevel, m_logLevelHint);
- CLog::SetLogLevel(m_logLevel);
+ CServiceBroker::GetLogging().SetLogLevel(m_logLevel);
}
XMLUtils::GetString(pRootElement, "cddbaddress", m_cddbAddress);
@@ -1351,7 +1351,7 @@ void CAdvancedSettings::SetDebugMode(bool debug)
{
int level = std::max(m_logLevelHint, LOG_LEVEL_DEBUG_FREEMEM);
m_logLevel = level;
- CLog::SetLogLevel(level);
+ CServiceBroker::GetLogging().SetLogLevel(level);
CLog::Log(LOGNOTICE, "Enabled debug logging due to GUI setting. Level %d.", level);
}
else
@@ -1359,7 +1359,7 @@ void CAdvancedSettings::SetDebugMode(bool debug)
int level = std::min(m_logLevelHint, LOG_LEVEL_DEBUG/*LOG_LEVEL_NORMAL*/);
CLog::Log(LOGNOTICE, "Disabled debug logging due to GUI setting. Level %d.", level);
m_logLevel = level;
- CLog::SetLogLevel(level);
+ CServiceBroker::GetLogging().SetLogLevel(level);
}
}
diff --git a/xbmc/test/TestBasicEnvironment.cpp b/xbmc/test/TestBasicEnvironment.cpp
index 9714018924..cbb9605c80 100644
--- a/xbmc/test/TestBasicEnvironment.cpp
+++ b/xbmc/test/TestBasicEnvironment.cpp
@@ -38,6 +38,9 @@ void TestBasicEnvironment::SetUp()
{
CAppParamParser params;
params.m_platformDirectories = false;
+
+ CServiceBroker::CreateLogging();
+
m_pSettingsComponent.reset(new CSettingsComponent());
m_pSettingsComponent->Init(params);
diff --git a/xbmc/utils/CMakeLists.txt b/xbmc/utils/CMakeLists.txt
index 648d15ffee..9705e31d09 100644
--- a/xbmc/utils/CMakeLists.txt
+++ b/xbmc/utils/CMakeLists.txt
@@ -111,6 +111,7 @@ set(HEADERS ActorProtocol.h
IBufferObject.h
ILocalizer.h
InfoLoader.h
+ IPlatformLog.h
IRssObserver.h
IScreenshotSurface.h
ISerializable.h
@@ -125,6 +126,7 @@ set(HEADERS ActorProtocol.h
LegacyPathTranslation.h
Locale.h
log.h
+ logtypes.h
MathUtils.h
MemUtils.h
Mime.h
diff --git a/xbmc/utils/IPlatformLog.h b/xbmc/utils/IPlatformLog.h
new file mode 100644
index 0000000000..6ccf98d334
--- /dev/null
+++ b/xbmc/utils/IPlatformLog.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include <memory>
+#include <mutex>
+#include <string>
+
+#ifdef TARGET_WINDOWS
+using spdlog_filename_t = std::wstring;
+#else
+using spdlog_filename_t = std::string;
+#endif
+
+namespace spdlog
+{
+namespace sinks
+{
+template<typename Mutex>
+class dist_sink;
+}
+} // namespace spdlog
+
+class IPlatformLog
+{
+public:
+ virtual ~IPlatformLog() = default;
+
+ static std::unique_ptr<IPlatformLog> CreatePlatformLog();
+
+ virtual spdlog_filename_t GetLogFilename(const std::string& filename) const = 0;
+ virtual void AddSinks(
+ std::shared_ptr<spdlog::sinks::dist_sink<std::mutex>> distributionSink) const = 0;
+};
diff --git a/xbmc/utils/log.cpp b/xbmc/utils/log.cpp
index e51a5f3d6f..79b423ff18 100644
--- a/xbmc/utils/log.cpp
+++ b/xbmc/utils/log.cpp
@@ -10,207 +10,185 @@
#include "CompileInfo.h"
#include "ServiceBroker.h"
-#include "settings/AdvancedSettings.h"
-#include "settings/SettingsComponent.h"
-#include "threads/CriticalSection.h"
-#include "threads/SingleLock.h"
-#include "threads/Thread.h"
-#include "utils/StringUtils.h"
-
-#include <inttypes.h>
-
-#if defined(TARGET_POSIX)
-#include "platform/posix/utils/PosixInterfaceForCLog.h"
-typedef class CPosixInterfaceForCLog PlatformInterfaceForCLog;
-#elif defined(TARGET_WINDOWS)
+#include "filesystem/File.h"
+#include "utils/URIUtils.h"
+
+#if defined(TARGET_ANDROID)
+#include "platform/android/utils/AndroidInterfaceForCLog.h"
+#elif defined(TARGET_DARWIN)
+#include "platform/darwin/utils/DarwinInterfaceForCLog.h"
+#elif defined(TARGET_WINDOWS) || defined(TARGET_WIN10)
#include "platform/win32/utils/Win32InterfaceForCLog.h"
-typedef class CWin32InterfaceForCLog PlatformInterfaceForCLog;
+#else
+#include "platform/posix/utils/PosixInterfaceForCLog.h"
#endif
+#include <cstring>
-static const char* const levelNames[] =
-{"DEBUG", "INFO", "NOTICE", "WARNING", "ERROR", "SEVERE", "FATAL", "NONE"};
+#include <spdlog/sinks/basic_file_sink.h>
+#include <spdlog/sinks/dist_sink.h>
-// add 1 to level number to get index of name
-static const char* const logLevelNames[] =
-{ "LOG_LEVEL_NONE" /*-1*/, "LOG_LEVEL_NORMAL" /*0*/, "LOG_LEVEL_DEBUG" /*1*/, "LOG_LEVEL_DEBUG_FREEMEM" /*2*/ };
+static constexpr unsigned char Utf8Bom[3] = {0xEF, 0xBB, 0xBF};
+static const std::string LogFileExtension = ".log";
+static const std::string LogPattern = "%Y-%m-%d %T.%e T:%-5t %7l <%n>: %v";
-namespace
-{
-class CLogGlobals
+CLog::CLog()
+ : m_platform(IPlatformLog::CreatePlatformLog()),
+ m_sinks(std::make_shared<spdlog::sinks::dist_sink_mt>()),
+ m_defaultLogger(CreateLogger("general")),
+ m_logLevel(LOG_LEVEL_DEBUG)
{
-public:
- ~CLogGlobals() = default;
- PlatformInterfaceForCLog m_platform;
- int m_repeatCount = 0;
- int m_repeatLogLevel = -1;
- std::string m_repeatLine;
- int m_logLevel = LOG_LEVEL_DEBUG;
- int m_extraLogLevels = 0;
- CCriticalSection critSec;
-};
-
-static CLogGlobals g_logState;
-}
+ // add platform-specific debug sinks
+ m_platform->AddSinks(m_sinks);
-CLog::CLog() = default;
+ // register the default logger with spdlog
+ spdlog::set_default_logger(m_defaultLogger);
-CLog::~CLog() = default;
+ // set the formatting pattern globally
+ spdlog::set_pattern(LogPattern);
-void CLog::Close()
-{
- CSingleLock waitLock(g_logState.critSec);
- g_logState.m_platform.CloseLogFile();
- g_logState.m_repeatLine.clear();
+ // flush on debug logs
+ spdlog::flush_on(spdlog::level::debug);
+
+ // set the log level
+ SetLogLevel(m_logLevel);
}
-void CLog::LogString(int logLevel, std::string&& logString)
+void CLog::Initialize(const std::string& path)
{
- CSingleLock waitLock(g_logState.critSec);
- std::string strData(logString);
- StringUtils::TrimRight(strData);
- if (!strData.empty())
+ if (m_fileSink != nullptr)
+ return;
+
+ if (path.empty())
+ return;
+
+ // put together the path to the log file(s)
+ std::string appName = CCompileInfo::GetAppName();
+ StringUtils::ToLower(appName);
+ const std::string filePathBase = URIUtils::AddFileToFolder(path, appName);
+ const std::string filePath = filePathBase + LogFileExtension;
+ const std::string oldFilePath = filePathBase + ".old" + LogFileExtension;
+
+ // handle old.log by deleting an existing old.log and renaming the last log to old.log
+ XFILE::CFile::Delete(oldFilePath);
+ XFILE::CFile::Rename(filePath, oldFilePath);
+
+ // write UTF-8 BOM
{
- if (g_logState.m_repeatLogLevel == logLevel && g_logState.m_repeatLine == strData)
- {
- g_logState.m_repeatCount++;
- return;
- }
- else if (g_logState.m_repeatCount)
- {
- std::string strData2 = StringUtils::Format("Previous line repeats %d times.",
- g_logState.m_repeatCount);
- PrintDebugString(strData2);
- WriteLogString(g_logState.m_repeatLogLevel, strData2);
- g_logState.m_repeatCount = 0;
- }
-
- g_logState.m_repeatLine = strData;
- g_logState.m_repeatLogLevel = logLevel;
-
- PrintDebugString(strData);
-
- WriteLogString(logLevel, strData);
+ XFILE::CFile file;
+ if (file.OpenForWrite(filePath, true))
+ file.Write(Utf8Bom, sizeof(Utf8Bom));
}
-}
-void CLog::LogString(int logLevel, int component, std::string&& logString)
-{
- if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->CanLogComponent(component) && IsLogLevelLogged(logLevel))
- LogString(logLevel, std::move(logString));
+ // create the file sink
+ m_fileSink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(
+ m_platform->GetLogFilename(filePath), false);
+ m_fileSink->set_pattern(LogPattern);
+
+ // add it to the existing sinks
+ m_sinks->add_sink(m_fileSink);
}
-bool CLog::Init(const std::string& path)
+void CLog::Uninitialize()
{
- CSingleLock waitLock(g_logState.critSec);
+ if (m_fileSink == nullptr)
+ return;
- // the log folder location is initialized in the CAdvancedSettings
- // constructor and changed in CApplication::Create()
+ // flush all loggers
+ spdlog::apply_all([](std::shared_ptr<spdlog::logger> logger) { logger->flush(); });
- std::string appName = CCompileInfo::GetAppName();
- StringUtils::ToLower(appName);
- return g_logState.m_platform.OpenLogFile(path + appName + ".log", path + appName + ".old.log");
-}
+ // flush the file sink
+ m_fileSink->flush();
-void CLog::MemDump(char *pData, int length)
-{
- Log(LOGDEBUG, "MEM_DUMP: Dumping from %p", pData);
- for (int i = 0; i < length; i+=16)
- {
- std::string strLine = StringUtils::Format("MEM_DUMP: %04x ", i);
- char *alpha = pData;
- for (int k=0; k < 4 && i + 4*k < length; k++)
- {
- for (int j=0; j < 4 && i + 4*k + j < length; j++)
- {
- std::string strFormat = StringUtils::Format(" %02x", (unsigned char)*pData++);
- strLine += strFormat;
- }
- strLine += " ";
- }
- // pad with spaces
- while (strLine.size() < 13*4 + 16)
- strLine += " ";
- for (int j=0; j < 16 && i + j < length; j++)
- {
- if (*alpha > 31)
- strLine += *alpha;
- else
- strLine += '.';
- alpha++;
- }
- Log(LOGDEBUG, "%s", strLine.c_str());
- }
+ // remove and destroy the file sink
+ m_sinks->remove_sink(m_fileSink);
+ m_fileSink.reset();
}
void CLog::SetLogLevel(int level)
{
- CSingleLock waitLock(g_logState.critSec);
- if (level >= LOG_LEVEL_NONE && level <= LOG_LEVEL_MAX)
- {
- g_logState.m_logLevel = level;
- CLog::Log(LOGNOTICE, "Log level changed to \"%s\"", logLevelNames[g_logState.m_logLevel + 1]);
- }
+ if (level < LOG_LEVEL_NONE || level > LOG_LEVEL_MAX)
+ return;
+
+ m_logLevel = level;
+
+ auto spdLevel = spdlog::level::info;
+#if defined(_DEBUG) || defined(PROFILE)
+ spdLevel = spdlog::level::trace;
+#else
+ if (level <= LOG_LEVEL_NONE)
+ spdLevel = spdlog::level::off;
+ else if (level >= LOG_LEVEL_DEBUG)
+ spdLevel = spdlog::level::trace;
else
- CLog::Log(LOGERROR, "%s: Invalid log level requested: %d", __FUNCTION__, level);
-}
+ spdLevel = spdlog::level::info;
+#endif
-int CLog::GetLogLevel()
-{
- return g_logState.m_logLevel;
-}
+ if (m_defaultLogger != nullptr && m_defaultLogger->level() == spdLevel)
+ return;
-void CLog::SetExtraLogLevels(int level)
-{
- CSingleLock waitLock(g_logState.critSec);
- g_logState.m_extraLogLevels = level;
+ spdlog::set_level(spdLevel);
+ FormatAndLogInternal(spdlog::level::info, "Log level changed to \"%s\"",
+ spdlog::level::to_string_view(spdLevel));
}
bool CLog::IsLogLevelLogged(int loglevel)
{
- const int extras = (loglevel & ~LOGMASK);
- if (extras != 0 && (g_logState.m_extraLogLevels & extras) == 0)
- return false;
-
- if (g_logState.m_logLevel >= LOG_LEVEL_DEBUG)
+ if (m_logLevel >= LOG_LEVEL_DEBUG)
return true;
- if (g_logState.m_logLevel <= LOG_LEVEL_NONE)
+ if (m_logLevel <= LOG_LEVEL_NONE)
return false;
- // "m_logLevel" is "LOG_LEVEL_NORMAL"
return (loglevel & LOGMASK) >= LOGNOTICE;
}
+Logger CLog::GetLogger(const std::string& loggerName)
+{
+ auto logger = spdlog::get(loggerName);
+ if (logger == nullptr)
+ logger = CreateLogger(loggerName);
-void CLog::PrintDebugString(const std::string& line)
+ return logger;
+}
+
+CLog& CLog::GetInstance()
{
-#if defined(_DEBUG) || defined(PROFILE)
- g_logState.m_platform.PrintDebugString(line);
-#endif // defined(_DEBUG) || defined(PROFILE)
+ return CServiceBroker::GetLogging();
}
-bool CLog::WriteLogString(int logLevel, const std::string& logString)
+spdlog::level::level_enum CLog::MapLogLevel(int level)
{
- static const char* prefixFormat = "%02d-%02d-%02d %02d:%02d:%02d.%03d T:%" PRIu64" %7s: ";
-
- std::string strData(logString);
- /* fixup newline alignment, number of spaces should equal prefix length */
- StringUtils::Replace(strData, "\n", "\n ");
-
- int year, month, day, hour, minute, second;
- double millisecond;
- g_logState.m_platform.GetCurrentLocalTime(year, month, day, hour, minute, second, millisecond);
-
- strData = StringUtils::Format(prefixFormat,
- year,
- month,
- day,
- hour,
- minute,
- second,
- static_cast<int>(millisecond),
- static_cast<uint64_t>(CThread::GetCurrentThreadNativeId()),
- levelNames[logLevel]) + strData;
-
- return g_logState.m_platform.WriteStringToLog(strData);
+ switch (level)
+ {
+ case LOGDEBUG:
+ return spdlog::level::debug;
+ case LOGINFO:
+ case LOGNOTICE:
+ return spdlog::level::info;
+ case LOGWARNING:
+ return spdlog::level::warn;
+ case LOGERROR:
+ return spdlog::level::err;
+ case LOGSEVERE:
+ case LOGFATAL:
+ return spdlog::level::critical;
+ case LOGNONE:
+ return spdlog::level::off;
+
+ default:
+ break;
+ }
+
+ return spdlog::level::info;
+}
+
+Logger CLog::CreateLogger(const std::string& loggerName)
+{
+ // create the logger
+ auto logger = std::make_shared<spdlog::logger>(loggerName, m_sinks);
+
+ // initialize the logger
+ spdlog::initialize_logger(logger);
+
+ return logger;
}
diff --git a/xbmc/utils/log.h b/xbmc/utils/log.h
index c45ed06c52..bf37ba3799 100644
--- a/xbmc/utils/log.h
+++ b/xbmc/utils/log.h
@@ -8,98 +8,191 @@
#pragma once
+// spdlog specific defines
+#define SPDLOG_LEVEL_NAMES {"TRACE", "DEBUG", "INFO", "WARNING", "ERROR", "FATAL", "OFF"};
+
#include "commons/ilog.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/IPlatformLog.h"
#include "utils/StringUtils.h"
+#include "utils/logtypes.h"
#include <string>
-#include <utility>
+#include <spdlog/spdlog.h>
+
+namespace spdlog
+{
+namespace sinks
+{
+template<typename Mutex>
+class basic_file_sink;
+
+template<typename Mutex>
+class dist_sink;
+} // namespace sinks
+} // namespace spdlog
class CLog
{
public:
CLog();
- ~CLog();
- static void Close();
+ ~CLog() = default;
+
+ void Initialize(const std::string& path);
+ void Uninitialize();
- static void Log(int loglevel, const char* format)
+ void SetLogLevel(int level);
+ int GetLogLevel() { return m_logLevel; }
+ bool IsLogLevelLogged(int loglevel);
+
+ Logger GetLogger(const std::string& loggerName);
+
+ template<typename Char, typename... Args>
+ static inline void Log(int level, const Char* format, Args&&... args)
{
- if (IsLogLevelLogged(loglevel))
- LogString(loglevel, format);
+ Log(MapLogLevel(level), format, std::forward<Args>(args)...);
}
- template<typename... Args>
- static void Log(int loglevel, const char* format, Args&&... args)
+ template<typename Char, typename... Args>
+ static inline void Log(int level, int component, const Char* format, Args&&... args)
{
- if (IsLogLevelLogged(loglevel))
- LogString(loglevel, StringUtils::Format(format, std::forward<Args>(args)...));
+ if (!CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->CanLogComponent(component))
+ return;
+
+ Log(level, format, std::forward<Args>(args)...);
}
- static void Log(int loglevel, int component, const char* format)
+ template<typename Char, typename... Args>
+ static inline void Log(spdlog::level::level_enum level, const Char* format, Args&&... args)
{
- if (IsLogLevelLogged(loglevel))
- LogString(loglevel, component, format);
+ GetInstance().FormatAndLogInternal(level, format, std::forward<Args>(args)...);
}
- template<typename... Args>
- static void Log(int loglevel, int component, const char* format, Args&&... args)
+ template<typename Char, typename... Args>
+ static inline void Log(spdlog::level::level_enum level,
+ int component,
+ const Char* format,
+ Args&&... args)
{
- // We defer component checking to LogString to avoid having to drag in advancedsettings
- // everywhere we want to log anything
- if (IsLogLevelLogged(loglevel))
- LogString(loglevel, component, StringUtils::Format(format, std::forward<Args>(args)...));
+ if (!CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->CanLogComponent(component))
+ return;
+
+ Log(level, format, std::forward<Args>(args)...);
}
- static void LogFunction(int loglevel, std::string functionName, const char* format)
+ template<typename Char, typename... Args>
+ static inline void LogFunction(int level,
+ const char* functionName,
+ const Char* format,
+ Args&&... args)
{
- if (IsLogLevelLogged(loglevel))
- LogString(loglevel, functionName + ": " + format);
+ LogFunction(MapLogLevel(level), functionName, format, std::forward<Args>(args)...);
}
- template<typename... Args>
- static void LogFunction(int loglevel,
- std::string functionName,
- const char* format,
- Args&&... args)
+ template<typename Char, typename... Args>
+ static inline void LogFunction(
+ int level, const char* functionName, int component, const Char* format, Args&&... args)
+ {
+ if (!CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->CanLogComponent(component))
+ return;
+
+ LogFunction(level, functionName, format, std::forward<Args>(args)...);
+ }
+
+ template<typename Char, typename... Args>
+ static inline void LogFunction(spdlog::level::level_enum level,
+ const char* functionName,
+ const Char* format,
+ Args&&... args)
{
- if (IsLogLevelLogged(loglevel))
- {
- functionName.append(": ");
- LogString(loglevel, functionName + StringUtils::Format(format, std::forward<Args>(args)...));
- }
+ if (functionName == nullptr || strlen(functionName) == 0)
+ GetInstance().FormatAndLogInternal(level, format, std::forward<Args>(args)...);
+ else
+ GetInstance().FormatAndLogFunctionInternal(level, functionName, format,
+ std::forward<Args>(args)...);
}
- static void LogFunction(int loglevel, std::string functionName, int component, const char* format)
+ template<typename Char, typename... Args>
+ static inline void LogFunction(spdlog::level::level_enum level,
+ const char* functionName,
+ int component,
+ const Char* format,
+ Args&&... args)
{
- if (IsLogLevelLogged(loglevel))
- LogString(loglevel, component, functionName + ": " + format);
+ if (!CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->CanLogComponent(component))
+ return;
+
+ LogFunction(level, functionName, format, std::forward<Args>(args)...);
}
+#define LogF(level, format, ...) LogFunction((level), __FUNCTION__, (format), ##__VA_ARGS__)
+#define LogFC(level, component, format, ...) \
+ LogFunction((level), __FUNCTION__, (component), (format), ##__VA_ARGS__)
+
+private:
+ static CLog& GetInstance();
+
+ static spdlog::level::level_enum MapLogLevel(int level);
+
template<typename... Args>
- static void LogFunction(
- int loglevel, std::string functionName, int component, const char* format, Args&&... args)
+ static inline void FormatAndLogFunctionInternal(spdlog::level::level_enum level,
+ const char* functionName,
+ const char* format,
+ Args&&... args)
+ {
+ GetInstance().FormatAndLogInternal(
+ level, StringUtils::Format("{0:s}: {1:s}", functionName, format).c_str(),
+ std::forward<Args>(args)...);
+ }
+
+ template<typename... Args>
+ static inline void FormatAndLogFunctionInternal(spdlog::level::level_enum level,
+ const char* functionName,
+ const wchar_t* format,
+ Args&&... args)
+ {
+ GetInstance().FormatAndLogInternal(
+ level, StringUtils::Format(L"{0:s}: {1:s}", functionName, format).c_str(),
+ std::forward<Args>(args)...);
+ }
+
+ template<typename Char, typename... Args>
+ inline void FormatAndLogInternal(spdlog::level::level_enum level,
+ const Char* format,
+ Args&&... args)
+ {
+ // TODO: for now we manually format the messages to support both python- and printf-style formatting.
+ // this can be removed once all log messages have been adjusted to python-style formatting
+ auto logString = StringUtils::Format(format, std::forward<Args>(args)...);
+
+ // fixup newline alignment, number of spaces should equal prefix length
+ StringUtils::Replace(logString, "\n", "\n ");
+
+ m_defaultLogger->log(level, std::move(logString));
+ }
+
+ Logger CreateLogger(const std::string& loggerName);
+
+ std::unique_ptr<IPlatformLog> m_platform;
+ std::shared_ptr<spdlog::sinks::dist_sink<std::mutex>> m_sinks;
+ Logger m_defaultLogger;
+
+ std::shared_ptr<spdlog::sinks::basic_file_sink<std::mutex>> m_fileSink;
+
+ int m_logLevel;
+};
+
+namespace XbmcUtils
+{
+class LogImplementation : public XbmcCommons::ILogger
+{
+public:
+ ~LogImplementation() override = default;
+ inline void log(int logLevel, IN_STRING const char* message) override
{
- // We defer component checking to LogString to avoid having to drag in advancedsettings
- // everywhere we want to log anything
- if (IsLogLevelLogged(loglevel))
- {
- functionName.append(": ");
- LogString(loglevel, component,
- functionName + StringUtils::Format(format, std::forward<Args>(args)...));
- }
+ CLog::Log(logLevel, "{0:s}", message);
}
-#define LogF(loglevel, ...) LogFunction((loglevel), __FUNCTION__, ##__VA_ARGS__)
-#define LogFC(loglevel, component, ...) LogFunction((loglevel), __FUNCTION__, (component), ##__VA_ARGS__)
- static void MemDump(char *pData, int length);
- static bool Init(const std::string& path);
- static void PrintDebugString(const std::string& line); // universal interface for printing debug strings
- static void SetLogLevel(int level);
- static int GetLogLevel();
- static void SetExtraLogLevels(int level);
- static bool IsLogLevelLogged(int loglevel);
-
-protected:
- static void LogString(int logLevel, std::string&& logString);
- static void LogString(int logLevel, int component, std::string&& logString);
- static bool WriteLogString(int logLevel, const std::string& logString);
};
+} // namespace XbmcUtils
diff --git a/xbmc/utils/logtypes.h b/xbmc/utils/logtypes.h
new file mode 100644
index 0000000000..f41aa7eb07
--- /dev/null
+++ b/xbmc/utils/logtypes.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2020 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include <memory>
+
+namespace spdlog
+{
+class logger;
+}
+
+using Logger = std::shared_ptr<spdlog::logger>;
diff --git a/xbmc/utils/test/TestRegExp.cpp b/xbmc/utils/test/TestRegExp.cpp
index 1019c5bfcb..dd04792365 100644
--- a/xbmc/utils/test/TestRegExp.cpp
+++ b/xbmc/utils/test/TestRegExp.cpp
@@ -10,6 +10,7 @@
* Investigate why.
*/
#include "CompileInfo.h"
+#include "ServiceBroker.h"
#include "filesystem/File.h"
#include "filesystem/SpecialProtocol.h"
#include "utils/RegExp.h"
@@ -128,7 +129,7 @@ protected:
TestRegExpLog() = default;
~TestRegExpLog() override
{
- CLog::Close();
+ CServiceBroker::GetLogging().Uninitialize();
}
};
@@ -143,13 +144,13 @@ TEST_F(TestRegExpLog, DumpOvector)
std::string appName = CCompileInfo::GetAppName();
StringUtils::ToLower(appName);
logfile = CSpecialProtocol::TranslatePath("special://temp/") + appName + ".log";
- EXPECT_TRUE(CLog::Init(CSpecialProtocol::TranslatePath("special://temp/").c_str()));
+ CServiceBroker::GetLogging().Initialize(CSpecialProtocol::TranslatePath("special://temp/").c_str());
EXPECT_TRUE(XFILE::CFile::Exists(logfile));
EXPECT_TRUE(regex.RegComp("^(?<first>Test)\\s*(?<second>.*)\\."));
EXPECT_EQ(0, regex.RegFind("Test string."));
regex.DumpOvector(LOGDEBUG);
- CLog::Close();
+ CServiceBroker::GetLogging().Uninitialize();
EXPECT_TRUE(file.Open(logfile));
while ((bytesread = file.Read(buf, sizeof(buf) - 1)) > 0)
@@ -162,7 +163,7 @@ TEST_F(TestRegExpLog, DumpOvector)
EXPECT_STREQ("\xEF\xBB\xBF", logstring.substr(0, 3).c_str());
- EXPECT_TRUE(regex.RegComp(".*DEBUG: regexp ovector=\\{\\[0,12\\],\\[0,4\\],"
+ EXPECT_TRUE(regex.RegComp(".*DEBUG <general>: regexp ovector=\\{\\[0,12\\],\\[0,4\\],"
"\\[5,11\\]\\}.*"));
EXPECT_GE(regex.RegFind(logstring), 0);
diff --git a/xbmc/utils/test/Testlog.cpp b/xbmc/utils/test/Testlog.cpp
index 71bf9d6a5f..15f37a8f9e 100644
--- a/xbmc/utils/test/Testlog.cpp
+++ b/xbmc/utils/test/Testlog.cpp
@@ -7,6 +7,7 @@
*/
#include "CompileInfo.h"
+#include "ServiceBroker.h"
#include "filesystem/File.h"
#include "filesystem/SpecialProtocol.h"
#include "test/TestUtils.h"
@@ -24,7 +25,7 @@ protected:
Testlog() = default;
~Testlog() override
{
- CLog::Close();
+ CServiceBroker::GetLogging().Uninitialize();
}
};
@@ -39,7 +40,7 @@ TEST_F(Testlog, Log)
std::string appName = CCompileInfo::GetAppName();
StringUtils::ToLower(appName);
logfile = CSpecialProtocol::TranslatePath("special://temp/") + appName + ".log";
- EXPECT_TRUE(CLog::Init(CSpecialProtocol::TranslatePath("special://temp/").c_str()));
+ CServiceBroker::GetLogging().Initialize(CSpecialProtocol::TranslatePath("special://temp/").c_str());
EXPECT_TRUE(XFILE::CFile::Exists(logfile));
CLog::Log(LOGDEBUG, "debug log message");
@@ -50,7 +51,7 @@ TEST_F(Testlog, Log)
CLog::Log(LOGSEVERE, "severe log message");
CLog::Log(LOGFATAL, "fatal log message");
CLog::Log(LOGNONE, "none type log message");
- CLog::Close();
+ CServiceBroker::GetLogging().Uninitialize();
EXPECT_TRUE(file.Open(logfile));
while ((bytesread = file.Read(buf, sizeof(buf) - 1)) > 0)
@@ -63,60 +64,21 @@ TEST_F(Testlog, Log)
EXPECT_STREQ("\xEF\xBB\xBF", logstring.substr(0, 3).c_str());
- EXPECT_TRUE(regex.RegComp(".*DEBUG: debug log message.*"));
+ EXPECT_TRUE(regex.RegComp(".*DEBUG <general>: debug log message.*"));
EXPECT_GE(regex.RegFind(logstring), 0);
- EXPECT_TRUE(regex.RegComp(".*INFO: info log message.*"));
+ EXPECT_TRUE(regex.RegComp(".*INFO <general>: info log message.*"));
EXPECT_GE(regex.RegFind(logstring), 0);
- EXPECT_TRUE(regex.RegComp(".*NOTICE: notice log message.*"));
+ EXPECT_TRUE(regex.RegComp(".*INFO <general>: notice log message.*"));
EXPECT_GE(regex.RegFind(logstring), 0);
- EXPECT_TRUE(regex.RegComp(".*WARNING: warning log message.*"));
+ EXPECT_TRUE(regex.RegComp(".*WARNING <general>: warning log message.*"));
EXPECT_GE(regex.RegFind(logstring), 0);
- EXPECT_TRUE(regex.RegComp(".*ERROR: error log message.*"));
+ EXPECT_TRUE(regex.RegComp(".*ERROR <general>: error log message.*"));
EXPECT_GE(regex.RegFind(logstring), 0);
- EXPECT_TRUE(regex.RegComp(".*SEVERE: severe log message.*"));
+ EXPECT_TRUE(regex.RegComp(".*FATAL <general>: severe log message.*"));
EXPECT_GE(regex.RegFind(logstring), 0);
- EXPECT_TRUE(regex.RegComp(".*FATAL: fatal log message.*"));
+ EXPECT_TRUE(regex.RegComp(".*FATAL <general>: fatal log message.*"));
EXPECT_GE(regex.RegFind(logstring), 0);
- EXPECT_TRUE(regex.RegComp(".*NONE: none type log message.*"));
- EXPECT_GE(regex.RegFind(logstring), 0);
-
- EXPECT_TRUE(XFILE::CFile::Delete(logfile));
-}
-
-TEST_F(Testlog, MemDump)
-{
- std::string logfile, logstring;
- char buf[100];
- unsigned int bytesread;
- XFILE::CFile file;
- CRegExp regex;
- char refdata[] = "0123456789abcdefghijklmnopqrstuvwxyz";
-
- std::string appName = CCompileInfo::GetAppName();
- StringUtils::ToLower(appName);
- logfile = CSpecialProtocol::TranslatePath("special://temp/") + appName + ".log";
- EXPECT_TRUE(CLog::Init(CSpecialProtocol::TranslatePath("special://temp/").c_str()));
- EXPECT_TRUE(XFILE::CFile::Exists(logfile));
-
- CLog::MemDump(refdata, sizeof(refdata));
- CLog::Close();
-
- EXPECT_TRUE(file.Open(logfile));
- while ((bytesread = file.Read(buf, sizeof(buf) - 1)) > 0)
- {
- buf[bytesread] = '\0';
- logstring.append(buf);
- }
- file.Close();
- EXPECT_FALSE(logstring.empty());
-
- EXPECT_STREQ("\xEF\xBB\xBF", logstring.substr(0, 3).c_str());
-
- EXPECT_TRUE(regex.RegComp(".*DEBUG: MEM_DUMP: Dumping from.*"));
- EXPECT_GE(regex.RegFind(logstring), 0);
- EXPECT_TRUE(regex.RegComp(".*DEBUG: MEM_DUMP: 0000 30 31 32 33.*"));
- EXPECT_GE(regex.RegFind(logstring), 0);
- EXPECT_TRUE(regex.RegComp(".*73 74 75 76 ghijklmnopqrstuv.*"));
+ EXPECT_TRUE(regex.RegComp(".*OFF <general>: none type log message.*"));
EXPECT_GE(regex.RegFind(logstring), 0);
EXPECT_TRUE(XFILE::CFile::Delete(logfile));
@@ -129,13 +91,13 @@ TEST_F(Testlog, SetLogLevel)
std::string appName = CCompileInfo::GetAppName();
StringUtils::ToLower(appName);
logfile = CSpecialProtocol::TranslatePath("special://temp/") + appName + ".log";
- EXPECT_TRUE(CLog::Init(CSpecialProtocol::TranslatePath("special://temp/").c_str()));
+ CServiceBroker::GetLogging().Initialize(CSpecialProtocol::TranslatePath("special://temp/").c_str());
EXPECT_TRUE(XFILE::CFile::Exists(logfile));
- EXPECT_EQ(LOG_LEVEL_DEBUG, CLog::GetLogLevel());
- CLog::SetLogLevel(LOG_LEVEL_MAX);
- EXPECT_EQ(LOG_LEVEL_MAX, CLog::GetLogLevel());
+ EXPECT_EQ(LOG_LEVEL_DEBUG, CServiceBroker::GetLogging().GetLogLevel());
+ CServiceBroker::GetLogging().SetLogLevel(LOG_LEVEL_MAX);
+ EXPECT_EQ(LOG_LEVEL_MAX, CServiceBroker::GetLogging().GetLogLevel());
- CLog::Close();
+ CServiceBroker::GetLogging().Uninitialize();
EXPECT_TRUE(XFILE::CFile::Delete(logfile));
}