Antkeeper  0.0.1
main.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Christopher J. Howard
3  *
4  * This file is part of Antkeeper source code.
5  *
6  * Antkeeper source code is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Antkeeper source code is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "game/game.hpp"
21 #include <SDL2/SDL.h>
22 #include <chrono>
23 #include <engine/config.hpp>
24 #include <engine/debug/console.hpp>
25 #include <engine/debug/log.hpp>
26 #include <engine/utility/ansi.hpp>
27 #include <engine/utility/paths.hpp>
28 #include <fstream>
29 #include <iostream>
30 #include <set>
31 #include <stdexcept>
32 #include <syncstream>
33 
34 int main(int argc, char* argv[])
35 {
36  // Get time at which the application launched
37  const auto launch_time = std::chrono::system_clock::now();
38 
39  // Enable console UTF-8 output and VT100 sequences (for colored text)
42 
43  // Subscribe log to cout function to message logged events
44  auto log_to_cout_subscription = debug::default_logger().get_message_logged_channel().subscribe
45  (
46  [&launch_time](const auto& event)
47  {
48  static const char* severities[] =
49  {
50  "trace",
51  "debug",
52  "info",
53  "warning",
54  "error",
55  "fatal"
56  };
57 
58  static const std::string colors[] =
59  {
66  };
67 
68  std::osyncstream(std::cout) << std::format
69  (
70  "[{:8.03f}] {}{}: {}:{}:{}: {}{}\n",
71  std::chrono::duration<float>(event.time - launch_time).count(),
72  colors[static_cast<int>(event.severity)],
73  //severities[static_cast<int>(event.severity)],
74  static_cast<int>(event.severity),
75  std::filesystem::path(event.location.file_name()).filename().string(),
76  event.location.line(),
77  event.location.column(),
78  event.message,
80  );
81  }
82  );
83 
84  // Determine path to log archive
85  const std::filesystem::path log_archive_path = get_shared_config_path() / config::application_name / "logs";
86 
87  // Set up log archive
88  bool log_archive_exists = false;
89  try
90  {
91  // Create log archive if it doesn't exist
92  if (std::filesystem::create_directories(log_archive_path))
93  {
94  debug::log_debug("Created log archive \"{}\"", log_archive_path.string());
95  }
96  else
97  {
98  // Clean pre-existing log archive
99  try
100  {
101  // Detect and sort archived logs
102  std::set<std::filesystem::path> log_archive;
103  for (const auto& entry: std::filesystem::directory_iterator{log_archive_path})
104  {
105  if (entry.is_regular_file() &&
106  entry.path().extension() == ".log")
107  {
108  log_archive.emplace(entry.path());
109  }
110  }
111 
112  debug::log_debug("Detected {} archived log{} at \"{}\"", log_archive.size(), log_archive.size() != 1 ? "s" : "", log_archive_path.string());
113 
114  // Delete expired logs
115  if (!log_archive.empty())
116  {
117  for (std::size_t i = log_archive.size() + 1; i > config::debug_log_archive_capacity; --i)
118  {
119  std::filesystem::remove(*log_archive.begin());
120  debug::log_debug("Deleted expired log file \"{}\"", log_archive.begin()->string());
121  log_archive.erase(log_archive.begin());
122  }
123  }
124  }
125  catch (const std::filesystem::filesystem_error& e)
126  {
127  debug::log_error("An error occured while cleaning the log archive \"{}\": {}", log_archive_path.string(), e.what());
128  }
129  }
130 
131  log_archive_exists = true;
132  }
133  catch (const std::filesystem::filesystem_error& e)
134  {
135  debug::log_error("Failed to create log archive at \"{}\": {}", log_archive_path.string(), e.what());
136  }
137 
138  // Set up logging to file
139  std::shared_ptr<event::subscription> log_to_file_subscription;
140  std::filesystem::path log_filepath;
141  if (config::debug_log_archive_capacity && log_archive_exists)
142  {
143  // Determine log filename
144  const auto time = std::chrono::floor<std::chrono::seconds>(launch_time);
145  const std::string log_filename = std::format("{0}-{1:%Y%m%d}T{1:%H%M%S}Z.log", config::application_slug, time);
146 
147  // Open log file
148  log_filepath = log_archive_path / log_filename;
149  const std::string log_filepath_string = log_filepath.string();
150  auto log_filestream = std::make_shared<std::ofstream>(log_filepath);
151 
152  if (log_filestream->is_open())
153  {
154  debug::log_debug("Opened log file \"{}\"", log_filepath_string);
155 
156  // Write log file header
157  (*log_filestream) << "time\tfile\tline\tcolumn\tseverity\tmessage";
158 
159  if (log_filestream->good())
160  {
161  // Subscribe log to file function to message logged events
162  log_to_file_subscription = debug::default_logger().get_message_logged_channel().subscribe
163  (
164  [&launch_time, log_filestream](const auto& event)
165  {
166  std::osyncstream(*log_filestream) << std::format
167  (
168  "\n{:.03f}\t{}\t{}\t{}\t{}\t{}",
169  std::chrono::duration<float>(event.time - launch_time).count(),
170  std::filesystem::path(event.location.file_name()).filename().string(),
171  event.location.line(),
172  event.location.column(),
173  static_cast<int>(event.severity),
174  event.message
175  );
176  }
177  );
178 
179  // Unsubscribe log to cout function from message logged events on release builds
180  #if defined(NDEBUG)
181  log_to_cout_subscription->unsubscribe();
182  #endif
183  }
184  else
185  {
186  debug::log_error("Failed to write to log file \"{}\"", log_filepath_string);
187  }
188  }
189  else
190  {
191  debug::log_error("Failed to open log file \"{}\"", log_filepath_string);
192  }
193  }
194 
195  // Log application name and version string, followed by launch time
196  debug::log_info("{0} {1}; {2:%Y%m%d}T{2:%H%M%S}Z", config::application_name, config::application_version_string, std::chrono::floor<std::chrono::milliseconds>(launch_time));
197 
198  // Start marker
199  debug::log_debug("Hi! 🐜");
200 
201  // #if defined(NDEBUG)
202  try
203  {
204  game(argc, argv).execute();
205  }
206  catch (const std::exception& e)
207  {
208  debug::log_fatal("Unhandled exception: {}", e.what());
209  SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", std::format("Unhandled exception: {}", e.what()).c_str(), nullptr);
210 
211  return EXIT_FAILURE;
212  }
213  // #else
214  // game(argc, argv).execute();
215  // #endif
216 
217  // Clean exit marker
218  debug::log_debug("Bye! 🐜");
219 
220  return EXIT_SUCCESS;
221 }
::event::channel< message_logged_event > & get_message_logged_channel() noexcept
Returns the channel through which message logged events are published.
Definition: logger.hpp:55
Definition: game.hpp:121
void execute()
Executes the game.
Definition: game.cpp:1268
int main(int argc, char *argv[])
Definition: main.cpp:34
const char * fg_bright_blue
Definition: ansi.hpp:61
const char * fg_yellow
Definition: ansi.hpp:40
const char * reset
Definition: ansi.hpp:30
const char * fg_bright_green
Definition: ansi.hpp:59
const char * fg_white
Definition: ansi.hpp:44
const char * fg_red
Definition: ansi.hpp:38
const char * bg_bright_red
Definition: ansi.hpp:66
void enable_vt100()
Enables VT100 virtual terminal sequences.
Definition: console.cpp:30
void enable_utf8()
Enables UTF-8 output.
Definition: console.cpp:50
log_message< log_message_severity::fatal, Args... > log_fatal
Formats and logs a fatal error message.
Definition: log.hpp:158
log_message< log_message_severity::debug, Args... > log_debug
Formats and logs a debug message.
Definition: log.hpp:102
log_message< log_message_severity::error, Args... > log_error
Formats and logs an error message.
Definition: log.hpp:144
logger & default_logger() noexcept
Returns the default logger.
Definition: log.cpp:24
log_message< log_message_severity::info, Args... > log_info
Formats and logs an info message.
Definition: log.hpp:116
Publish-subscribe messaging.
Definition: channel.hpp:32
format
Image and vertex formats.
Definition: format.hpp:29
constexpr T e
e.
Definition: numbers.hpp:37
std::filesystem::path get_shared_config_path()
Returns the absolute path to the directory containing user-specific application data that may be shar...
Definition: paths.cpp:110