Skip to content

Commit c23f1b9

Browse files
committed
Add a new logging class
1 parent 8e22895 commit c23f1b9

File tree

5 files changed

+515
-1
lines changed

5 files changed

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

src/libprojectM/Logging.hpp

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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+
* The default log level used if no log level is set (LogLevel::Information)
94+
*/
95+
static const LogLevel m_defaultLogLevel;
96+
97+
private:
98+
/**
99+
* @brief Returns the active callback for this thread.
100+
* If the thread has a local callback, this is returned, otherwise the global callback.
101+
* @return A pointer to the active callback function, or nullptr if none is registered.
102+
*/
103+
static auto GetLoggingCallback() -> UserCallback;
104+
105+
static UserCallback m_globalCallback; //!< The global callback function.
106+
thread_local static UserCallback m_threadCallback; //!< The thread-specific callback function.
107+
108+
static LogLevel m_globalLogLevel; //!< The global log level.
109+
thread_local static LogLevel m_threadLogLevel; //!< The thread-specific log level.
110+
};
111+
112+
#define LOG_TRACE(message) \
113+
if (Logging::HasCallback() && Logging::GetLogLevel() == Logging::LogLevel::Trace) \
114+
{ \
115+
Logging::Log(message, Logging::LogLevel::Trace); \
116+
}
117+
118+
#define LOG_DEBUG(message) \
119+
if (Logging::HasCallback() && Logging::GetLogLevel() <= Logging::LogLevel::Debug) \
120+
{ \
121+
Logging::Log(message, Logging::LogLevel::Debug); \
122+
}
123+
124+
#define LOG_INFO(message) \
125+
if (Logging::HasCallback() && Logging::GetLogLevel() <= Logging::LogLevel::Information) \
126+
{ \
127+
Logging::Log(message, Logging::LogLevel::Information); \
128+
}
129+
130+
#define LOG_WARN(message) \
131+
if (Logging::HasCallback() && Logging::GetLogLevel() <= Logging::LogLevel::Warning) \
132+
{ \
133+
Logging::Log(message, Logging::LogLevel::Warning); \
134+
}
135+
136+
#define LOG_ERROR(message) \
137+
if (Logging::HasCallback() && Logging::GetLogLevel() <= Logging::LogLevel::Error) \
138+
{ \
139+
Logging::Log(message, Logging::LogLevel::Error); \
140+
}
141+
142+
#define LOG_FATAL(message) \
143+
if (Logging::HasCallback() && Logging::GetLogLevel() <= Logging::LogLevel::Fatal) \
144+
{ \
145+
Logging::Log(message, Logging::LogLevel::Fatal); \
146+
}
147+
148+
} // 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)