feat: Enhance logging with detailed function tracing and argument conversion utilities

This commit is contained in:
2026-01-04 01:58:08 +00:00
parent 99a4f756fd
commit 4edf0a2bbf
9 changed files with 235 additions and 90 deletions

View File

@@ -23,7 +23,8 @@ struct DecodedAudio {
};
DecodedAudio DecodeOgg(const std::filesystem::path& path) {
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs(path.string());
using sdl3cpp::logging::ToString;
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs("DecodeOgg", path.string());
FILE* file = std::fopen(path.string().c_str(), "rb");
if (!file) {
throw std::runtime_error("Failed to open audio file: " + path.string());
@@ -84,7 +85,8 @@ AudioPlayer::~AudioPlayer() {
}
void AudioPlayer::PlayBackground(const std::filesystem::path& path, bool loop) {
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs(path.string(), loop);
using sdl3cpp::logging::ToString;
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs("PlayBackground", path.string() + " " + ToString(loop));
DecodedAudio clip = DecodeOgg(path);
EnsureStream(clip.sampleRate, clip.channels);
std::scoped_lock lock(voicesMutex_);
@@ -92,7 +94,8 @@ void AudioPlayer::PlayBackground(const std::filesystem::path& path, bool loop) {
}
void AudioPlayer::PlayEffect(const std::filesystem::path& path, bool loop) {
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs(path.string(), loop);
using sdl3cpp::logging::ToString;
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs("PlayEffect", path.string() + " " + ToString(loop));
DecodedAudio clip = DecodeOgg(path);
EnsureStream(clip.sampleRate, clip.channels);
std::scoped_lock lock(voicesMutex_);
@@ -105,7 +108,8 @@ void AudioPlayer::AudioStreamCallback(void* userdata, SDL_AudioStream* stream, i
}
void AudioPlayer::FeedStream(SDL_AudioStream* stream, int totalAmount) {
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs(static_cast<void*>(stream), totalAmount);
using sdl3cpp::logging::ToString;
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs("FeedStream", ToString(static_cast<const void*>(stream)) + " " + ToString(totalAmount));
if (totalAmount <= 0 || !stream_) {
return;
}
@@ -147,7 +151,8 @@ void AudioPlayer::FeedStream(SDL_AudioStream* stream, int totalAmount) {
}
void AudioPlayer::EnsureStream(int sampleRate, int channels) {
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs(sampleRate, channels);
using sdl3cpp::logging::ToString;
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs("EnsureStream", ToString(sampleRate) + " " + ToString(channels));
if (sampleRate <= 0 || channels <= 0) {
throw std::runtime_error("Audio format is invalid");
}
@@ -178,7 +183,8 @@ void AudioPlayer::EnsureStream(int sampleRate, int channels) {
}
void AudioPlayer::AddVoiceSamples(AudioVoice& voice, std::vector<int32_t>& mixBuffer, size_t sampleCount) {
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs(voice.data.size(), mixBuffer.size(), sampleCount);
using sdl3cpp::logging::ToString;
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs("AddVoiceSamples", ToString(voice.data.size()) + " " + ToString(mixBuffer.size()) + " " + ToString(sampleCount));
if (voice.data.empty()) {
voice.active = false;
return;

View File

@@ -85,8 +85,9 @@ void ShowErrorDialog(const char* title, const std::string& message) {
Sdl3App::Sdl3App(const std::filesystem::path& scriptPath, bool luaDebug)
: scriptEngine_(scriptPath, luaDebug),
scriptDirectory_(scriptPath.parent_path()) {
sdl3cpp::logging::TraceGuard trace;;
sdl3cpp::logging::Logger::GetInstance().TraceVariable("scriptPath", scriptPath);
sdl3cpp::logging::TraceGuard trace;
auto& logger = sdl3cpp::logging::Logger::GetInstance();
logger.TraceVariable("scriptPath", scriptPath.string());
}
bool Sdl3App::ShouldStop() {
@@ -102,9 +103,10 @@ void Sdl3App::Run() {
}
void Sdl3App::InitSDL() {
sdl3cpp::logging::TraceGuard trace;;
sdl3cpp::logging::Logger::GetInstance().TraceVariable("kWidth", kWidth);
sdl3cpp::logging::Logger::GetInstance().TraceVariable("kHeight", kHeight);
sdl3cpp::logging::TraceGuard trace;
auto& logger = sdl3cpp::logging::Logger::GetInstance();
logger.TraceVariable("kWidth", kWidth);
logger.TraceVariable("kHeight", kHeight);
try {
ThrowSdlErrorIfFailed(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO), "SDL_Init failed");

View File

@@ -29,7 +29,8 @@ std::optional<std::filesystem::path> GetUserConfigDirectory() {
#ifdef _WIN32
namespace {
std::string FormatWin32Error(DWORD errorCode) {
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs(errorCode);
using sdl3cpp::logging::ToString;
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs("FormatWin32Error", ToString(static_cast<unsigned long>(errorCode)));
if (errorCode == ERROR_SUCCESS) {
return "ERROR_SUCCESS";
}

View File

@@ -1,47 +1,68 @@
#include "logging/logger.hpp"
#include <atomic>
#include <chrono>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <mutex>
#include <sstream>
#include <thread>
namespace sdl3cpp::logging {
// Implementation class that holds all the C++ magic
class LoggerImpl {
public:
std::atomic<LogLevel> level_;
bool consoleEnabled_;
std::unique_ptr<std::ofstream> fileStream_;
std::mutex mutex_;
LoggerImpl() : level_(LogLevel::INFO), consoleEnabled_(true) {}
~LoggerImpl() {
if (fileStream_) {
fileStream_->close();
}
}
};
Logger& Logger::GetInstance() {
static Logger instance;
return instance;
}
Logger::Logger() : level_(LogLevel::INFO), consoleEnabled_(true) {}
Logger::Logger() : impl_(new LoggerImpl()) {}
Logger::~Logger() {
if (fileStream_) {
fileStream_->close();
}
delete impl_;
}
void Logger::SetLevel(LogLevel level) {
level_.store(level, std::memory_order_relaxed);
impl_->level_.store(level, std::memory_order_relaxed);
}
LogLevel Logger::GetLevel() const {
return level_.load(std::memory_order_relaxed);
return impl_->level_.load(std::memory_order_relaxed);
}
void Logger::SetOutputFile(const std::string& filename) {
std::lock_guard<std::mutex> lock(mutex_);
if (fileStream_) {
fileStream_->close();
std::lock_guard<std::mutex> lock(impl_->mutex_);
if (impl_->fileStream_) {
impl_->fileStream_->close();
}
fileStream_ = std::make_unique<std::ofstream>(filename, std::ios::app);
if (!fileStream_->is_open()) {
impl_->fileStream_ = std::make_unique<std::ofstream>(filename, std::ios::app);
if (!impl_->fileStream_->is_open()) {
// Fallback to console if file can't be opened
std::cerr << "Failed to open log file: " << filename << std::endl;
fileStream_.reset();
impl_->fileStream_.reset();
}
}
void Logger::EnableConsoleOutput(bool enable) {
consoleEnabled_ = enable;
impl_->consoleEnabled_ = enable;
}
void Logger::Log(LogLevel level, const std::string& message) {
@@ -49,14 +70,14 @@ void Logger::Log(LogLevel level, const std::string& message) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
std::lock_guard<std::mutex> lock(impl_->mutex_);
std::string formattedMessage = FormatMessage(level, message);
if (consoleEnabled_) {
if (impl_->consoleEnabled_) {
WriteToConsole(level, formattedMessage);
}
if (fileStream_) {
if (impl_->fileStream_) {
WriteToFile(formattedMessage);
}
}
@@ -85,9 +106,9 @@ void Logger::WriteToConsole(LogLevel level, const std::string& message) {
}
void Logger::WriteToFile(const std::string& message) {
if (fileStream_) {
*fileStream_ << message << std::endl;
fileStream_->flush();
if (impl_->fileStream_) {
*impl_->fileStream_ << message << std::endl;
impl_->fileStream_->flush();
}
}
@@ -105,4 +126,76 @@ std::string Logger::FormatMessage(LogLevel level, const std::string& message) {
return oss.str();
}
void Logger::Trace(const std::string& message) {
Log(LogLevel::TRACE, message);
}
void Logger::Debug(const std::string& message) {
Log(LogLevel::DEBUG, message);
}
void Logger::Info(const std::string& message) {
Log(LogLevel::INFO, message);
}
void Logger::Warn(const std::string& message) {
Log(LogLevel::WARN, message);
}
void Logger::Error(const std::string& message) {
Log(LogLevel::ERROR, message);
}
void Logger::TraceFunction(const std::string& funcName) {
if (GetLevel() <= LogLevel::TRACE) {
Trace("Entering " + funcName);
}
}
void Logger::TraceVariable(const std::string& name, const std::string& value) {
if (GetLevel() <= LogLevel::TRACE) {
Trace(name + " = " + value);
}
}
void Logger::TraceVariable(const std::string& name, int value) {
TraceVariable(name, std::to_string(value));
}
void Logger::TraceVariable(const std::string& name, size_t value) {
TraceVariable(name, std::to_string(value));
}
void Logger::TraceVariable(const std::string& name, bool value) {
TraceVariable(name, value ? "true" : "false");
}
void Logger::TraceVariable(const std::string& name, float value) {
TraceVariable(name, std::to_string(value));
}
void Logger::TraceVariable(const std::string& name, double value) {
TraceVariable(name, std::to_string(value));
}
void Logger::TraceFunctionWithArgs(const std::string& description, const std::string& args) {
if (GetLevel() <= LogLevel::TRACE) {
Trace(description + ": " + args);
}
}
TraceGuard::TraceGuard(const std::string& funcName)
: funcName_(funcName), ended_(false) {
if (!funcName_.empty()) {
Logger::GetInstance().Trace("Entering " + funcName_);
}
}
void TraceGuard::End() {
if (!ended_ && !funcName_.empty()) {
Logger::GetInstance().Trace("Exiting " + funcName_);
ended_ = true;
}
}
} // namespace sdl3cpp::logging

View File

@@ -1,14 +1,7 @@
#ifndef SDL3CPP_LOGGING_LOGGER_HPP
#define SDL3CPP_LOGGING_LOGGER_HPP
#include <atomic>
#include <iostream>
#include <sstream>
#include <string>
#include <mutex>
#include <fstream>
#include <memory>
#include <source_location>
namespace sdl3cpp::logging {
@@ -21,6 +14,9 @@ enum class LogLevel {
OFF = 5
};
// Forward declaration to hide implementation details
class LoggerImpl;
class Logger {
public:
static Logger& GetInstance();
@@ -35,67 +31,48 @@ public:
void Log(LogLevel level, const char* message);
// Convenience methods
void Trace(const std::string& message) { Log(LogLevel::TRACE, message); }
void Debug(const std::string& message) { Log(LogLevel::DEBUG, message); }
void Info(const std::string& message) { Log(LogLevel::INFO, message); }
void Warn(const std::string& message) { Log(LogLevel::WARN, message); }
void Error(const std::string& message) { Log(LogLevel::ERROR, message); }
void Trace(const std::string& message);
void Debug(const std::string& message);
void Info(const std::string& message);
void Warn(const std::string& message);
void Error(const std::string& message);
// Tracing methods
void TraceFunction(const std::string& funcName) {
if (GetLevel() <= LogLevel::TRACE) {
Trace(std::string("Entering ") + funcName);
}
}
void TraceFunction(const std::string& funcName);
template <typename T>
void TraceVariable(const std::string& name, const T& value) {
if (GetLevel() <= LogLevel::TRACE) {
std::ostringstream oss;
oss << name << " = " << value;
Trace(oss.str());
}
}
// TraceVariable overloads for common types
void TraceVariable(const std::string& name, const std::string& value);
void TraceVariable(const std::string& name, int value);
void TraceVariable(const std::string& name, size_t value);
void TraceVariable(const std::string& name, bool value);
void TraceVariable(const std::string& name, float value);
void TraceVariable(const std::string& name, double value);
template <typename... Args>
void TraceFunctionWithArgs(const Args&... args, const std::source_location& location = std::source_location::current()) {
if (GetLevel() <= LogLevel::TRACE) {
std::ostringstream oss;
oss << "Entering " << location.function_name() << " with args: ";
((oss << args << " "), ...);
Trace(oss.str());
}
}
void TraceFunctionWithArgs(const std::string& description, const std::string& args);
private:
Logger();
~Logger();
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
// Non-copyable (Java final class pattern)
Logger(const Logger&);
Logger& operator=(const Logger&);
std::string LevelToString(LogLevel level) const;
std::string FormatMessage(LogLevel level, const std::string& message);
void WriteToConsole(LogLevel level, const std::string& message);
void WriteToFile(const std::string& message);
std::atomic<LogLevel> level_;
bool consoleEnabled_;
std::unique_ptr<std::ofstream> fileStream_;
std::mutex mutex_;
LoggerImpl* impl_;
};
class TraceGuard {
public:
explicit TraceGuard(const std::source_location& location = std::source_location::current())
: funcName_(location.function_name()) {
Logger::GetInstance().Trace("Entering " + funcName_);
}
~TraceGuard() {
Logger::GetInstance().Trace("Exiting " + funcName_);
}
explicit TraceGuard(const std::string& funcName = "");
void End();
private:
std::string funcName_;
bool ended_;
};
} // namespace sdl3cpp::logging

View File

@@ -0,0 +1,37 @@
#include "string_utils.hpp"
#include <sstream>
#include <string>
namespace sdl3cpp::logging {
std::string ToString(int value) {
return std::to_string(value);
}
std::string ToString(size_t value) {
return std::to_string(value);
}
std::string ToString(bool value) {
return value ? "true" : "false";
}
std::string ToString(float value) {
return std::to_string(value);
}
std::string ToString(double value) {
return std::to_string(value);
}
std::string ToString(const void* value) {
std::ostringstream oss;
oss << value;
return oss.str();
}
std::string ToString(unsigned long value) {
return std::to_string(value);
}
} // namespace sdl3cpp::logging

View File

@@ -0,0 +1,19 @@
#ifndef SDL3CPP_LOGGING_STRING_UTILS_HPP
#define SDL3CPP_LOGGING_STRING_UTILS_HPP
#include <string>
namespace sdl3cpp::logging {
// Helper functions to convert common types to strings for logging
std::string ToString(int value);
std::string ToString(size_t value);
std::string ToString(bool value);
std::string ToString(float value);
std::string ToString(double value);
std::string ToString(const void* value);
std::string ToString(unsigned long value);
} // namespace sdl3cpp::logging
#endif // SDL3CPP_LOGGING_STRING_UTILS_HPP

View File

@@ -72,7 +72,8 @@ RuntimeConfig GenerateDefaultRuntimeConfig(const char* argv0) {
}
RuntimeConfig LoadRuntimeConfigFromJson(const std::filesystem::path& configPath, bool dumpConfig) {
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs(configPath.string(), dumpConfig);
using sdl3cpp::logging::ToString;
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs("LoadRuntimeConfigFromJson", configPath.string() + " " + ToString(dumpConfig));
std::ifstream configStream(configPath);
if (!configStream) {
throw std::runtime_error("Failed to open config file: " + configPath.string());
@@ -237,9 +238,10 @@ AppOptions ParseCommandLine(int argc, char** argv) {
}
void LogRuntimeConfig(const RuntimeConfig& config) {
sdl3cpp::logging::Logger::GetInstance().TraceVariable("config.width", config.width);
sdl3cpp::logging::Logger::GetInstance().TraceVariable("config.height", config.height);
sdl3cpp::logging::Logger::GetInstance().TraceVariable("config.scriptPath", config.scriptPath);
auto& logger = sdl3cpp::logging::Logger::GetInstance();
logger.TraceVariable("config.width", config.width);
logger.TraceVariable("config.height", config.height);
logger.TraceVariable("config.scriptPath", config.scriptPath.string());
}
void WriteRuntimeConfigJson(const RuntimeConfig& runtimeConfig,

View File

@@ -11,7 +11,8 @@
namespace sdl3cpp::script {
std::array<float, 3> ReadVector3(lua_State* L, int index) {
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs(static_cast<void*>(L), index);
using sdl3cpp::logging::ToString;
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs("ReadVector3", ToString(static_cast<const void*>(L)) + " " + ToString(index));
std::array<float, 3> result{};
int absIndex = lua_absindex(L, index);
size_t len = lua_rawlen(L, absIndex);
@@ -31,7 +32,8 @@ std::array<float, 3> ReadVector3(lua_State* L, int index) {
}
std::array<float, 4> ReadQuaternion(lua_State* L, int index) {
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs(static_cast<void*>(L), index);
using sdl3cpp::logging::ToString;
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs("ReadQuaternion", ToString(static_cast<const void*>(L)) + " " + ToString(index));
std::array<float, 4> result{};
int absIndex = lua_absindex(L, index);
size_t len = lua_rawlen(L, absIndex);
@@ -51,7 +53,8 @@ std::array<float, 4> ReadQuaternion(lua_State* L, int index) {
}
std::array<float, 16> ReadMatrix(lua_State* L, int index) {
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs(static_cast<void*>(L), index);
using sdl3cpp::logging::ToString;
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs("ReadMatrix", ToString(static_cast<const void*>(L)) + " " + ToString(index));
std::array<float, 16> result{};
int absIndex = lua_absindex(L, index);
size_t len = lua_rawlen(L, absIndex);
@@ -71,7 +74,8 @@ std::array<float, 16> ReadMatrix(lua_State* L, int index) {
}
std::string GetLuaError(lua_State* L) {
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs(static_cast<void*>(L));
using sdl3cpp::logging::ToString;
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs("GetLuaError", ToString(static_cast<const void*>(L)));
const char* message = lua_tostring(L, -1);
return message ? message : "unknown lua error";
}
@@ -85,17 +89,20 @@ std::array<float, 16> IdentityMatrix() {
}
glm::vec3 ToVec3(const std::array<float, 3>& value) {
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs(value[0], value[1], value[2]);
using sdl3cpp::logging::ToString;
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs("ToVec3", ToString(value[0]) + " " + ToString(value[1]) + " " + ToString(value[2]));
return glm::vec3(value[0], value[1], value[2]);
}
glm::quat ToQuat(const std::array<float, 4>& value) {
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs(value[0], value[1], value[2], value[3]);
using sdl3cpp::logging::ToString;
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs("ToQuat", ToString(value[0]) + " " + ToString(value[1]) + " " + ToString(value[2]) + " " + ToString(value[3]));
return glm::quat(value[3], value[0], value[1], value[2]);
}
void PushMatrix(lua_State* L, const glm::mat4& matrix) {
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs(static_cast<void*>(L));
using sdl3cpp::logging::ToString;
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs("PushMatrix", ToString(static_cast<const void*>(L)));
lua_newtable(L);
const float* ptr = glm::value_ptr(matrix);
for (int i = 0; i < 16; ++i) {
@@ -105,7 +112,8 @@ void PushMatrix(lua_State* L, const glm::mat4& matrix) {
}
int LuaGlmMatrixFromTransform(lua_State* L) {
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs(static_cast<void*>(L));
using sdl3cpp::logging::ToString;
sdl3cpp::logging::Logger::GetInstance().TraceFunctionWithArgs("LuaGlmMatrixFromTransform", ToString(static_cast<const void*>(L)));
std::array<float, 3> translation = ReadVector3(L, 1);
std::array<float, 4> rotation = ReadQuaternion(L, 2);
glm::vec3 pos = ToVec3(translation);