custom log function; change Game and Node from struct to class

This commit is contained in:
frank 2021-06-24 17:40:30 -04:00
parent 4ece64442f
commit fff9e13562
12 changed files with 126 additions and 66 deletions

View File

@ -46,7 +46,7 @@ struct Mushroom : Sprite
Mushroom(Node*); Mushroom(Node*);
void update(); void update();
std::string get_class_name() { return "Mushroom"; } virtual std::string get_class_name() const { return "Mushroom"; }
}; };
@ -71,6 +71,6 @@ struct Demo : Game
void load_gl_context(); void load_gl_context();
void respond(SDL_Event&); void respond(SDL_Event&);
void update(); void update();
std::string get_class_name() { return "Demo"; } virtual std::string get_class_name() const { return "Demo"; }
}; };

View File

@ -9,7 +9,7 @@
typedef std::function<void()> callback; typedef std::function<void()> callback;
struct Node; class Node;
struct Animation struct Animation
{ {

View File

@ -77,7 +77,7 @@ struct Box : SDL_FRect
bool collide(const Segment&, glm::vec2&) const; bool collide(const Segment&, glm::vec2&) const;
bool collide(const Box&, Box* = nullptr) const; bool collide(const Box&, Box* = nullptr) const;
bool collide(const Box&, Box&) const; bool collide(const Box&, Box&) const;
std::string get_class_name() { return "Box"; } virtual std::string get_class_name() const { return "Box"; }
std::ostream& to_string (std::ostream&) const; std::ostream& to_string (std::ostream&) const;
}; };

View File

@ -75,9 +75,10 @@ void Configuration::set_defaults()
sys_config["animation"] = { sys_config["animation"] = {
{"all-frames-frameset-name", "all"} {"all-frames-frameset-name", "all"}
}; };
sys_config["logging"] = { sys_config["log"] = {
{"enabled", false}, {"enabled", false},
{"debug", false}, {"debug-to-stdout", false},
{"debug-to-file", false},
{"ouput-directory", "."}, {"ouput-directory", "."},
{"info-file-name", "log.txt"}, {"info-file-name", "log.txt"},
{"debug-file-name", "debug_log.txt"} {"debug-file-name", "debug_log.txt"}

View File

@ -20,7 +20,7 @@ struct Configuration : Node
void merge(); void merge();
void write(); void write();
void write(fs::path path); void write(fs::path path);
std::string get_class_name() { return "Configuration"; } virtual std::string get_class_name() const { return "Configuration"; }
}; };

View File

