From 642d29634a541b93a59c4496b43ad7d8e2fd4d39 Mon Sep 17 00:00:00 2001 From: Cristian Gora Date: Thu, 1 Oct 2026 03:04:17 +0200 Subject: [PATCH] Implemented a custom logger --- src/framebuffer.cpp | 5 ++- src/loader.cpp | 15 ++++---- src/logger.hpp | 87 +++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 2 +- src/mesh.cpp | 2 + src/renderer.cpp | 8 ++-- src/shader.cpp | 15 ++++---- src/texture.cpp | 6 +-- src/uniformbuffer.cpp | 10 ++--- src/utils.hpp | 6 +-- src/window.cpp | 11 +++--- 11 files changed, 129 insertions(+), 38 deletions(-) create mode 100644 src/logger.hpp diff --git a/src/framebuffer.cpp b/src/framebuffer.cpp index 2f260f7..56b7ebe 100644 --- a/src/framebuffer.cpp +++ b/src/framebuffer.cpp @@ -1,7 +1,7 @@ -#include #include #include "framebuffer.hpp" +#include "logger.hpp" namespace Fan { @@ -35,10 +35,11 @@ Rc FrameBuffer::create(GLsizei width, GLsizei height) glDeleteRenderbuffers(1, &render_buffer); glDeleteTextures(1, &texture); glDeleteFramebuffers(1, &id); - std::cerr << "ERROR: FRAMEBUFFER: Could not create incomplete framebuffer" << std::endl; + Log::error() << "Could not create incomplete framebuffer"; return nullptr; } + Log::info() << "Created framebuffer (" << width << "x" << height << ")"; return make_rc(id, width, height, texture, render_buffer); } diff --git a/src/loader.cpp b/src/loader.cpp index f9eff24..b17bf8c 100644 --- a/src/loader.cpp +++ b/src/loader.cpp @@ -1,9 +1,9 @@ #include -#include #include #include #include "loader.hpp" +#include "logger.hpp" namespace Fan { @@ -41,7 +41,7 @@ Opt load_obj(const std::string& file_path, Rc shader) bool ok = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, file_path.c_str(), base_dir.c_str()); if (!ok) { - std::cerr << "ERROR: LOAD_OBJ: Could not load " << file_path << ": " << err; + Log::error() << "Could not load " << file_path << ": " << err; return std::nullopt; } @@ -60,15 +60,13 @@ Opt load_obj(const std::string& file_path, Rc shader) if (!mtl.diffuse_texname.empty()) { mat->diffuse_map = Texture::create(base_dir + mtl.diffuse_texname); if (!mat->diffuse_map) { - std::cerr << "WARNING: LOAD_OBJ: Could not create diffuse texture: " - << mtl.diffuse_texname << std::endl; + Log::warn() << "Could not create diffuse texture " << mtl.diffuse_texname; } } if (!mtl.specular_texname.empty()) { mat->specular_map = Texture::create(base_dir + mtl.specular_texname); if (!mat->specular_map) { - std::cerr << "WARNING: LOAD_OBJ: Could not create specular texture: " - << mtl.specular_texname << std::endl; + Log::warn() << "Could not create specular texture" << mtl.specular_texname; } } @@ -152,10 +150,13 @@ Opt load_obj(const std::string& file_path, Rc shader) auto mesh = Mesh::create(vertices, all_indices, submeshes); if (!mesh) { - std::cerr << "ERROR: LOAD_OBJ: Could not create mesh" << std::endl; + Log::error() << "Could not create mesh for " << file_path; return std::nullopt; } + Log::info() << "Loaded " << file_path << " (" + << attrib.vertices.size() << " vertices, " + << materials.size() << " materials)"; return Entity{mesh}; } diff --git a/src/logger.hpp b/src/logger.hpp new file mode 100644 index 0000000..26be458 --- /dev/null +++ b/src/logger.hpp @@ -0,0 +1,87 @@ +#ifndef FAN_LOGGER_HPP_ +#define FAN_LOGGER_HPP_ + +#include +#include +#include + +namespace Fan +{ + +enum class LogLevel +{ + Trace = 0, + Debug, + Info, + Warn, + Error, + Fatal, + Off, +}; + +class LogStream +{ +public: + LogStream(LogLevel level, bool enabled = true) + : m_enabled(enabled) + { + if (!m_enabled) { return; } + + const char* color = "\033[37m"; // White + const char* prefix = "[TRACE]"; + + switch (level) { + case LogLevel::Debug: color = "\033[36m"; prefix = "[DEBUG]"; break; // Cyan + case LogLevel::Info: color = "\033[32m"; prefix = "[INFO] "; break; // Green + case LogLevel::Warn: color = "\033[33m"; prefix = "[WARN] "; break; // Yellow + case LogLevel::Error: color = "\033[31m"; prefix = "[ERROR]"; break; // Red + case LogLevel::Fatal: color = "\033[35m"; prefix = "[FATAL]"; break; // Magenta + default: break; + } + + std::time_t t = std::time(nullptr); + std::tm tm = *std::localtime(&t); + + // Print header: [HH:MM:SS] [LEVEL] + std::cout << color << "[" << std::put_time(&tm, "%H:%M:%S") << "] " << prefix << " "; + } + + ~LogStream() + { + if (m_enabled) { + std::cout << "\033[0m\n"; // Reset color and print newline + } + } + + template + LogStream& operator<<(const T& msg) + { + if (m_enabled) { + std::cout << msg; + } + + return *this; + } + +private: + bool m_enabled{}; +}; + +class Log +{ +public: + static inline LogLevel level = LogLevel::Trace; + + static void set_level(LogLevel new_level) { level = new_level; } + + static LogStream trace() { return LogStream(LogLevel::Trace, LogLevel::Trace >= level); } + static LogStream debug() { return LogStream(LogLevel::Debug, LogLevel::Debug >= level); } + static LogStream info() { return LogStream(LogLevel::Info, LogLevel::Info >= level); } + static LogStream warn() { return LogStream(LogLevel::Warn, LogLevel::Warn >= level); } + static LogStream error() { return LogStream(LogLevel::Error, LogLevel::Error >= level); } + static LogStream fatal() { return LogStream(LogLevel::Fatal, LogLevel::Fatal >= level); } +}; + +} // namespace Fan + +#endif // FAN_LOGGER_HPP_ diff --git a/src/main.cpp b/src/main.cpp index 82a311b..459db76 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,7 +41,7 @@ void add_random_lights(Scene& scene, int point_light_count, int spot_light_count unsigned int seed = std::random_device{}()); std::vector get_cube_vertices(); -template +template void overlay_text(float offset_x, float offset_y, ImVec4 color, const char* format, Args... args) { char buffer[256]; diff --git a/src/mesh.cpp b/src/mesh.cpp index 7a3779c..2874744 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -2,6 +2,7 @@ #include #include "mesh.hpp" +#include "logger.hpp" namespace Fan { @@ -50,6 +51,7 @@ Rc Mesh::create(const std::vector& vertices, final_submeshes.push_back(default_submesh); } + Log::debug() << "Created mesh (VAO " << VAO << ")"; return make_rc(vertices, indices, final_submeshes, has_indices, VBO, VAO, EBO); } diff --git a/src/renderer.cpp b/src/renderer.cpp index 3d9b6e4..80e6ab4 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -1,10 +1,10 @@ -#include #include #include #include #include #include "renderer.hpp" +#include "logger.hpp" namespace Fan { @@ -178,8 +178,7 @@ void Renderer::update_global_data(const Scene& scene, const Camera& camera, int // Map PointLights if (light_data.active_point_lights > MAX_POINT_LIGHTS) { - std::cerr << "WARNING: RENDERER: Maximum number of point lights reached" - << " (" << MAX_POINT_LIGHTS << ")" << std::endl; + Log::warn() << "Maximum number of point lights reached (" << MAX_POINT_LIGHTS << ")"; } light_data.active_point_lights = std::min(static_cast(env.point_lights.size()), MAX_POINT_LIGHTS); for (int i = 0; i < light_data.active_point_lights; ++i) { @@ -194,8 +193,7 @@ void Renderer::update_global_data(const Scene& scene, const Camera& camera, int // Map SpotLights if (light_data.active_spot_lights > MAX_SPOT_LIGHTS) { - std::cerr << "WARNING: RENDERER: Maximum number of spot lights reached" - << " (" << MAX_SPOT_LIGHTS << ")" << std::endl; + Log::warn() << "Maximum number of spot lights reached (" << MAX_SPOT_LIGHTS << ")"; } light_data.active_spot_lights = std::min(static_cast(env.spot_lights.size()), MAX_SPOT_LIGHTS); for (int i = 0; i < light_data.active_spot_lights; ++i) { diff --git a/src/shader.cpp b/src/shader.cpp index f5a6a5c..df59687 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -1,9 +1,9 @@ -#include #include #include #include "shader.hpp" #include "utils.hpp" +#include "logger.hpp" namespace Fan { @@ -40,7 +40,7 @@ Rc Shader::create(const std::string& vertex_shader_path, if (log_length > 0) { std::string log(log_length, ' '); glGetProgramInfoLog(shader_program, log_length, nullptr, &log[0]); - std::cerr << "ERROR: SHADER: Could not link shader program: " << log << std::endl; + Log::error() << "Could not link shader program: " << log; } glDeleteProgram(shader_program); @@ -54,6 +54,8 @@ Rc Shader::create(const std::string& vertex_shader_path, shader->bind_uniform_block("LightEnvironmentBlock", BindingPoint::LightEnvironment); } + Log::info() << "Created shader (" << vertex_shader_path << ", " + << fragment_shader_path << ")"; return shader; } @@ -85,7 +87,7 @@ GLint Shader::get_location(const std::string &name) { GLint location = glGetUniformLocation(m_id, name.c_str()); if (location == -1) { - std::cerr << "WARNING: SHADER: uniform " << name << " not found" << std::endl; + Log::warn() << "Uniform " << name << " not found"; } return location; } @@ -121,7 +123,7 @@ Opt Shader::compile_shader(const std::string& source_path, { auto shader_source = read_entire_file(source_path); if (!shader_source) { - std::cerr << "ERROR: SHADER: Could not read shader " << source_path << std::endl; + Log::error() << "Could not read shader " << source_path; return std::nullopt; } @@ -139,7 +141,7 @@ Opt Shader::compile_shader(const std::string& source_path, if (log_length > 0) { std::string log(log_length, ' '); glGetShaderInfoLog(shader, log_length, nullptr, &log[0]); - std::cerr << "ERROR: SHADER: Could not compile shader: " << log << std::endl; + Log::error() << "Could not compile shader: " << log; } glDeleteShader(shader); @@ -163,8 +165,7 @@ void Shader::bind_uniform_block(const std::string& name, GLuint binding_point) c GLuint block_index = glGetUniformBlockIndex(m_id, name.c_str()); if (block_index == GL_INVALID_INDEX) { - std::cerr << "ERROR: SHADER: Uniform block not found: " - << name << std::endl; + Log::error() << "Uniform block not found: " << name; return; } diff --git a/src/texture.cpp b/src/texture.cpp index 300dfd7..f4d481f 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -1,7 +1,7 @@ -#include #include #include "texture.hpp" +#include "logger.hpp" namespace Fan { @@ -13,14 +13,14 @@ Rc Texture::create(const std::string& image_path) int width, height, n_channels; stbi_uc *image_data = stbi_load(image_path.c_str(), &width, &height, &n_channels, STBI_rgb_alpha); if (image_data == NULL) { - std::cerr << "ERROR: TEXTURE: Could not load image " << image_path << std::endl; + Log::error() << "Could not load texture image " << image_path; return nullptr; } GLuint texture; glGenTextures(1, &texture); if (glGetError() != GL_NO_ERROR) { - std::cerr << "ERROR: TEXTURE: glGenTextures" << std::endl; + Log::error() << "glGenTextures failed"; stbi_image_free(image_data); return nullptr; } diff --git a/src/uniformbuffer.cpp b/src/uniformbuffer.cpp index 8975c89..bccd227 100644 --- a/src/uniformbuffer.cpp +++ b/src/uniformbuffer.cpp @@ -1,7 +1,7 @@ -#include #include #include "uniformbuffer.hpp" +#include "logger.hpp" namespace Fan { @@ -19,7 +19,7 @@ Rc UniformBuffer::create(size_t size, GLuint binding_point) GLenum err = glGetError(); if (err != GL_NO_ERROR) { - std::cerr << "ERROR: UNIFORMBUFFER: Could not create uniformbuffer" << std::endl; + Log::error() << "Could not create uniformbuffer"; glDeleteBuffers(1, &id); return nullptr; } @@ -53,12 +53,12 @@ UniformBuffer& UniformBuffer::operator=(UniformBuffer&& other) noexcept void UniformBuffer::update(const void* data, size_t size, size_t offset) const { if (size + offset > m_size) { - std::cerr << "ERROR: UNIFORMBUFFER: Attempted to upload data outside of bounds on update" << std::endl; + Log::error() << "Attempted to upload uniformbuffer data outside of bounds"; return; } - if (!data) { - std::cerr << "ERROR: UNIFORMBUFFER: Provided null data pointer on update" << std::endl; + if (data == nullptr) { + Log::error() << "Provided null data pointer on uniform buffer update"; return; } diff --git a/src/utils.hpp b/src/utils.hpp index 6cedace..44280c3 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -8,16 +8,16 @@ namespace Fan { -template +template using Rc = std::shared_ptr; -template +template Rc make_rc(Args&&... args) { return std::make_shared(std::forward(args)...); } -template +template using Opt = std::optional; Opt read_entire_file(const std::string& file_path); diff --git a/src/window.cpp b/src/window.cpp index ca27518..7e15815 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -6,6 +5,7 @@ #include #include "window.hpp" +#include "logger.hpp" namespace Fan { @@ -15,7 +15,7 @@ Rc Window::create(int width, int height, const std::string& title) glfwSetErrorCallback(error_callback); if (!glfwInit()) { - std::cerr << "ERROR: WINDOW: Could not initialize GLFW" << std::endl; + Log::error() << "Could not initialize GLFW"; return nullptr; } @@ -26,7 +26,7 @@ Rc Window::create(int width, int height, const std::string& title) GLFWwindow *window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); if (window == nullptr) { - std::cerr << "ERROR: WINDOW: Could not create window" << std::endl; + Log::error() << "Could not create window"; glfwTerminate(); return nullptr; } @@ -37,7 +37,7 @@ Rc Window::create(int width, int height, const std::string& title) glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE); if (!gladLoadGL(glfwGetProcAddress)) { - std::cerr << "ERROR: WINDOW: Could not initialize OpenGL context" << std::endl; + Log::error() << "Could not initialize OpenGL context"; glfwDestroyWindow(window); glfwTerminate(); return nullptr; @@ -58,6 +58,7 @@ Rc Window::create(int width, int height, const std::string& title) ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init(); + Log::info() << "Created window"; return make_rc(window, width, height); } @@ -251,7 +252,7 @@ void Window::end() void Window::error_callback(int error, const char* description) { - std::cerr << "ERROR: WINDOW: GLFW: code: " << error << " msg: " << description << std::endl; + Log::error() << "GLFW code: " << error << " msg: " << description; } void Window::framebuffer_size_callback(GLFWwindow* window, int width, int height)