Implemented a custom logger

This commit is contained in:
2026-10-01 03:04:17 +02:00
parent 3972413594
commit 642d29634a
11 changed files with 129 additions and 38 deletions

View File

@@ -1,7 +1,7 @@
#include <iostream>
#include <utility> #include <utility>
#include "framebuffer.hpp" #include "framebuffer.hpp"
#include "logger.hpp"
namespace Fan namespace Fan
{ {
@@ -35,10 +35,11 @@ Rc<FrameBuffer> FrameBuffer::create(GLsizei width, GLsizei height)
glDeleteRenderbuffers(1, &render_buffer); glDeleteRenderbuffers(1, &render_buffer);
glDeleteTextures(1, &texture); glDeleteTextures(1, &texture);
glDeleteFramebuffers(1, &id); glDeleteFramebuffers(1, &id);
std::cerr << "ERROR: FRAMEBUFFER: Could not create incomplete framebuffer" << std::endl; Log::error() << "Could not create incomplete framebuffer";
return nullptr; return nullptr;
} }
Log::info() << "Created framebuffer (" << width << "x" << height << ")";
return make_rc<FrameBuffer>(id, width, height, texture, render_buffer); return make_rc<FrameBuffer>(id, width, height, texture, render_buffer);
} }

View File

@@ -1,9 +1,9 @@
#include <filesystem> #include <filesystem>
#include <iostream>
#include <unordered_map> #include <unordered_map>
#include <tiny_obj_loader.h> #include <tiny_obj_loader.h>
#include "loader.hpp" #include "loader.hpp"
#include "logger.hpp"
namespace Fan namespace Fan
{ {
@@ -41,7 +41,7 @@ Opt<Entity> load_obj(const std::string& file_path, Rc<Shader> shader)
bool ok = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, bool ok = tinyobj::LoadObj(&attrib, &shapes, &materials, &err,
file_path.c_str(), base_dir.c_str()); file_path.c_str(), base_dir.c_str());
if (!ok) { if (!ok) {
std::cerr << "ERROR: LOAD_OBJ: Could not load " << file_path << ": " << err; Log::error() << "Could not load " << file_path << ": " << err;
return std::nullopt; return std::nullopt;
} }
@@ -60,15 +60,13 @@ Opt<Entity> load_obj(const std::string& file_path, Rc<Shader> shader)
if (!mtl.diffuse_texname.empty()) { if (!mtl.diffuse_texname.empty()) {
mat->diffuse_map = Texture::create(base_dir + mtl.diffuse_texname); mat->diffuse_map = Texture::create(base_dir + mtl.diffuse_texname);
if (!mat->diffuse_map) { if (!mat->diffuse_map) {
std::cerr << "WARNING: LOAD_OBJ: Could not create diffuse texture: " Log::warn() << "Could not create diffuse texture " << mtl.diffuse_texname;
<< mtl.diffuse_texname << std::endl;
} }
} }
if (!mtl.specular_texname.empty()) { if (!mtl.specular_texname.empty()) {
mat->specular_map = Texture::create(base_dir + mtl.specular_texname); mat->specular_map = Texture::create(base_dir + mtl.specular_texname);
if (!mat->specular_map) { if (!mat->specular_map) {
std::cerr << "WARNING: LOAD_OBJ: Could not create specular texture: " Log::warn() << "Could not create specular texture" << mtl.specular_texname;
<< mtl.specular_texname << std::endl;
} }
} }
@@ -152,10 +150,13 @@ Opt<Entity> load_obj(const std::string& file_path, Rc<Shader> shader)
auto mesh = Mesh::create(vertices, all_indices, submeshes); auto mesh = Mesh::create(vertices, all_indices, submeshes);
if (!mesh) { 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; return std::nullopt;
} }
Log::info() << "Loaded " << file_path << " ("
<< attrib.vertices.size() << " vertices, "
<< materials.size() << " materials)";
return Entity{mesh}; return Entity{mesh};
} }

87
src/logger.hpp Normal file
View File

@@ -0,0 +1,87 @@
#ifndef FAN_LOGGER_HPP_
#define FAN_LOGGER_HPP_
#include <ctime>
#include <iostream>
#include <iomanip>
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<typename T>
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_

View File

