feat: 实现日志与调试系统(Debug模块)

- LogLevel: 日志级别枚举 (Verbose, Debug, Info, Warning, Error, Fatal)
- LogCategory: 日志分类 (General, Rendering, Physics, Memory, Threading等)
- ILogSink: 日志输出接口
- ConsoleLogSink: 控制台输出, 支持Windows颜色
- FileLogSink: 文件日志输出
- FileWriter: 文件写入器
- Logger: 日志管理器, 支持多sink, 分类控制
- Profiler: 性能分析器
- 单元测试覆盖
This commit is contained in:
2026-03-13 20:53:57 +08:00
parent dc9b0751cb
commit 83fd517974
22 changed files with 780 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
#pragma once
#include "../Containers/String.h"
#include <cstdio>
namespace XCEngine {
namespace Core {
class FileWriter {
public:
FileWriter();
FileWriter(const char* filePath, bool append = false);
~FileWriter();
bool Open(const char* filePath, bool append = false);
void Close();
bool Write(const char* data, size_t length);
bool Write(const Containers::String& str);
bool Flush();
bool IsOpen() const { return m_file != nullptr; }
private:
FILE* m_file = nullptr;
};
} // namespace Core
} // namespace XCEngine

View File

@@ -0,0 +1,25 @@
#pragma once
#include "ILogSink.h"
#include "LogLevel.h"
namespace XCEngine {
namespace Debug {
class ConsoleLogSink : public ILogSink {
public:
ConsoleLogSink();
~ConsoleLogSink() override;
void Log(const LogEntry& entry) override;
void Flush() override;
void SetColorOutput(bool enable);
void SetMinimumLevel(LogLevel level);
private:
bool m_colorOutput = true;
LogLevel m_minimumLevel = LogLevel::Verbose;
};
} // namespace Debug
} // namespace XCEngine

View File

@@ -0,0 +1,12 @@
#pragma once
#include "LogLevel.h"
#include "LogCategory.h"
#include "LogEntry.h"
#include "ILogSink.h"
#include "ConsoleLogSink.h"
#include "FileLogSink.h"
#include "Logger.h"
#include "Profiler.h"
#include "../Core/FileWriter.h"

View File

@@ -0,0 +1,25 @@
#pragma once
#include "ILogSink.h"
#include "LogEntry.h"
#include "../Containers/String.h"
#include "../Core/FileWriter.h"
namespace XCEngine {
namespace Debug {
class FileLogSink : public ILogSink {
public:
FileLogSink(const Containers::String& filePath);
~FileLogSink() override;
void Log(const LogEntry& entry) override;
void Flush() override;
private:
Containers::String m_filePath;
Core::FileWriter m_writer;
};
} // namespace Debug
} // namespace XCEngine

View File

@@ -0,0 +1,16 @@
#pragma once
#include "LogEntry.h"
namespace XCEngine {
namespace Debug {
class ILogSink {
public:
virtual ~ILogSink() = default;
virtual void Log(const LogEntry& entry) = 0;
virtual void Flush() = 0;
};
} // namespace Debug
} // namespace XCEngine

View File

@@ -0,0 +1,22 @@
#pragma once
namespace XCEngine {
namespace Debug {
enum class LogCategory {
General,
Rendering,
Physics,
Audio,
Scripting,
Network,
Memory,
Threading,
FileSystem,
Custom
};
const char* LogCategoryToString(LogCategory category);
} // namespace Debug
} // namespace XCEngine

View File

@@ -0,0 +1,22 @@
#pragma once
#include "LogLevel.h"
#include "LogCategory.h"
#include "../Containers/String.h"
namespace XCEngine {
namespace Debug {
struct LogEntry {
LogLevel level;
LogCategory category;
Containers::String message;
Containers::String file;
int32_t line;
Containers::String function;
uint64_t timestamp;
uint32_t threadId;
};
} // namespace Debug
} // namespace XCEngine

View File

@@ -0,0 +1,20 @@
#pragma once
#include <cstdint>
namespace XCEngine {
namespace Debug {
enum class LogLevel : uint8_t {
Verbose = 0,
Debug = 1,
Info = 2,
Warning = 3,
Error = 4,
Fatal = 5
};
const char* LogLevelToString(LogLevel level);
} // namespace Debug
} // namespace XCEngine

View File

@@ -0,0 +1,59 @@
#pragma once
#include "LogLevel.h"
#include "LogCategory.h"
#include "ILogSink.h"
#include "../Containers/String.h"
#include "../Threading/Mutex.h"
#include <vector>
#include <memory>
namespace XCEngine {
namespace Debug {
class Logger {
public:
static Logger& Get();
void Initialize();
void Shutdown();
void AddSink(std::unique_ptr<ILogSink> sink);
void RemoveSink(ILogSink* sink);
void Log(LogLevel level, LogCategory category,
const Containers::String& message, const char* file = nullptr,
int32_t line = 0, const char* function = nullptr);
void Verbose(LogCategory category, const Containers::String& message);
void Debug(LogCategory category, const Containers::String& message);
void Info(LogCategory category, const Containers::String& message);
void Warning(LogCategory category, const Containers::String& message);
void Error(LogCategory category, const Containers::String& message);
void Fatal(LogCategory category, const Containers::String& message);
void SetMinimumLevel(LogLevel level);
void SetCategoryEnabled(LogCategory category, bool enabled);
private:
Logger() = default;
~Logger() = default;
std::vector<std::unique_ptr<ILogSink>> m_sinks;
LogLevel m_minimumLevel = LogLevel::Verbose;
bool m_categoryEnabled[11] = { true };
Threading::Mutex m_mutex;
bool m_initialized = false;
};
#define XE_LOG(category, level, message) \
XCEngine::Debug::Logger::Get().Log(level, category, message, __FILE__, __LINE__, __FUNCTION__)
#define XE_ASSERT(condition, message) \
if (!(condition)) { \
XCEngine::Debug::Logger::Get().Fatal(XCEngine::Debug::LogCategory::General, message); \
}
} // namespace Debug
} // namespace XCEngine

View File

@@ -0,0 +1,56 @@
#pragma once
#include "LogLevel.h"
#include "../Containers/String.h"
#include <vector>
#include <chrono>
#include <unordered_map>
namespace XCEngine {
namespace Debug {
class Profiler {
public:
static Profiler& Get();
void Initialize();
void Shutdown();
void BeginProfile(const char* name);
void EndProfile();
void BeginFrame();
void EndFrame();
void MarkEvent(const char* name, uint64_t timestamp, uint32_t threadId);
void SetMarker(const char* name, uint32_t color);
void ExportChromeTracing(const Containers::String& filePath);
private:
struct ProfileNode {
const char* name;
uint64_t startTime;
uint64_t endTime;
uint32_t threadId;
};
struct ProfileSample {
const char* name;
uint64_t duration;
uint32_t threadId;
};
std::vector<ProfileNode> m_profileStack;
std::vector<ProfileSample> m_samples;
uint64_t m_frameStartTime = 0;
bool m_initialized = false;
};
#define XE_PROFILE_BEGIN(name) XCEngine::Debug::Profiler::Get().BeginProfile(name)
#define XE_PROFILE_END() XCEngine::Debug::Profiler::Get().EndProfile()
#define XE_PROFILE_FUNCTION() XE_PROFILE_BEGIN(__FUNCTION__)
} // namespace Debug
} // namespace XCEngine