@ -39,49 +39,26 @@ void FramerateIndicator::refresh()
} }
} }
void log(void* userdata, int category, SDL_LogPriority priority, const char* message)
{
Game* game = static_cast<Game*>(userdata);
std::ostream& out = (priority > SDL_LOG_PRIORITY_DEBUG) ? std::cerr : std::cout;
out << message << std::endl;
if (game->get_configuration()["logging"]["enabled"])
{
fs::path path = game->get_configuration()["logging"]["output-directory"];
if (!fs::exists(path))
{
fs::create_directories(path);
}
std::time_t now = std::time(nullptr);
std::stringstream stamped_message;
stamped_message << std::put_time(std::localtime(&now), "%F %T ") << message;
if (game->get_configuration()["logging"]["debug"])
{
fs::path debug_path = path / game->get_configuration()["logging"]["debug-file-name"];
std::ofstream debug_stream(debug_path, std::ios_base::app);
debug_stream << stamped_message.str() << std::endl;
}
if (priority > SDL_LOG_PRIORITY_DEBUG)
{
fs::path info_path = path / game->get_configuration()["logging"]["info-file-name"];
std::ofstream info_stream(info_path, std::ios_base::app);
info_stream << stamped_message.str() << std::endl;
}
}
}
Game::Game() Game::Game()
{ {
// use config to determine if debug statements should be printed // set the appropriate priority level for the default log category so either info level messages
if (get_configuration()["logging"]["debug"]) // and higher are enabled or debug level messages are enabled, depending on the global configuration
SDL_LogPriority default_log_category_priority;
if (get_configuration()["log"]["debug-to-file"] || get_configuration()["log"]["debug-to-stdout"])
{ {
SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG); default_log_category_priority = SDL_LOG_PRIORITY_DEBUG;
} }
else
{
default_log_category_priority = SDL_LOG_PRIORITY_INFO;
}
SDL_LogSetPriority(DEFAULT_SDL_LOG_CATEGORY, default_log_category_priority);
// set custom log function (prints to stdout/stderr and to file if enabled) // set custom log function (prints to stdout/stderr and to file if enabled)
SDL_LogSetOutputFunction(&log, this); SDL_LogSetOutputFunction(&Game::sdl_log_override, this);
// pretty print config to debug log // pretty print config to debug log
std::stringstream config_formatted; std::stringstream config_formatted;
config_formatted << std::setw(4) << get_configuration() << std::endl; config_formatted << std::setw(4) << get_configuration() << std::endl;
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "%s", config_formatted.str().c_str()); SDL_LogDebug(DEFAULT_SDL_LOG_CATEGORY, "%s", config_formatted.str().c_str());
SDL_SetHint(SDL_HINT_RENDER_DRIVER, get_configuration()["display"]["render driver"].get<std::string>().c_str()); SDL_SetHint(SDL_HINT_RENDER_DRIVER, get_configuration()["display"]["render driver"].get<std::string>().c_str());
frame_length_history.reserve(5000); frame_length_history.reserve(5000);
set_framerate(get_configuration()["display"]["framerate"]); set_framerate(get_configuration()["display"]["framerate"]);
@ -192,6 +169,48 @@ Game::~Game()
get_delegate().unsubscribe(this); get_delegate().unsubscribe(this);
} }
/* Overrides SDL's default log function to log a message to stdout/stderr and, if log is enabled in the
* global configuration, to a file. Debug level statements may be suppressed, printed to stdout, or printed to
* both stdout and file, depending on the global configuration.
*/
void Game::sdl_log_override(void* userdata, int category, SDL_LogPriority priority, const char* message)
{
Game* game = static_cast<Game*>(userdata);
std::ostream& out = (priority > SDL_LOG_PRIORITY_WARN) ? std::cerr : std::cout;
// print to stdout/stderr if priority is higher than debug or debug statements are enabled
if (priority > SDL_LOG_PRIORITY_DEBUG || game->get_configuration()["log"]["debug-to-stdout"])
{
out << message << std::endl;
}
// handle writing to log file
if (game->get_configuration()["log"]["enabled"])
{
fs::path path = game->get_configuration()["log"]["output-directory"];
if (!fs::exists(path))
{
fs::create_directories(path);
}
// prepend a timestamp to the message
std::time_t now = std::time(nullptr);
std::stringstream stamped_message;
stamped_message << std::put_time(std::localtime(&now), "%F %T ") << message;
// if debug is enabled, append message to debug log file
if (game->get_configuration()["log"]["debug-to-file"])
{
fs::path debug_path = path / game->get_configuration()["log"]["debug-file-name"];
std::ofstream debug_stream(debug_path, std::ios_base::app);
debug_stream << stamped_message.str() << std::endl;
}
// only append messages to the info log that are higher than debug priority
if (priority > SDL_LOG_PRIORITY_DEBUG)
{
fs::path info_path = path / game->get_configuration()["log"]["info-file-name"];
std::ofstream info_stream(info_path, std::ios_base::app);
info_stream << stamped_message.str() << std::endl;
}
}
}
void Game::print_error(const std::string& message) void Game::print_error(const std::string& message)
{ {
sfw::print_error(message); sfw::print_error(message);
@ -288,6 +307,23 @@ void Game::load_gl_context()
log_display_mode(); log_display_mode();
} }
/* Send an info priority message to SDL's log output function, which is overridden by our own member
* function. Category will default to SDL's custom category.
*/
void Game::log(const std::string& message, const int category) const
{
SDL_LogInfo(category, "%s", message.c_str());
}
/* Send a debug priority message to SDL's log output function, which is overridden by our own member
* function. Category will default to SDL's custom category. Using the default category will ensure
* that debug level statements are handled according to the options in the global configuration.
*/
void Game::debug(const std::string& message, const int category) const
{
SDL_LogDebug(category, "%s", message.c_str());
}
void Game::log_renderer_info(SDL_RendererInfo& info) void Game::log_renderer_info(SDL_RendererInfo& info)
{ {
SDL_Log("renderer name: %s, flags: %i, texture formats: %i, " SDL_Log("renderer name: %s, flags: %i, texture formats: %i, "

View File

@ -42,16 +42,21 @@ struct FramerateIndicator : Sprite
}; };
void log(void*, int, SDL_LogPriority, const char*); class Game : public Node
struct Game : Node
{ {
private:
static void sdl_log_override(void*, int, SDL_LogPriority, const char*);
public:
Game(const Game&) = delete; Game(const Game&) = delete;
Game& operator=(const Game&) = delete; Game& operator=(const Game&) = delete;
Game(Game&&) = delete; Game(Game&&) = delete;
Game& operator=(Game&&) = delete; Game& operator=(Game&&) = delete;
static const int DEFAULT_SDL_LOG_CATEGORY = SDL_LOG_CATEGORY_CUSTOM;
SDL_Window* window; SDL_Window* window;
SDL_Renderer* renderer = NULL; SDL_Renderer* renderer = NULL;
SDL_GLContext glcontext = NULL; SDL_GLContext glcontext = NULL;
@ -78,6 +83,8 @@ struct Game : Node
void print_frame_length_history(); void print_frame_length_history();
void load_sdl_context(); void load_sdl_context();
void load_gl_context(); void load_gl_context();
void log(const std::string&, const int = DEFAULT_SDL_LOG_CATEGORY) const;
void debug(const std::string&, const int = DEFAULT_SDL_LOG_CATEGORY) const;
void log_renderer_info(SDL_RendererInfo&); void log_renderer_info(SDL_RendererInfo&);
bool log_gl_errors(std::string); bool log_gl_errors(std::string);
void log_display_mode(); void log_display_mode();
@ -98,7 +105,7 @@ struct Game : Node
void set_framerate(int); void set_framerate(int);
void handle_quit_event(SDL_Event&); void handle_quit_event(SDL_Event&);
void quit(); void quit();
std::string get_class_name() { return "Game"; } virtual std::string get_class_name() const { return "Game"; }
template<typename T> template<typename T>
float weight(T amount) float weight(T amount)

View File

@ -60,7 +60,7 @@ struct Input : Node
void suppress(); void suppress();
void unsuppress(); void unsuppress();
bool is_suppressed(); bool is_suppressed();
std::string get_class_name() { return "Input"; } virtual std::string get_class_name() const { return "Input"; }
}; };

View File

@ -11,8 +11,7 @@ Node::Node() : Node(nullptr) {}
Node::Node(Node* parent) : parent(parent) Node::Node(Node* parent) : parent(parent)
{ {
std::cout << "Constructing "; debug("constructing node " + get_branch_as_string());
print_branch();
} }
void Node::set_parent(Node* other) void Node::set_parent(Node* other)
@ -35,6 +34,11 @@ bool Node::is_active() const
return active; return active;
} }
const nlohmann::json& Node::get_configuration() const
{
return get_root()->configuration.config;
}
nlohmann::json& Node::get_configuration() nlohmann::json& Node::get_configuration()
{ {
return get_root()->configuration.config; return get_root()->configuration.config;
@ -120,27 +124,34 @@ void Node::unsuppress_input()
get_root()->get_input().unsuppress(); get_root()->get_input().unsuppress();
} }
void Node::print_branch() void Node::log(const std::string& message) const
{ {
Node* current = this; get_root()->log(message);
}
void Node::debug(const std::string& message) const
{
get_root()->debug(message);
}
const std::string Node::get_branch_as_string() const
{
const Node* current = this;
std::stringstream branch;
while (current != nullptr) while (current != nullptr)
{ {
std::cout << current->get_class_name() << " @ " << current; branch << current->get_class_name() << " @ " << current;
if (current->parent != nullptr) if (current->parent != nullptr)
{ {
std::cout << " -> "; branch << " -> ";
}
else
{
std::cout << std::endl;
} }
current = current->parent; current = current->parent;
} }
return branch.str();
} }
Node::~Node() Node::~Node()
{ {
// std::cout << "Destructing "; debug("destroying node " + get_branch_as_string());
// print_branch();
get_delegate().unsubscribe(this); get_delegate().unsubscribe(this);
} }

