// Crash and log helpers. string log_format_timestamp() { datetime dt; string stamp = dt.format(DATE_TIME_FORMAT_RFC1123); return "[" + stamp + "]"; } string log_flatten_multiline(const string& in text) { string result = ""; bool lastWasSeparator = false; for (uint charIndex = 0; charIndex < text.length(); charIndex++) { string ch = text.substr(charIndex, 1); if (ch == "\r" || ch == "\n") { if (!lastWasSeparator) { result += " | "; lastWasSeparator = true; } continue; } result += ch; lastWasSeparator = false; } return result; } bool log_append_line(const string& in logPath, const string& in message) { file logFile; if (!logFile.open(logPath, "ab")) { return false; } // Keep log format as message then timestamp. logFile.write(message + " " + log_format_timestamp() + "\r\n"); logFile.close(); return true; } void log_unhandled_exception_to_file(const string& in logPath = "crash.log", const string& in context = "") { string info = get_exception_info(); string filePath = get_exception_file(); int line = get_exception_line(); string func = get_exception_function(); string stack = log_flatten_multiline(last_exception_call_stack); string message = "Unhandled exception"; if (context != "") message += " (" + context + ")"; if (info != "") message += ": " + info; if (filePath != "") message += " at " + filePath; if (line > 0) message += ":" + line; if (func != "") message += " in " + func; if (stack != "") message += " | stack: " + stack; log_append_line(logPath, message); }