28 #include <boost/algorithm/string/predicate.hpp>
29 #include <boost/noncopyable.hpp>
30 #include <boost/scoped_ptr.hpp>
36 #define WIN32_LEAN_AND_MEAN
41 #define ERR_LS LOG_STREAM(err, log_setup)
42 #define WRN_LS LOG_STREAM(warn, log_setup)
43 #define LOG_LS LOG_STREAM(info, log_setup)
44 #define DBG_LS LOG_STREAM(debug, log_setup)
59 const unsigned max_logs = 8;
64 return !(boost::algorithm::istarts_with(fn, log_file_prefix) &&
65 boost::algorithm::iends_with(fn, log_file_suffix));
73 std::vector<std::string> files;
76 files.erase(std::remove_if(files.begin(), files.end(), is_not_log_file), files.end());
78 if(files.size() <= max_logs) {
88 for(
size_t j = 0; j < files.size() - max_logs; ++j) {
90 LOG_LS <<
"rotate_logs(): delete " << path <<
'\n';
92 WRN_LS <<
"rotate_logs(): failed to delete " << path <<
"!\n";
109 std::ostringstream o;
111 o << log_file_prefix;
113 const time_t cur = time(
nullptr);
114 const tm*
const lt = localtime(&cur);
117 char ts_buf[128] = { 0 };
118 strftime(ts_buf, 128,
"%Y%m%d-%H%M%S-", lt);
122 o << GetCurrentProcessId() << log_file_suffix;
132 wchar_t tmpdir[MAX_PATH + 1];
134 if(GetTempPath(MAX_PATH + 1, tmpdir) == 0) {
146 ERR_LS <<
"Log initialization panic call: " << msg <<
'\n';
148 const std::string full_msg = msg +
"\n\n" +
"This may indicate an issue with your Wesnoth launch configuration. If the problem persists, contact the development team for technical support, including the full contents of this message (copy with CTRL+C).";
153 unicode_cast<std::wstring>(full_msg).c_str(),
154 L
"Battle for Wesnoth",
155 MB_ICONEXCLAMATION | MB_OK);
171 std::ostringstream
msg;
173 if(old_log_path.empty()) {
174 msg <<
"Early log initialization failed.";
176 msg <<
"Log relocation failed.";
180 <<
"Runtime error: " << e.
desc() <<
" (" << e.
num() <<
")\n";
182 if(old_log_path.empty()) {
183 msg <<
"Log file path: " << new_log_path <<
'\n';
185 msg <<
"New log file path: " << new_log_path <<
'\n'
186 <<
"Old log file path: " << old_log_path;
189 log_init_panic(msg.str());
195 class log_file_manager :
private boost::noncopyable
198 log_file_manager(
bool native_console =
false);
230 bool console_enabled()
const;
239 bool console_attached()
const;
275 void do_redirect_single_stream(
const std::string& file_path,
280 log_file_manager::log_file_manager(
bool native_console)
281 : fn_(unique_log_filename())
283 , use_wincon_(console_attached())
285 DBG_LS <<
"Early init message\n";
291 LOG_LS <<
"Console already attached at startup, log file disabled.\n";
303 const std::string new_path = temp_dir() +
"/" + fn_;
306 open_log_file(new_path,
true);
308 log_init_panic(e, new_path, cur_path_);
311 LOG_LS <<
"Opened log file at " << new_path <<
'\n';
314 log_file_manager::~log_file_manager()
316 if(cur_path_.empty()) {
330 void log_file_manager::move_log_file(
const std::string& log_dir)
335 if(!cur_path_.empty()) {
340 open_log_file(
"NUL",
false);
342 const std::wstring old_path_w
344 const std::wstring new_path_w
347 if(_wrename(old_path_w.c_str(), new_path_w.c_str()) != 0) {
353 open_log_file(new_path,
false);
355 log_init_panic(e, new_path, cur_path_);
358 LOG_LS <<
"Moved log file to " << new_path <<
'\n';
363 do_redirect_single_stream(file_path, STREAM_STDERR, truncate);
364 do_redirect_single_stream(file_path, STREAM_STDOUT,
false);
366 cur_path_ = file_path;
369 void log_file_manager::do_redirect_single_stream(
const std::string& file_path,
370 log_file_manager::STREAM_ID
stream,
373 DBG_LS << stream <<
' ' << cur_path_ <<
" -> " << file_path <<
" [side A]\n";
375 FILE* crts = stream == STREAM_STDERR ? stderr : stdout;
376 std::ostream& cxxs = stream == STREAM_STDERR ? std::cerr : std::cout;
381 const std::wstring file_path_w =
unicode_cast<std::wstring>(file_path);
383 if(!_wfreopen(file_path_w.c_str(), (truncate ? L
"w" : L
"a"), crts))
390 DBG_LS << stream <<
' ' << cur_path_ <<
" -> " << file_path <<
" [side B]\n";
393 bool log_file_manager::console_enabled()
const
398 bool log_file_manager::console_attached()
const
400 return GetConsoleWindow() !=
nullptr;
411 if(AttachConsole(ATTACH_PARENT_PROCESS)) {
412 LOG_LS <<
"Attached parent process console.\n";
413 }
else if(AllocConsole()) {
414 LOG_LS <<
"Allocated own console.\n";
416 ERR_LS <<
"Console attachment or allocation failed!\n";
420 DBG_LS <<
"stderr to console\n";
423 freopen(
"CONOUT$",
"wb", stderr);
425 DBG_LS <<
"stdout to console\n";
428 freopen(
"CONOUT$",
"wb", stdout);
430 DBG_LS <<
"stdin from console\n";
431 freopen(
"CONIN$",
"rb", stdin);
439 LOG_LS <<
"Console streams handover complete!\n";
442 boost::scoped_ptr<log_file_manager> lfm;
449 return lfm->log_file_path();
461 lfm.reset(
new log_file_manager());
467 lfm->enable_native_console_output();
471 lfm.reset(
new log_file_manager(
true));
479 if(lfm->console_enabled()) {
484 static bool setup_complete =
false;
487 ERR_LS <<
"finish_log_file_setup() called more than once!\n";
493 log_init_panic(
std::string(
"Could not create logs directory at ") +
496 rotate_logs(log_dir);
499 lfm->move_log_file(log_dir);
501 setup_complete =
true;
size_t strftime(char *str, size_t count, const std::string &format, const std::tm *time)
std::string log_file_path()
Returns the path to the current log file.
bool delete_file(const std::string &filename)
ucs4_convert_impl::enableif< TD, typename TS::value_type >::type unicode_cast(const TS &source)
const std::string & desc() const
Returns an explanatory string describing the runtime error alone.
void finish_log_file_setup()
Relocates the stdout+stderr log file to the user data directory.
void early_log_file_setup()
Sets up the initial temporary log file.
static lg::log_domain log_setup("logsetup")
GLsizei const char ** path
Exception type used to propagate C runtime errors across functions.
std::string get_user_data_dir()
void enable_native_console_output()
Switches to using a native console instead of log file redirection.
utf8::string & truncate(utf8::string &str, const size_t size)
Truncates a UTF-8 string to the specified number of characters.
void get_files_in_dir(const std::string &dir, std::vector< std::string > *files, std::vector< std::string > *dirs=nullptr, file_name_option mode=FILE_NAME_ONLY, file_filter_option filter=NO_FILTER, file_reorder_option reorder=DONT_REORDER, file_tree_checksum *checksum=nullptr)
Populates 'files' with all the files and 'dirs' with all the directories in dir.
Log file control routines for Windows.
bool make_directory(const std::string &dirname)
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Declarations for File-IO.
static int sort(lua_State *L)
Standard logging facilities (interface).
int num() const
Returns the value of errno at the time the exception was thrown.
bool file_exists(const std::string &name)
Returns true if a file or directory with such name already exists.
GLsizei const GLcharARB ** string