View File

@ -10,16 +10,18 @@
#include "filesystem.hpp" #include "filesystem.hpp"
struct Game; class Game;
struct Delegate; struct Delegate;
struct Display; struct Display;
struct Input; struct Input;
struct Box; struct Box;
struct Audio; struct Audio;
struct Node class Node
{ {
public:
Node *parent; Node *parent;
SDL_Texture* canvas = nullptr; SDL_Texture* canvas = nullptr;
bool active = true; bool active = true;
@ -33,6 +35,7 @@ struct Node
void set_canvas(SDL_Texture*); void set_canvas(SDL_Texture*);
SDL_Texture* get_canvas(); SDL_Texture* get_canvas();
bool is_active() const; bool is_active() const;
const nlohmann::json& get_configuration() const;
nlohmann::json& get_configuration(); nlohmann::json& get_configuration();
Delegate& get_delegate(); Delegate& get_delegate();
const Display& get_display() const; const Display& get_display() const;
@ -48,8 +51,10 @@ struct Node
void suppress_input(); void suppress_input();
void suppress_input_temporarily(int = 0); void suppress_input_temporarily(int = 0);
void unsuppress_input(); void unsuppress_input();
void print_branch(); void log(const std::string&) const;
virtual std::string get_class_name() { return "Node"; }; void debug(const std::string&) const;
const std::string get_branch_as_string() const;
virtual std::string get_class_name() const { return "Node"; };
virtual ~Node(); virtual ~Node();
template<typename T = Game> template<typename T = Game>

View File

@ -62,7 +62,7 @@ struct Recorder : Node
void write_mp4(); void write_mp4();
void write_audio(Uint8*, int); void write_audio(Uint8*, int);
void update(); void update();
std::string get_class_name() { return "Recorder"; } virtual std::string get_class_name() const { return "Recorder"; }
}; };

View File

@ -17,7 +17,7 @@
#include "Animation.hpp" #include "Animation.hpp"
#include "Color.hpp" #include "Color.hpp"
struct Game; class Game;
struct Frameset; struct Frameset;
struct Sprite : Node struct Sprite : Node
@ -143,7 +143,7 @@ struct Sprite : Node
void render_subsection(SDL_Renderer*, SDL_Texture*, const Box&, const Box&); void render_subsection(SDL_Renderer*, SDL_Texture*, const Box&, const Box&);
void set_to_leave_memory_allocated(); void set_to_leave_memory_allocated();
void set_to_deallocate_memory(); void set_to_deallocate_memory();
virtual std::string get_class_name() { return "Sprite"; } virtual std::string get_class_name() const { return "Sprite"; }
~Sprite() { unload(); } ~Sprite() { unload(); }
}; };