@@ -41,7 +41,7 @@ void add_random_lights(Scene& scene, int point_light_count, int spot_light_count
unsigned int seed = std::random_device{}()); unsigned int seed = std::random_device{}());
std::vector<Vertex> get_cube_vertices(); std::vector<Vertex> get_cube_vertices();
template <typename... Args> template<typename... Args>
void overlay_text(float offset_x, float offset_y, ImVec4 color, const char* format, Args... args) void overlay_text(float offset_x, float offset_y, ImVec4 color, const char* format, Args... args)
{ {
char buffer[256]; char buffer[256];

View File

@@ -2,6 +2,7 @@
#include <utility> #include <utility>
#include "mesh.hpp" #include "mesh.hpp"
#include "logger.hpp"
namespace Fan namespace Fan
{ {
@@ -50,6 +51,7 @@ Rc<Mesh> Mesh::create(const std::vector<Vertex>& vertices,
final_submeshes.push_back(default_submesh); final_submeshes.push_back(default_submesh);
} }
Log::debug() << "Created mesh (VAO " << VAO << ")";
return make_rc<Mesh>(vertices, indices, final_submeshes, has_indices, VBO, VAO, EBO); return make_rc<Mesh>(vertices, indices, final_submeshes, has_indices, VBO, VAO, EBO);
} }

View File

@@ -1,10 +1,10 @@
#include <iostream>
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <glad/gl.h> #include <glad/gl.h>
#include "renderer.hpp" #include "renderer.hpp"
#include "logger.hpp"
namespace Fan namespace Fan
{ {
@@ -178,8 +178,7 @@ void Renderer::update_global_data(const Scene& scene, const Camera& camera, int
// Map PointLights // Map PointLights
if (light_data.active_point_lights > MAX_POINT_LIGHTS) { if (light_data.active_point_lights > MAX_POINT_LIGHTS) {
std::cerr << "WARNING: RENDERER: Maximum number of point lights reached" Log::warn() << "Maximum number of point lights reached (" << MAX_POINT_LIGHTS << ")";
<< " (" << MAX_POINT_LIGHTS << ")" << std::endl;
} }
light_data.active_point_lights = std::min(static_cast<int>(env.point_lights.size()), MAX_POINT_LIGHTS); light_data.active_point_lights = std::min(static_cast<int>(env.point_lights.size()), MAX_POINT_LIGHTS);
for (int i = 0; i < light_data.active_point_lights; ++i) { 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 // Map SpotLights
if (light_data.active_spot_lights > MAX_SPOT_LIGHTS) { if (light_data.active_spot_lights > MAX_SPOT_LIGHTS) {
std::cerr << "WARNING: RENDERER: Maximum number of spot lights reached" Log::warn() << "Maximum number of spot lights reached (" << MAX_SPOT_LIGHTS << ")";
<< " (" << MAX_SPOT_LIGHTS << ")" << std::endl;
} }
light_data.active_spot_lights = std::min(static_cast<int>(env.spot_lights.size()), MAX_SPOT_LIGHTS); light_data.active_spot_lights = std::min(static_cast<int>(env.spot_lights.size()), MAX_SPOT_LIGHTS);
for (int i = 0; i < light_data.active_spot_lights; ++i) { for (int i = 0; i < light_data.active_spot_lights; ++i) {

View File

@@ -1,9 +1,9 @@
#include <iostream>
#include <utility> #include <utility>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include "shader.hpp" #include "shader.hpp"
#include "utils.hpp" #include "utils.hpp"
#include "logger.hpp"
namespace Fan namespace Fan
{ {
@@ -40,7 +40,7 @@ Rc<Shader> Shader::create(const std::string& vertex_shader_path,
if (log_length > 0) { if (log_length > 0) {
std::string log(log_length, ' '); std::string log(log_length, ' ');
glGetProgramInfoLog(shader_program, log_length, nullptr, &log[0]); 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); glDeleteProgram(shader_program);
@@ -54,6 +54,8 @@ Rc<Shader> Shader::create(const std::string& vertex_shader_path,
shader->bind_uniform_block("LightEnvironmentBlock", BindingPoint::LightEnvironment); shader->bind_uniform_block("LightEnvironmentBlock", BindingPoint::LightEnvironment);
} }
Log::info() << "Created shader (" << vertex_shader_path << ", "
<< fragment_shader_path << ")";
return shader; return shader;
} }
@@ -85,7 +87,7 @@ GLint Shader::get_location(const std::string &name)
{ {
GLint location = glGetUniformLocation(m_id, name.c_str()); GLint location = glGetUniformLocation(m_id, name.c_str());
if (location == -1) { if (location == -1) {
std::cerr << "WARNING: SHADER: uniform " << name << " not found" << std::endl; Log::warn() << "Uniform " << name << " not found";
} }
return location; return location;
} }
@@ -121,7 +123,7 @@ Opt<GLuint> Shader::compile_shader(const std::string& source_path,
{ {
auto shader_source = read_entire_file(source_path); auto shader_source = read_entire_file(source_path);
if (!shader_source) { 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; return std::nullopt;
} }
@@ -139,7 +141,7 @@ Opt<GLuint> Shader::compile_shader(const std::string& source_path,
if (log_length > 0) { if (log_length > 0) {
std::string log(log_length, ' '); std::string log(log_length, ' ');
glGetShaderInfoLog(shader, log_length, nullptr, &log[0]); 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); 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()); GLuint block_index = glGetUniformBlockIndex(m_id, name.c_str());
if (block_index == GL_INVALID_INDEX) { if (block_index == GL_INVALID_INDEX) {
std::cerr << "ERROR: SHADER: Uniform block not found: " Log::error() << "Uniform block not found: " << name;
<< name << std::endl;
return; return;
} }

View File

@@ -1,7 +1,7 @@
#include <iostream>
#include <stb_image.h> #include <stb_image.h>
#include "texture.hpp" #include "texture.hpp"
#include "logger.hpp"
namespace Fan namespace Fan
{ {
@@ -13,14 +13,14 @@ Rc<Texture> Texture::create(const std::string& image_path)
int width, height, n_channels; int width, height, n_channels;
stbi_uc *image_data = stbi_load(image_path.c_str(), &width, &height, &n_channels, STBI_rgb_alpha); stbi_uc *image_data = stbi_load(image_path.c_str(), &width, &height, &n_channels, STBI_rgb_alpha);
if (image_data == NULL) { 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; return nullptr;
} }
GLuint texture; GLuint texture;
glGenTextures(1, &texture); glGenTextures(1, &texture);
if (glGetError() != GL_NO_ERROR) { if (glGetError() != GL_NO_ERROR) {
std::cerr << "ERROR: TEXTURE: glGenTextures" << std::endl; Log::error() << "glGenTextures failed";
stbi_image_free(image_data); stbi_image_free(image_data);
return nullptr; return nullptr;
} }

View File

@@ -1,7 +1,7 @@
#include <iostream>
#include <utility> #include <utility>
#include "uniformbuffer.hpp" #include "uniformbuffer.hpp"
#include "logger.hpp"
namespace Fan namespace Fan
{ {
@@ -19,7 +19,7 @@ Rc<UniformBuffer> UniformBuffer::create(size_t size, GLuint binding_point)
GLenum err = glGetError(); GLenum err = glGetError();
if (err != GL_NO_ERROR) { if (err != GL_NO_ERROR) {
std::cerr << "ERROR: UNIFORMBUFFER: Could not create uniformbuffer" << std::endl; Log::error() << "Could not create uniformbuffer";
glDeleteBuffers(1, &id); glDeleteBuffers(1, &id);
return nullptr; 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 void UniformBuffer::update(const void* data, size_t size, size_t offset) const
{ {
if (size + offset > m_size) { 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; return;
} }
if (!data) { if (data == nullptr) {
std::cerr << "ERROR: UNIFORMBUFFER: Provided null data pointer on update" << std::endl; Log::error() << "Provided null data pointer on uniform buffer update";
return; return;
} }

View File

@@ -8,16 +8,16 @@
namespace Fan namespace Fan
{ {
template <typename T> template<typename T>
using Rc = std::shared_ptr<T>; using Rc = std::shared_ptr<T>;
template <typename T, typename... Args> template<typename T, typename... Args>
Rc<T> make_rc(Args&&... args) Rc<T> make_rc(Args&&... args)
{ {
return std::make_shared<T>(std::forward<Args>(args)...); return std::make_shared<T>(std::forward<Args>(args)...);
} }
template <typename T> template<typename T>
using Opt = std::optional<T>; using Opt = std::optional<T>;
Opt<std::string> read_entire_file(const std::string& file_path); Opt<std::string> read_entire_file(const std::string& file_path);

View File

@@ -1,4 +1,3 @@
#include <iostream>
#include <utility> #include <utility>
#include <glad/gl.h> #include <glad/gl.h>
#include <imgui.h> #include <imgui.h>
@@ -6,6 +5,7 @@
#include <imgui_impl_opengl3.h> #include <imgui_impl_opengl3.h>
#include "window.hpp" #include "window.hpp"
#include "logger.hpp"
namespace Fan namespace Fan
{ {
@@ -15,7 +15,7 @@ Rc<Window> Window::create(int width, int height, const std::string& title)
glfwSetErrorCallback(error_callback); glfwSetErrorCallback(error_callback);
if (!glfwInit()) { if (!glfwInit()) {
std::cerr << "ERROR: WINDOW: Could not initialize GLFW" << std::endl; Log::error() << "Could not initialize GLFW";
return nullptr; return nullptr;
} }
@@ -26,7 +26,7 @@ Rc<Window> Window::create(int width, int height, const std::string& title)
GLFWwindow *window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); GLFWwindow *window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
if (window == nullptr) { if (window == nullptr) {
std::cerr << "ERROR: WINDOW: Could not create window" << std::endl; Log::error() << "Could not create window";
glfwTerminate(); glfwTerminate();
return nullptr; return nullptr;
} }
@@ -37,7 +37,7 @@ Rc<Window> Window::create(int width, int height, const std::string& title)
glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE); glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
if (!gladLoadGL(glfwGetProcAddress)) { if (!gladLoadGL(glfwGetProcAddress)) {
std::cerr << "ERROR: WINDOW: Could not initialize OpenGL context" << std::endl; Log::error() << "Could not initialize OpenGL context";
glfwDestroyWindow(window); glfwDestroyWindow(window);
glfwTerminate(); glfwTerminate();
return nullptr; return nullptr;
@@ -58,6 +58,7 @@ Rc<Window> Window::create(int width, int height, const std::string& title)
ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init(); ImGui_ImplOpenGL3_Init();
Log::info() << "Created window";
return make_rc<Window>(window, width, height); return make_rc<Window>(window, width, height);
} }
@@ -251,7 +252,7 @@ void Window::end()
void Window::error_callback(int error, const char* description) 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) void Window::framebuffer_size_callback(GLFWwindow* window, int width, int height)