229 lines
5.8 KiB
C
229 lines
5.8 KiB
C
|
/*
|
||
|
* Logging.h
|
||
|
* ---------
|
||
|
* Purpose: General logging
|
||
|
* Notes : (currently none)
|
||
|
* Authors: OpenMPT Devs
|
||
|
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
|
||
|
*/
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#include "openmpt/all/BuildSettings.hpp"
|
||
|
|
||
|
#include "openmpt/logging/Logger.hpp"
|
||
|
|
||
|
#include "mptPathString.h"
|
||
|
#include "mptString.h"
|
||
|
|
||
|
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
|
||
|
#include <atomic>
|
||
|
#endif
|
||
|
|
||
|
OPENMPT_NAMESPACE_BEGIN
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
|
||
|
Build time logging configuration:
|
||
|
|
||
|
* #define MPT_LOG_GLOBAL_LEVEL_STATIC
|
||
|
#define MPT_LOG_GLOBAL_LEVEL #
|
||
|
Define the former (to anything) and the latter (to one of the log levels
|
||
|
below) in order to statically select the verbosity of logging at build time.
|
||
|
MPT_LOG calls that exceed the specified logging level will get dead-code
|
||
|
eliminated at compile time.
|
||
|
This especially means that, when setting MPT_LOG_GLOBAL_LEVEL to 0, no
|
||
|
MPT_LOG call (with a constant level parameter) remains in the resulting
|
||
|
binary, however, they still do get parsed and properly type checked by the
|
||
|
compiler.
|
||
|
|
||
|
|
||
|
Logging:
|
||
|
|
||
|
If the context is related to a particular CSoundfile instance, use
|
||
|
CSoundfile::AddToLog.
|
||
|
|
||
|
Logging a simple message:
|
||
|
MPT_LOG_GLOBAL(LogWarning, "sounddev", "some message");
|
||
|
MPT_LOG_GLOBAL(LogWarning, "sounddev", U_("some message"));
|
||
|
Facility is some course grained code section identifier (more coarse grained
|
||
|
than the current file name probably), useful to do some selective logging.
|
||
|
|
||
|
Logging a more complex message:
|
||
|
MPT_LOG_GLOBAL(LogWarning, "sounddev", MPT_UFORMAT("Some message: foo={}, bar=0x{}")(foo, mpt::ufmt::hex0<8>(bar)));
|
||
|
|
||
|
Note that even with full enabled logging and a runtime configurable logging
|
||
|
level, the runtime overhead of a MPT_LOG_GLOBAL(level, facility, text) call is just a
|
||
|
single conditional in case the verbosity does not require logging the respective
|
||
|
message. Even the expression "text" is not evaluated.
|
||
|
|
||
|
|
||
|
*/
|
||
|
|
||
|
|
||
|
inline mpt::ustring LogLevelToString(LogLevel level)
|
||
|
{
|
||
|
switch(level)
|
||
|
{
|
||
|
case LogError: return U_("error"); break;
|
||
|
case LogWarning: return U_("warning"); break;
|
||
|
case LogNotification: return U_("notify"); break;
|
||
|
case LogInformation: return U_("info"); break;
|
||
|
case LogDebug: return U_("debug"); break;
|
||
|
}
|
||
|
return U_("unknown");
|
||
|
}
|
||
|
|
||
|
|
||
|
class ILog
|
||
|
{
|
||
|
protected:
|
||
|
virtual ~ILog() { }
|
||
|
public:
|
||
|
virtual void AddToLog(LogLevel level, const mpt::ustring &text) const = 0;
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
namespace mpt
|
||
|
{
|
||
|
namespace log
|
||
|
{
|
||
|
|
||
|
|
||
|
|
||
|
#if defined(MPT_LOG_GLOBAL_LEVEL_STATIC)
|
||
|
#if (MPT_LOG_GLOBAL_LEVEL <= 0)
|
||
|
// All logging has beeen statically disabled.
|
||
|
// All logging code gets compiled and immediately dead-code eliminated.
|
||
|
#define MPT_LOG_IS_DISABLED
|
||
|
#endif
|
||
|
inline constexpr int GlobalLogLevel = MPT_LOG_GLOBAL_LEVEL ;
|
||
|
#else
|
||
|
extern int GlobalLogLevel;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if defined(MODPLUG_TRACKER) && !defined(MPT_LOG_IS_DISABLED)
|
||
|
extern bool FileEnabled;
|
||
|
extern bool DebuggerEnabled;
|
||
|
extern bool ConsoleEnabled;
|
||
|
void SetFacilities(const std::string &solo, const std::string &blocked);
|
||
|
bool IsFacilityActive(const char *facility) noexcept;
|
||
|
#else
|
||
|
MPT_FORCEINLINE bool IsFacilityActive(const char * /*facility*/ ) noexcept { return true; }
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
class GlobalLogger final
|
||
|
: public ILogger
|
||
|
{
|
||
|
public:
|
||
|
GlobalLogger() = default;
|
||
|
~GlobalLogger() final = default;
|
||
|
public:
|
||
|
bool IsLevelActive(LogLevel level) const noexcept override
|
||
|
{
|
||
|
return (mpt::log::GlobalLogLevel >= level);
|
||
|
}
|
||
|
bool IsFacilityActive(const char *facility) const noexcept override
|
||
|
{
|
||
|
return mpt::log::IsFacilityActive(facility);
|
||
|
}
|
||
|
void SendLogMessage(const mpt::source_location &loc, LogLevel level, const char *facility, const mpt::ustring &message) const override;
|
||
|
};
|
||
|
|
||
|
|
||
|
#define MPT_LOG_GLOBAL(level, facility, text) MPT_LOG(mpt::log::GlobalLogger{}, (level), (facility), (text))
|
||
|
|
||
|
|
||
|
|
||
|
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
|
||
|
|
||
|
namespace Trace {
|
||
|
|
||
|
// This is not strictly thread safe in all corner cases because of missing barriers.
|
||
|
// We do not care in order to not harm the fast path with additional barriers.
|
||
|
// Enabled tracing incurs a runtime overhead with multiple threads as a global atomic variable
|
||
|
// gets modified.
|
||
|
// This cacheline bouncing does not matter at all
|
||
|
// if there are not multiple thread adding trace points at high frequency (way greater than 1000Hz),
|
||
|
// which, in OpenMPT, is only ever the case for just a single thread (the audio thread), if at all.
|
||
|
extern std::atomic<bool> g_Enabled;
|
||
|
inline bool IsEnabled() { return g_Enabled; }
|
||
|
|
||
|
enum class Direction : int8
|
||
|
{
|
||
|
Unknown = 0,
|
||
|
Enter = 1,
|
||
|
Leave = -1,
|
||
|
};
|
||
|
|
||
|
MPT_NOINLINE void Trace(const mpt::source_location & loc, Direction direction = Direction::Unknown) noexcept;
|
||
|
|
||
|
enum ThreadKind {
|
||
|
ThreadKindGUI,
|
||
|
ThreadKindAudio,
|
||
|
ThreadKindNotify,
|
||
|
ThreadKindWatchdir,
|
||
|
};
|
||
|
|
||
|
void Enable(std::size_t numEntries);
|
||
|
void Disable();
|
||
|
|
||
|
void SetThreadId(mpt::log::Trace::ThreadKind kind, uint32 id);
|
||
|
uint32 GetThreadId(mpt::log::Trace::ThreadKind kind);
|
||
|
|
||
|
void Seal();
|
||
|
bool Dump(const mpt::PathString &filename);
|
||
|
|
||
|
class Scope
|
||
|
{
|
||
|
private:
|
||
|
const mpt::source_location loc;
|
||
|
public:
|
||
|
MPT_FORCEINLINE Scope(mpt::source_location loc) noexcept
|
||
|
: loc(loc)
|
||
|
{
|
||
|
if(mpt::log::Trace::g_Enabled)
|
||
|
{
|
||
|
mpt::log::Trace::Trace(loc, mpt::log::Trace::Direction::Enter);
|
||
|
}
|
||
|
}
|
||
|
MPT_FORCEINLINE ~Scope() noexcept
|
||
|
{
|
||
|
if(mpt::log::Trace::g_Enabled)
|
||
|
{
|
||
|
mpt::log::Trace::Trace(loc, mpt::log::Trace::Direction::Leave);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
#define MPT_TRACE_CONCAT_HELPER(x, y) x ## y
|
||
|
#define MPT_TRACE_CONCAT(x, y) MPT_TRACE_CONCAT_HELPER(x, y)
|
||
|
|
||
|
#define MPT_TRACE_SCOPE() mpt::log::Trace::Scope MPT_TRACE_CONCAT(MPT_TRACE_VAR, __LINE__)(MPT_SOURCE_LOCATION_CURRENT())
|
||
|
|
||
|
#define MPT_TRACE() do { if(mpt::log::Trace::g_Enabled) { mpt::log::Trace::Trace(MPT_SOURCE_LOCATION_CURRENT()); } } while(0)
|
||
|
|
||
|
} // namespace Trace
|
||
|
|
||
|
#else // !MODPLUG_TRACKER
|
||
|
|
||
|
#define MPT_TRACE_SCOPE() do { } while(0)
|
||
|
|
||
|
#define MPT_TRACE() do { } while(0)
|
||
|
|
||
|
#endif // MODPLUG_TRACKER
|
||
|
|
||
|
|
||
|
|
||
|
} // namespace log
|
||
|
} // namespace mpt
|
||
|
|
||
|
|
||
|
OPENMPT_NAMESPACE_END
|