Skip to content

Commit b5616e9

Browse files
committed
Add a new logging class
1 parent 45d7d84 commit b5616e9

File tree

5 files changed

+511
-1
lines changed

5 files changed

+511
-1
lines changed

src/libprojectM/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ add_subdirectory(UserSprites)
1313

1414
add_library(projectM_main OBJECT
1515
"${PROJECTM_EXPORT_HEADER}"
16+
Logging.cpp
17+
Logging.hpp
1618
Preset.hpp
1719
PresetFactory.cpp
1820
PresetFactory.hpp

src/libprojectM/Logging.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include "Logging.hpp"
2+
3+
#include <string>
4+
5+
namespace libprojectM {
6+
7+
Logging::UserCallback Logging::m_globalCallback = {};
8+
thread_local Logging::UserCallback Logging::m_threadCallback = {};
9+
10+
Logging::LogLevel Logging::m_globalLogLevel = LogLevel::NotSet;
11+
thread_local Logging::LogLevel Logging::m_threadLogLevel = LogLevel::NotSet;
12+
13+
void Logging::SetGlobalCallback(const UserCallback callback)
14+
{
15+
m_globalCallback = callback;
16+
}
17+
18+
void Logging::SetThreadCallback(const UserCallback callback)
19+
{
20+
m_threadCallback = callback;
21+
}
22+
23+
void Logging::SetGlobalLogLevel(const LogLevel logLevel)
24+
{
25+
m_globalLogLevel = logLevel;
26+
}
27+
28+
void Logging::SetThreadLogLevel(const LogLevel logLevel)
29+
{
30+
m_threadLogLevel = logLevel;
31+
}
32+
33+
auto Logging::GetLogLevel() -> LogLevel
34+
{
35+
if (m_threadLogLevel != LogLevel::NotSet)
36+
{
37+
return m_threadLogLevel;
38+
}
39+
40+
if (m_globalLogLevel != LogLevel::NotSet)
41+
{
42+
return m_globalLogLevel;
43+
}
44+
45+
return m_defaultLogLevel;
46+
}
47+
48+
auto Logging::HasCallback() -> bool
49+
{
50+
return GetLoggingCallback().callbackFunction != nullptr;
51+
}
52+
53+
void Logging::Log(const std::string& message, LogLevel severity)
54+
{
55+
auto callback = GetLoggingCallback();
56+
57+
if (callback.callbackFunction == nullptr)
58+
{
59+
return;
60+
}
61+
62+
callback.callbackFunction(message.c_str(), static_cast<int>(severity), callback.userData);
63+
}
64+
65+
auto Logging::GetLoggingCallback() -> UserCallback
66+
{
67+
if (m_threadCallback.callbackFunction != nullptr)
68+
{
69+
return m_threadCallback;
70+
}
71+
72+
return m_globalCallback;
73+
}
74+
75+
} // namespace libprojectM

src/libprojectM/Logging.hpp

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <string>
5+
6+
namespace libprojectM {
7+
8+
/**
9+
* @class Logging
10+
* @brief A simple logger implementation to forward messages to the outside app.
11+
*
12+
* This class wraps logging functionality which can be used at either a global or thread-local
13+
* level, for both callbacks and log levels.
14+
*/
15+
class Logging
16+
{
17+
public:
18+
/**
19+
* The configurable log levels. If not set, "Information" is used by default.
20+
*/
21+
enum class LogLevel : uint8_t
22+
{
23+
NotSet, //!< Log level not set
24+
Trace, //!< Most verbose logging, used to trace individual function calls or values.
25+
Debug, //!< Debug-related messages for relevant data and developer information.
26+
Information, //!< Not-too-frequent messages possibly relevant for users and developers.
27+
Warning, //!< Something is wrong, but doesn't affect execution in a major way.
28+
Error, //!< A recoverable error occurred which negatively affects projectM, e.g. shader compilation issues.
29+
Fatal //!< An unrecoverable error occurred and the projectM instance cannot continue execution.
30+
};
31+
32+
/**
33+
* The application callback function.
34+
*/
35+
using CallbackFunction = void (*)(const char* message, int severity, void* userData);
36+
37+
/**
38+
* A struct holding the user callback function and data pointer.
39+
*/
40+
struct UserCallback {
41+
CallbackFunction callbackFunction{}; //!< Function pointer of the user callback.
42+
void* userData{}; //!< User data pointer for the callback.
43+
};
44+
45+
Logging() = delete;
46+
47+
/**
48+
* Sets the global callback function pointer used across all threads.
49+
* @param callback A UserCallback struct with the new function and user data pointers.
50+
*/
51+
static void SetGlobalCallback(UserCallback callback);
52+
53+
/**
54+
* Sets the thread-specific callback function pointer only used in the thread which registered it.
55+
* @param callback A UserCallback struct with the new function and user data pointers.
56+
*/
57+
static void SetThreadCallback(UserCallback callback);
58+
59+
/**
60+
* Sets the global log level used across all threads.
61+
* @param logLevel The log level to use. If set to LogLevel::NotSet, the value of m_defaultLogLevel is used.
62+
*/
63+
static void SetGlobalLogLevel(LogLevel logLevel);
64+
65+
/**
66+
* Sets the thread-specific log level only used in the thread which set it.
67+
* @param logLevel The log level to use. If set to LogLevel::NotSet, the value of m_defaultLogLevel is used.
68+
*/
69+
static void SetThreadLogLevel(LogLevel logLevel);
70+
71+
/**
72+
* Returns the effective log level for the current thread.
73+
* @return The log level set for this thread, or, if LogLevel::NotSet, the global log level.
74+
* If no global log level is set, it returns the value of m_defaultLogLevel.
75+
*/
76+
static auto GetLogLevel() -> LogLevel;
77+
78+
/**
79+
* Returns whether a callback is registered or not.
80+
* @return true if a callback is registered for the current thread or globally, false if none is registered.
81+
*/
82+
static auto HasCallback() -> bool;
83+
84+
/**
85+
* @brief Passes a log message with the given severity to the active thread or global callback.
86+
* If no callbacks are registered, this function does nothing.
87+
* @param message
88+
* @param severity
89+
*/
90+
static void Log(const std::string& message, LogLevel severity);
91+
92+
93+
static constexpr LogLevel m_defaultLogLevel{LogLevel::Information};
94+
95+
private:
96+
/**
97+
* @brief Returns the active callback for this thread.
98+
* If the thread has a local callback, this is returned, otherwise the global callback.
99+
* @return A pointer to the active callback function, or nullptr if none is registered.
100+
*/
101+
static auto GetLoggingCallback() -> UserCallback;
102+
103+
static UserCallback m_globalCallback; //!< The global callback function.
104+
thread_local static UserCallback m_threadCallback; //!< The thread-specific callback function.
105+
106+
static LogLevel m_globalLogLevel; //!< The global log level.
107+
thread_local static LogLevel m_threadLogLevel; //!< The thread-specific log level.
108+
};
109+
110+
#define LOG_TRACE(message) \
111+
if (Logging::HasCallback() && Logging::GetLogLevel() == Logging::LogLevel::Trace) \
112+
{ \
113+
Logging::Log(message, Logging::LogLevel::Trace); \
114+
}
115+
116+
#define LOG_DEBUG(message) \
117+
if (Logging::HasCallback() && Logging::GetLogLevel() <= Logging::LogLevel::Debug) \
118+
{ \
119+
Logging::Log(message, Logging::LogLevel::Debug); \
120+
}
121+
122+
#define LOG_INFO(message) \
123+
if (Logging::HasCallback() && Logging::GetLogLevel() <= Logging::LogLevel::Information) \
124+
{ \
125+
Logging::Log(message, Logging::LogLevel::Information); \
126+
}
127+
128+
#define LOG_WARN(message) \
129+
if (Logging::HasCallback() && Logging::GetLogLevel() <= Logging::LogLevel::Warning) \
130+
{ \
131+
Logging::Log(message, Logging::LogLevel::Warning); \
132+
}
133+
134+
#define LOG_ERROR(message) \
135+
if (Logging::HasCallback() && Logging::GetLogLevel() <= Logging::LogLevel::Error) \
136+
{ \
137+
Logging::Log(message, Logging::LogLevel::Error); \
138+
}
139+
140+
#define LOG_FATAL(message) \
141+
if (Logging::HasCallback() && Logging::GetLogLevel() <= Logging::LogLevel::Fatal) \
142+
{ \
143+
Logging::Log(message, Logging::LogLevel::Fatal); \
144+
}
145+
146+
} // namespace libprojectM

tests/libprojectM/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ test_api_headers(TestMainAPIHeaders
2727
)
2828

2929
add_executable(projectM-unittest
30-
WaveformAlignerTest.cpp
30+
LoggingTest.cpp
3131
PresetFileParserTest.cpp
32+
WaveformAlignerTest.cpp
3233

3334
$<TARGET_OBJECTS:Audio>
3435
$<TARGET_OBJECTS:MilkdropPreset>

0 commit comments

Comments
 (0)