Browse Source

Merge pull request #11568 from endragor/loggers

Extract logging logic
Andreas Haas 8 years ago
parent
commit
132ba0ed97
49 changed files with 948 additions and 332 deletions
  1. 2 1
      core/error_macros.cpp
  2. 5 0
      core/io/file_access_buffered_fa.h
  3. 7 0
      core/io/file_access_compressed.cpp
  4. 1 0
      core/io/file_access_compressed.h
  5. 6 0
      core/io/file_access_encrypted.cpp
  6. 1 0
      core/io/file_access_encrypted.h
  7. 4 0
      core/io/file_access_memory.cpp
  8. 1 0
      core/io/file_access_memory.h
  9. 4 0
      core/io/file_access_network.cpp
  10. 1 0
      core/io/file_access_network.h
  11. 5 0
      core/io/file_access_pack.cpp
  12. 1 0
      core/io/file_access_pack.h
  13. 5 0
      core/io/file_access_zip.cpp
  14. 1 0
      core/io/file_access_zip.h
  15. 252 0
      core/io/logger.cpp
  16. 107 0
      core/io/logger.h
  17. 1 1
      core/os/file_access.cpp
  18. 1 0
      core/os/file_access.h
  19. 18 16
      core/os/os.cpp
  20. 10 11
      core/os/os.h
  21. 6 0
      drivers/unix/file_access_unix.cpp
  22. 1 0
      drivers/unix/file_access_unix.h
  23. 41 52
      drivers/unix/os_unix.cpp
  24. 7 5
      drivers/unix/os_unix.h
  25. 71 0
      drivers/unix/syslog_logger.cpp
  26. 48 0
      drivers/unix/syslog_logger.h
  27. 1 1
      drivers/windows/dir_access_windows.cpp
  28. 6 0
      drivers/windows/file_access_windows.cpp
  29. 1 0
      drivers/windows/file_access_windows.h
  30. 2 1
      main/main.cpp
  31. 5 0
      platform/android/file_access_android.cpp
  32. 1 0
      platform/android/file_access_android.h
  33. 3 0
      platform/android/file_access_jandroid.cpp
  34. 1 0
      platform/android/file_access_jandroid.h
  35. 18 14
      platform/android/os_android.cpp
  36. 1 2
      platform/android/os_android.h
  37. 10 0
      platform/iphone/os_iphone.cpp
  38. 1 0
      platform/iphone/os_iphone.h
  39. 4 0
      platform/javascript/os_javascript.cpp
  40. 1 5
      platform/javascript/os_javascript.h
  41. 1 2
      platform/osx/os_osx.h
  42. 58 32
      platform/osx/os_osx.mm
  43. 10 50
      platform/uwp/os_uwp.cpp
  44. 1 3
      platform/uwp/os_uwp.h
  45. 1 0
      platform/windows/SCsub
  46. 10 133
      platform/windows/os_windows.cpp
  47. 1 3
      platform/windows/os_windows.h
  48. 157 0
      platform/windows/windows_terminal_logger.cpp
  49. 47 0
      platform/windows/windows_terminal_logger.h

+ 2 - 1
core/error_macros.cpp

@@ -29,6 +29,7 @@
 /*************************************************************************/
 #include "error_macros.h"
 
+#include "io/logger.h"
 #include "os/os.h"
 
 bool _err_error_exists = false;
@@ -79,7 +80,7 @@ void remove_error_handler(ErrorHandlerList *p_handler) {
 
 void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, ErrorHandlerType p_type) {
 
-	OS::get_singleton()->print_error(p_function, p_file, p_line, p_error, _err_error_exists ? OS::get_singleton()->get_last_error() : "", (OS::ErrorType)p_type);
+	OS::get_singleton()->print_error(p_function, p_file, p_line, p_error, _err_error_exists ? OS::get_singleton()->get_last_error() : "", (Logger::ErrorType)p_type);
 
 	_global_lock();
 	ErrorHandlerList *l = error_handler_list;

+ 5 - 0
core/io/file_access_buffered_fa.h

@@ -76,6 +76,11 @@ protected:
 	};
 
 public:
+	void flush() {
+
+		f.flush();
+	};
+
 	void store_8(uint8_t p_dest) {
 
 		f.store_8(p_dest);

+ 7 - 0
core/io/file_access_compressed.cpp

@@ -338,6 +338,13 @@ Error FileAccessCompressed::get_error() const {
 	return read_eof ? ERR_FILE_EOF : OK;
 }
 
+void FileAccessCompressed::flush() {
+	ERR_FAIL_COND(!f);
+	ERR_FAIL_COND(!writing);
+
+	// compressed files keep data in memory till close()
+}
+
 void FileAccessCompressed::store_8(uint8_t p_dest) {
 
 	ERR_FAIL_COND(!f);

+ 1 - 0
core/io/file_access_compressed.h

@@ -84,6 +84,7 @@ public:
 
 	virtual Error get_error() const; ///< get last error
 
+	virtual void flush();
 	virtual void store_8(uint8_t p_dest); ///< store a byte
 
 	virtual bool file_exists(const String &p_name); ///< return true if a file exists

+ 6 - 0
core/io/file_access_encrypted.cpp

@@ -268,6 +268,12 @@ void FileAccessEncrypted::store_buffer(const uint8_t *p_src, int p_length) {
 	}
 }
 
+void FileAccessEncrypted::flush() {
+	ERR_FAIL_COND(!writing);
+
+	// encrypted files keep data in memory till close()
+}
+
 void FileAccessEncrypted::store_8(uint8_t p_dest) {
 
 	ERR_FAIL_COND(!writing);

+ 1 - 0
core/io/file_access_encrypted.h

@@ -71,6 +71,7 @@ public:
 
 	virtual Error get_error() const; ///< get last error
 
+	virtual void flush();
 	virtual void store_8(uint8_t p_dest); ///< store a byte
 	virtual void store_buffer(const uint8_t *p_src, int p_length); ///< store an array of bytes
 

+ 4 - 0
core/io/file_access_memory.cpp

@@ -170,6 +170,10 @@ Error FileAccessMemory::get_error() const {
 	return pos >= length ? ERR_FILE_EOF : OK;
 }
 
+void FileAccessMemory::flush() {
+	ERR_FAIL_COND(!data);
+}
+
 void FileAccessMemory::store_8(uint8_t p_byte) {
 
 	ERR_FAIL_COND(!data);

+ 1 - 0
core/io/file_access_memory.h

@@ -62,6 +62,7 @@ public:
 
 	virtual Error get_error() const; ///< get last error
 
+	virtual void flush();
 	virtual void store_8(uint8_t p_byte); ///< store a byte
 	virtual void store_buffer(const uint8_t *p_src, int p_length); ///< store an array of bytes
 

+ 4 - 0
core/io/file_access_network.cpp

@@ -456,6 +456,10 @@ Error FileAccessNetwork::get_error() const {
 	return pos == total_size ? ERR_FILE_EOF : OK;
 }
 
+void FileAccessNetwork::flush() {
+	ERR_FAIL();
+}
+
 void FileAccessNetwork::store_8(uint8_t p_dest) {
 
 	ERR_FAIL();

+ 1 - 0
core/io/file_access_network.h

@@ -155,6 +155,7 @@ public:
 
 	virtual Error get_error() const; ///< get last error
 
+	virtual void flush();
 	virtual void store_8(uint8_t p_dest); ///< store a byte
 
 	virtual bool file_exists(const String &p_path); ///< return true if a file exists

+ 5 - 0
core/io/file_access_pack.cpp

@@ -293,6 +293,11 @@ Error FileAccessPack::get_error() const {
 	return OK;
 }
 
+void FileAccessPack::flush() {
+
+	ERR_FAIL();
+}
+
 void FileAccessPack::store_8(uint8_t p_dest) {
 
 	ERR_FAIL();

+ 1 - 0
core/io/file_access_pack.h

@@ -161,6 +161,7 @@ public:
 
 	virtual Error get_error() const;
 
+	virtual void flush();
 	virtual void store_8(uint8_t p_dest);
 
 	virtual void store_buffer(const uint8_t *p_src, int p_length);

+ 5 - 0
core/io/file_access_zip.cpp

@@ -353,6 +353,11 @@ Error FileAccessZip::get_error() const {
 	return OK;
 };
 
+void FileAccessZip::flush() {
+
+	ERR_FAIL();
+}
+
 void FileAccessZip::store_8(uint8_t p_dest) {
 
 	ERR_FAIL();

+ 1 - 0
core/io/file_access_zip.h

@@ -108,6 +108,7 @@ public:
 
 	virtual Error get_error() const; ///< get last error
 
+	virtual void flush();
 	virtual void store_8(uint8_t p_dest); ///< store a byte
 	virtual bool file_exists(const String &p_name); ///< return true if a file exists
 

+ 252 - 0
core/io/logger.cpp

@@ -0,0 +1,252 @@
+/*************************************************************************/
+/*  logger.cpp                                                           */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "logger.h"
+#include "os/dir_access.h"
+#include "os/os.h"
+#include "print_string.h"
+
+bool Logger::should_log(bool p_err) {
+	return (!p_err || _print_error_enabled) && (p_err || _print_line_enabled);
+}
+
+void Logger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
+	if (!should_log(true)) {
+		return;
+	}
+
+	const char *err_type = "**ERROR**";
+	switch (p_type) {
+		case ERR_ERROR: err_type = "**ERROR**"; break;
+		case ERR_WARNING: err_type = "**WARNING**"; break;
+		case ERR_SCRIPT: err_type = "**SCRIPT ERROR**"; break;
+		case ERR_SHADER: err_type = "**SHADER ERROR**"; break;
+		default: ERR_PRINT("Unknown error type"); break;
+	}
+
+	const char *err_details;
+	if (p_rationale && *p_rationale)
+		err_details = p_rationale;
+	else
+		err_details = p_code;
+
+	logf_error("%s: %s\n", err_type, err_details);
+	logf_error("   At: %s:%i:%s() - %s\n", p_file, p_line, p_function, p_code);
+}
+
+void Logger::logf(const char *p_format, ...) {
+	if (!should_log(false)) {
+		return;
+	}
+
+	va_list argp;
+	va_start(argp, p_format);
+
+	logv(p_format, argp, false);
+
+	va_end(argp);
+}
+
+void Logger::logf_error(const char *p_format, ...) {
+	if (!should_log(true)) {
+		return;
+	}
+
+	va_list argp;
+	va_start(argp, p_format);
+
+	logv(p_format, argp, true);
+
+	va_end(argp);
+}
+
+Logger::~Logger() {}
+
+void RotatedFileLogger::close_file() {
+	if (file) {
+		memdelete(file);
+		file = NULL;
+	}
+}
+
+void RotatedFileLogger::clear_old_backups() {
+	int max_backups = max_files - 1; // -1 for the current file
+
+	String basename = base_path.get_basename();
+	String extension = "." + base_path.get_extension();
+
+	DirAccess *da = DirAccess::open(base_path.get_base_dir());
+	if (!da) {
+		return;
+	}
+
+	da->list_dir_begin();
+	String f = da->get_next();
+	Set<String> backups;
+	while (f != String()) {
+		if (!da->current_is_dir() && f.begins_with(basename) && f.ends_with(extension) && f != base_path) {
+			backups.insert(f);
+		}
+		f = da->get_next();
+	}
+	da->list_dir_end();
+
+	if (backups.size() > max_backups) {
+		// since backups are appended with timestamp and Set iterates them in sorted order,
+		// first backups are the oldest
+		int to_delete = backups.size() - max_backups;
+		for (Set<String>::Element *E = backups.front(); E && to_delete > 0; E = E->next(), --to_delete) {
+			da->remove(E->get());
+		}
+	}
+
+	memdelete(da);
+}
+
+void RotatedFileLogger::rotate_file() {
+	close_file();
+
+	if (FileAccess::exists(base_path)) {
+		if (max_files > 1) {
+			char timestamp[21];
+			OS::Date date = OS::get_singleton()->get_date();
+			OS::Time time = OS::get_singleton()->get_time();
+			sprintf(timestamp, "-%04d-%02d-%02d-%02d-%02d-%02d", date.year, date.month, date.day + 1, time.hour, time.min, time.sec);
+
+			String backup_name = base_path.get_basename() + timestamp + "." + base_path.get_extension();
+
+			DirAccess *da = DirAccess::open(base_path.get_base_dir());
+			if (da) {
+				da->copy(base_path, backup_name);
+				memdelete(da);
+			}
+			clear_old_backups();
+		}
+	} else {
+		DirAccess *da = DirAccess::create(DirAccess::ACCESS_USERDATA);
+		if (da) {
+			da->make_dir_recursive(base_path.get_base_dir());
+			memdelete(da);
+		}
+	}
+
+	file = FileAccess::open(base_path, FileAccess::WRITE);
+}
+
+RotatedFileLogger::RotatedFileLogger(const String &p_base_path, int p_max_files) {
+	file = NULL;
+	base_path = p_base_path.simplify_path();
+	max_files = p_max_files > 0 ? p_max_files : 1;
+
+	rotate_file();
+}
+
+void RotatedFileLogger::logv(const char *p_format, va_list p_list, bool p_err) {
+	if (!should_log(p_err)) {
+		return;
+	}
+
+	if (file) {
+		const int static_buf_size = 512;
+		char static_buf[static_buf_size];
+		char *buf = static_buf;
+		int len = vsnprintf(buf, static_buf_size, p_format, p_list);
+		if (len >= static_buf_size) {
+			buf = (char *)Memory::alloc_static(len + 1);
+			vsnprintf(buf, len + 1, p_format, p_list);
+		}
+		file->store_buffer((uint8_t *)buf, len);
+		if (len >= static_buf_size) {
+			Memory::free_static(buf);
+		}
+#ifdef DEBUG_ENABLED
+		const bool need_flush = true;
+#else
+		bool need_flush = p_err;
+#endif
+		if (need_flush) {
+			file->flush();
+		}
+	}
+}
+
+RotatedFileLogger::~RotatedFileLogger() {
+	close_file();
+}
+
+void StdLogger::logv(const char *p_format, va_list p_list, bool p_err) {
+	if (!should_log(p_err)) {
+		return;
+	}
+
+	if (p_err) {
+		vfprintf(stderr, p_format, p_list);
+	} else {
+		vprintf(p_format, p_list);
+#ifdef DEBUG_ENABLED
+		fflush(stdout);
+#endif
+	}
+}
+
+StdLogger::~StdLogger() {}
+
+CompositeLogger::CompositeLogger(Vector<Logger *> p_loggers) {
+	loggers = p_loggers;
+}
+
+void CompositeLogger::logv(const char *p_format, va_list p_list, bool p_err) {
+	if (!should_log(p_err)) {
+		return;
+	}
+
+	for (int i = 0; i < loggers.size(); ++i) {
+		va_list list_copy;
+		va_copy(list_copy, p_list);
+		loggers[i]->logv(p_format, list_copy, p_err);
+		va_end(list_copy);
+	}
+}
+
+void CompositeLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
+	if (!should_log(true)) {
+		return;
+	}
+
+	for (int i = 0; i < loggers.size(); ++i) {
+		loggers[i]->log_error(p_function, p_file, p_line, p_code, p_rationale, p_type);
+	}
+}
+
+CompositeLogger::~CompositeLogger() {
+	for (int i = 0; i < loggers.size(); ++i) {
+		memdelete(loggers[i]);
+	}
+}

+ 107 - 0
core/io/logger.h

@@ -0,0 +1,107 @@
+/*************************************************************************/
+/*  logger.h                                                             */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef LOGGER_H
+#define LOGGER_H
+
+#include "os/file_access.h"
+#include "ustring.h"
+#include "vector.h"
+#include <stdarg.h>
+
+class Logger {
+protected:
+	bool should_log(bool p_err);
+
+public:
+	enum ErrorType {
+		ERR_ERROR,
+		ERR_WARNING,
+		ERR_SCRIPT,
+		ERR_SHADER
+	};
+
+	virtual void logv(const char *p_format, va_list p_list, bool p_err) = 0;
+	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR);
+
+	void logf(const char *p_format, ...);
+	void logf_error(const char *p_format, ...);
+
+	virtual ~Logger();
+};
+
+/**
+ * Writes messages to stdout/stderr.
+ */
+class StdLogger : public Logger {
+
+public:
+	virtual void logv(const char *p_format, va_list p_list, bool p_err);
+	virtual ~StdLogger();
+};
+
+/**
+ * Writes messages to the specified file. If the file already exists, creates a copy (backup)
+ * of it with timestamp appended to the file name. Maximum number of backups is configurable.
+ * When maximum is reached, the oldest backups are erased. With the maximum being equal to 1,
+ * it acts as a simple file logger.
+ */
+class RotatedFileLogger : public Logger {
+	String base_path;
+	int max_files;
+
+	FileAccess *file;
+
+	void rotate_file_without_closing();
+	void close_file();
+	void clear_old_backups();
+	void rotate_file();
+
+public:
+	RotatedFileLogger(const String &p_base_path, int p_max_files = 10);
+
+	virtual void logv(const char *p_format, va_list p_list, bool p_err);
+
+	virtual ~RotatedFileLogger();
+};
+
+class CompositeLogger : public Logger {
+	Vector<Logger *> loggers;
+
+public:
+	CompositeLogger(Vector<Logger *> p_loggers);
+
+	virtual void logv(const char *p_format, va_list p_list, bool p_err);
+	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR);
+
+	virtual ~CompositeLogger();
+};
+
+#endif

+ 1 - 1
core/os/file_access.cpp

@@ -55,7 +55,7 @@ FileAccess *FileAccess::create(AccessType p_access) {
 
 bool FileAccess::exists(const String &p_name) {
 
-	if (PackedData::get_singleton()->has_path(p_name))
+	if (PackedData::get_singleton() && PackedData::get_singleton()->has_path(p_name))
 		return true;
 
 	FileAccess *f = open(p_name, READ);

+ 1 - 0
core/os/file_access.h

@@ -119,6 +119,7 @@ public:
 
 	virtual Error get_error() const = 0; ///< get last error
 
+	virtual void flush() = 0;
 	virtual void store_8(uint8_t p_dest) = 0; ///< store a byte
 	virtual void store_16(uint16_t p_dest); ///< store 16 bits uint
 	virtual void store_32(uint32_t p_dest); ///< store 32 bits uint

+ 18 - 16
core/os/os.cpp

@@ -62,20 +62,20 @@ void OS::debug_break(){
 	// something
 };
 
-void OS::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
-
-	const char *err_type = "**ERROR**";
-	switch (p_type) {
-		case ERR_ERROR: err_type = "**ERROR**"; break;
-		case ERR_WARNING: err_type = "**WARNING**"; break;
-		case ERR_SCRIPT: err_type = "**SCRIPT ERROR**"; break;
-		case ERR_SHADER: err_type = "**SHADER ERROR**"; break;
-		default: ERR_PRINT("Unknown error type"); break;
+void OS::_set_logger(Logger *p_logger) {
+	if (_logger) {
+		memdelete(_logger);
 	}
+	_logger = p_logger;
+}
+
+void OS::initialize_logger() {
+	_set_logger(memnew(StdLogger));
+}
+
+void OS::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type) {
 
-	if (p_rationale && *p_rationale)
-		print("%s: %s\n ", err_type, p_rationale);
-	print("%s: At: %s:%i:%s() - %s\n", err_type, p_file, p_line, p_function, p_code);
+	_logger->log_error(p_function, p_file, p_line, p_code, p_rationale, p_type);
 }
 
 void OS::print(const char *p_format, ...) {
@@ -83,17 +83,16 @@ void OS::print(const char *p_format, ...) {
 	va_list argp;
 	va_start(argp, p_format);
 
-	vprint(p_format, argp);
+	_logger->logv(p_format, argp, false);
 
 	va_end(argp);
 };
 
 void OS::printerr(const char *p_format, ...) {
-
 	va_list argp;
 	va_start(argp, p_format);
 
-	vprint(p_format, argp, true);
+	_logger->logv(p_format, argp, true);
 
 	va_end(argp);
 };
@@ -533,9 +532,12 @@ OS::OS() {
 
 	_allow_hidpi = true;
 	_stack_bottom = (void *)(&stack_bottom);
+
+	_logger = NULL;
+	_set_logger(memnew(StdLogger));
 }
 
 OS::~OS() {
-
+	memdelete(_logger);
 	singleton = NULL;
 }

+ 10 - 11
core/os/os.h

@@ -32,6 +32,7 @@
 
 #include "engine.h"
 #include "image.h"
+#include "io/logger.h"
 #include "list.h"
 #include "os/main_loop.h"
 #include "ustring.h"
@@ -61,6 +62,11 @@ class OS {
 
 	void *_stack_bottom;
 
+	Logger *_logger;
+
+protected:
+	void _set_logger(Logger *p_logger);
+
 public:
 	typedef void (*ImeCallback)(void *p_inp, String p_text, Point2 p_selection);
 
@@ -108,6 +114,7 @@ protected:
 	virtual int get_audio_driver_count() const = 0;
 	virtual const char *get_audio_driver_name(int p_driver) const = 0;
 
+	virtual void initialize_logger();
 	virtual void initialize_core() = 0;
 	virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) = 0;
 
@@ -127,18 +134,10 @@ public:
 
 	static OS *get_singleton();
 
-	enum ErrorType {
-		ERR_ERROR,
-		ERR_WARNING,
-		ERR_SCRIPT,
-		ERR_SHADER
-	};
-
-	virtual void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR);
+	void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type = Logger::ERR_ERROR);
+	void print(const char *p_format, ...);
+	void printerr(const char *p_format, ...);
 
-	virtual void print(const char *p_format, ...);
-	virtual void printerr(const char *p_format, ...);
-	virtual void vprint(const char *p_format, va_list p_list, bool p_stderr = false) = 0;
 	virtual void alert(const String &p_alert, const String &p_title = "ALERT!") = 0;
 	virtual String get_stdin_string(bool p_block = true) = 0;
 

+ 6 - 0
drivers/unix/file_access_unix.cpp

@@ -223,6 +223,12 @@ Error FileAccessUnix::get_error() const {
 	return last_error;
 }
 
+void FileAccessUnix::flush() {
+
+	ERR_FAIL_COND(!f);
+	fflush(f);
+}
+
 void FileAccessUnix::store_8(uint8_t p_dest) {
 
 	ERR_FAIL_COND(!f);

+ 1 - 0
drivers/unix/file_access_unix.h

@@ -72,6 +72,7 @@ public:
 
 	virtual Error get_error() const; ///< get last error
 
+	virtual void flush();
 	virtual void store_8(uint8_t p_dest); ///< store a byte
 
 	virtual bool file_exists(const String &p_path); ///< return true if a file exists

+ 41 - 52
drivers/unix/os_unix.cpp

@@ -64,39 +64,7 @@
 #include <string.h>
 #include <sys/time.h>
 #include <sys/wait.h>
-
-extern bool _print_error_enabled;
-
-void OS_Unix::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
-
-	if (!_print_error_enabled)
-		return;
-
-	const char *err_details;
-	if (p_rationale && p_rationale[0])
-		err_details = p_rationale;
-	else
-		err_details = p_code;
-
-	switch (p_type) {
-		case ERR_ERROR:
-			print("\E[1;31mERROR: %s: \E[0m\E[1m%s\n", p_function, err_details);
-			print("\E[0;31m   At: %s:%i.\E[0m\n", p_file, p_line);
-			break;
-		case ERR_WARNING:
-			print("\E[1;33mWARNING: %s: \E[0m\E[1m%s\n", p_function, err_details);
-			print("\E[0;33m   At: %s:%i.\E[0m\n", p_file, p_line);
-			break;
-		case ERR_SCRIPT:
-			print("\E[1;35mSCRIPT ERROR: %s: \E[0m\E[1m%s\n", p_function, err_details);
-			print("\E[0;35m   At: %s:%i.\E[0m\n", p_file, p_line);
-			break;
-		case ERR_SHADER:
-			print("\E[1;36mSHADER ERROR: %s: \E[0m\E[1m%s\n", p_function, err_details);
-			print("\E[0;36m   At: %s:%i.\E[0m\n", p_file, p_line);
-			break;
-	}
-}
+#include <unistd.h>
 
 void OS_Unix::debug_break() {
 
@@ -165,29 +133,16 @@ void OS_Unix::initialize_core() {
 	}
 }
 
-void OS_Unix::finalize_core() {
+void OS_Unix::initialize_logger() {
+	Vector<Logger *> loggers;
+	loggers.push_back(memnew(UnixTerminalLogger));
+	loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt")));
+	_set_logger(memnew(CompositeLogger(loggers)));
 }
 
-void OS_Unix::vprint(const char *p_format, va_list p_list, bool p_stder) {
-
-	if (p_stder) {
-
-		vfprintf(stderr, p_format, p_list);
-		fflush(stderr);
-	} else {
-
-		vprintf(p_format, p_list);
-		fflush(stdout);
-	}
+void OS_Unix::finalize_core() {
 }
 
-void OS_Unix::print(const char *p_format, ...) {
-
-	va_list argp;
-	va_start(argp, p_format);
-	vprintf(p_format, argp);
-	va_end(argp);
-}
 void OS_Unix::alert(const String &p_alert, const String &p_title) {
 
 	fprintf(stderr, "ERROR: %s\n", p_alert.utf8().get_data());
@@ -559,4 +514,38 @@ String OS_Unix::get_executable_path() const {
 #endif
 }
 
+void UnixTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
+	if (!should_log(true)) {
+		return;
+	}
+
+	const char *err_details;
+	if (p_rationale && p_rationale[0])
+		err_details = p_rationale;
+	else
+		err_details = p_code;
+
+	switch (p_type) {
+		case ERR_WARNING:
+			logf_error("\E[1;33mWARNING: %s: \E[0m\E[1m%s\n", p_function, err_details);
+			logf_error("\E[0;33m   At: %s:%i.\E[0m\n", p_file, p_line);
+			break;
+		case ERR_SCRIPT:
+			logf_error("\E[1;35mSCRIPT ERROR: %s: \E[0m\E[1m%s\n", p_function, err_details);
+			logf_error("\E[0;35m   At: %s:%i.\E[0m\n", p_file, p_line);
+			break;
+		case ERR_SHADER:
+			logf_error("\E[1;36mSHADER ERROR: %s: \E[0m\E[1m%s\n", p_function, err_details);
+			logf_error("\E[0;36m   At: %s:%i.\E[0m\n", p_file, p_line);
+			break;
+		case ERR_ERROR:
+		default:
+			logf_error("\E[1;31mERROR: %s: \E[0m\E[1m%s\n", p_function, err_details);
+			logf_error("\E[0;31m   At: %s:%i.\E[0m\n", p_file, p_line);
+			break;
+	}
+}
+
+UnixTerminalLogger::~UnixTerminalLogger() {}
+
 #endif

+ 7 - 5
drivers/unix/os_unix.h

@@ -54,11 +54,11 @@ protected:
 	virtual int get_audio_driver_count() const;
 	virtual const char *get_audio_driver_name(int p_driver) const;
 
+	virtual void initialize_logger();
 	virtual void initialize_core();
 	virtual int unix_initialize_audio(int p_audio_driver);
 	//virtual void initialize(int p_video_driver,int p_audio_driver);
 
-	//virtual void finalize();
 	virtual void finalize_core();
 
 	String stdin_buf;
@@ -66,10 +66,6 @@ protected:
 	String get_global_settings_path() const;
 
 public:
-	virtual void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR);
-
-	virtual void print(const char *p_format, ...);
-	virtual void vprint(const char *p_format, va_list p_list, bool p_stder = false);
 	virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
 	virtual String get_stdin_string(bool p_block);
 
@@ -120,6 +116,12 @@ public:
 	//virtual void run( MainLoop * p_main_loop );
 };
 
+class UnixTerminalLogger : public StdLogger {
+public:
+	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR);
+	virtual ~UnixTerminalLogger();
+};
+
 #endif
 
 #endif

+ 71 - 0
drivers/unix/syslog_logger.cpp

@@ -0,0 +1,71 @@
+/*************************************************************************/
+/*  syslog_logger.cpp                                                    */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifdef UNIX_ENABLED
+
+#include "syslog_logger.h"
+#include "print_string.h"
+#include <syslog.h>
+
+void SyslogLogger::logv(const char *p_format, va_list p_list, bool p_err) {
+	if (!should_log(p_err)) {
+		return;
+	}
+
+	vsyslog(p_err ? LOG_ERR : LOG_INFO, p_format, p_list);
+}
+
+void SyslogLogger::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
+	if (!should_log(true)) {
+		return;
+	}
+
+	const char *err_type = "**ERROR**";
+	switch (p_type) {
+		case ERR_ERROR: err_type = "**ERROR**"; break;
+		case ERR_WARNING: err_type = "**WARNING**"; break;
+		case ERR_SCRIPT: err_type = "**SCRIPT ERROR**"; break;
+		case ERR_SHADER: err_type = "**SHADER ERROR**"; break;
+		default: ERR_PRINT("Unknown error type"); break;
+	}
+
+	const char *err_details;
+	if (p_rationale && *p_rationale)
+		err_details = p_rationale;
+	else
+		err_details = p_code;
+
+	syslog(p_type == ERR_WARNING ? LOG_WARNING : LOG_ERR, "%s: %s\n   At: %s:%i:%s() - %s", err_type, err_details, p_file, p_line, p_function, p_code);
+}
+
+SyslogLogger::~SyslogLogger() {
+}
+
+#endif

+ 48 - 0
drivers/unix/syslog_logger.h

@@ -0,0 +1,48 @@
+/*************************************************************************/
+/*  syslog_logger.h                                                      */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef SYSLOG_LOGGER_H
+#define SYSLOG_LOGGER_H
+
+#ifdef UNIX_ENABLED
+
+#include "io/logger.h"
+
+class SyslogLogger : public Logger {
+public:
+	virtual void logv(const char *p_format, va_list p_list, bool p_err);
+	virtual void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type);
+
+	virtual ~SyslogLogger();
+};
+
+#endif
+
+#endif

+ 1 - 1
drivers/windows/dir_access_windows.cpp

@@ -162,10 +162,10 @@ Error DirAccessWindows::make_dir(String p_dir) {
 
 	GLOBAL_LOCK_FUNCTION
 
+	p_dir = fix_path(p_dir);
 	if (p_dir.is_rel_path())
 		p_dir = get_current_dir().plus_file(p_dir);
 
-	p_dir = fix_path(p_dir);
 	p_dir = p_dir.replace("/", "\\");
 
 	bool success;

+ 6 - 0
drivers/windows/file_access_windows.cpp

@@ -207,6 +207,12 @@ Error FileAccessWindows::get_error() const {
 	return last_error;
 }
 
+void FileAccessWindows::flush() {
+
+	ERR_FAIL_COND(!f);
+	fflush(f);
+}
+
 void FileAccessWindows::store_8(uint8_t p_dest) {
 
 	ERR_FAIL_COND(!f);

+ 1 - 0
drivers/windows/file_access_windows.h

@@ -64,6 +64,7 @@ public:
 
 	virtual Error get_error() const; ///< get last error
 
+	virtual void flush();
 	virtual void store_8(uint8_t p_dest); ///< store a byte
 
 	virtual bool file_exists(const String &p_name); ///< return true if a file exists

+ 2 - 1
main/main.cpp

@@ -231,7 +231,6 @@ void Main::print_help(const char *p_binary) {
 }
 
 Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_phase) {
-
 	RID_OwnerBase::init_rid();
 
 	OS::get_singleton()->initialize_core();
@@ -254,6 +253,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 
 	register_core_settings(); //here globals is present
 
+	OS::get_singleton()->initialize_logger();
+
 	translation_server = memnew(TranslationServer);
 	performance = memnew(Performance);
 	globals->add_singleton(ProjectSettings::Singleton("Performance", performance));

+ 5 - 0
platform/android/file_access_android.cpp

@@ -146,6 +146,11 @@ Error FileAccessAndroid::get_error() const {
 	return eof ? ERR_FILE_EOF : OK; //not sure what else it may happen
 }
 
+void FileAccessAndroid::flush() {
+
+	ERR_FAIL();
+}
+
 void FileAccessAndroid::store_8(uint8_t p_dest) {
 
 	ERR_FAIL();

+ 1 - 0
platform/android/file_access_android.h

@@ -63,6 +63,7 @@ public:
 
 	virtual Error get_error() const; ///< get last error
 
+	virtual void flush();
 	virtual void store_8(uint8_t p_dest); ///< store a byte
 
 	virtual bool file_exists(const String &p_path); ///< return true if a file exists

+ 3 - 0
platform/android/file_access_jandroid.cpp

@@ -157,6 +157,9 @@ Error FileAccessJAndroid::get_error() const {
 	return OK;
 }
 
+void FileAccessJAndroid::flush() {
+}
+
 void FileAccessJAndroid::store_8(uint8_t p_dest) {
 }
 

+ 1 - 0
platform/android/file_access_jandroid.h

@@ -67,6 +67,7 @@ public:
 
 	virtual Error get_error() const; ///< get last error
 
+	virtual void flush();
 	virtual void store_8(uint8_t p_dest); ///< store a byte
 
 	virtual bool file_exists(const String &p_path); ///< return true if a file exists

+ 18 - 14
platform/android/os_android.cpp

@@ -47,6 +47,15 @@
 #include "file_access_jandroid.h"
 #endif
 
+class AndroidLogger : public Logger {
+public:
+	virtual void logv(const char *p_format, va_list p_list, bool p_err) {
+		__android_log_vprint(p_err ? ANDROID_LOG_ERROR : ANDROID_LOG_INFO, "godot", p_format, p_list);
+	}
+
+	virtual ~AndroidLogger() {}
+};
+
 int OS_Android::get_video_driver_count() const {
 
 	return 1;
@@ -111,6 +120,13 @@ void OS_Android::initialize_core() {
 #endif
 }
 
+void OS_Android::initialize_logger() {
+	Vector<Logger *> loggers;
+	loggers.push_back(memnew(AndroidLogger));
+	loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt")));
+	_set_logger(memnew(CompositeLogger(loggers)));
+}
+
 void OS_Android::set_opengl_extensions(const char *p_gl_extensions) {
 
 	ERR_FAIL_COND(!p_gl_extensions);
@@ -162,23 +178,9 @@ void OS_Android::delete_main_loop() {
 }
 
 void OS_Android::finalize() {
-
 	memdelete(input);
 }
 
-void OS_Android::vprint(const char *p_format, va_list p_list, bool p_stderr) {
-
-	__android_log_vprint(p_stderr ? ANDROID_LOG_ERROR : ANDROID_LOG_INFO, "godot", p_format, p_list);
-}
-
-void OS_Android::print(const char *p_format, ...) {
-
-	va_list argp;
-	va_start(argp, p_format);
-	__android_log_vprint(ANDROID_LOG_INFO, "godot", p_format, argp);
-	va_end(argp);
-}
-
 void OS_Android::alert(const String &p_alert, const String &p_title) {
 
 	//print("ALERT: %s\n", p_alert.utf8().get_data());
@@ -737,6 +739,8 @@ OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURI
 	set_keep_screen_on_func = p_set_keep_screen_on_func;
 	alert_func = p_alert_func;
 	use_reload_hooks = false;
+
+	_set_logger(memnew(AndroidLogger));
 }
 
 OS_Android::~OS_Android() {

+ 1 - 2
platform/android/os_android.h

@@ -149,6 +149,7 @@ public:
 	virtual int get_audio_driver_count() const;
 	virtual const char *get_audio_driver_name(int p_driver) const;
 
+	virtual void initialize_logger();
 	virtual void initialize_core();
 	virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
 
@@ -161,8 +162,6 @@ public:
 
 	static OS *get_singleton();
 
-	virtual void vprint(const char *p_format, va_list p_list, bool p_stderr = false);
-	virtual void print(const char *p_format, ...);
 	virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
 
 	virtual void set_mouse_show(bool p_show);

+ 10 - 0
platform/iphone/os_iphone.cpp

@@ -41,6 +41,7 @@
 #include "core/os/dir_access.h"
 #include "core/os/file_access.h"
 #include "core/project_settings.h"
+#include "drivers/unix/syslog_logger.h"
 
 #include "sem_iphone.h"
 
@@ -98,6 +99,13 @@ void OSIPhone::initialize_core() {
 	SemaphoreIphone::make_default();
 };
 
+void OSIPhone::initialize_logger() {
+	Vector<Logger *> loggers;
+	loggers.push_back(memnew(SyslogLogger));
+	loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt")));
+	_set_logger(memnew(CompositeLogger(loggers)));
+}
+
 void OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
 
 	supported_orientations = 0;
@@ -568,6 +576,8 @@ OSIPhone::OSIPhone(int width, int height) {
 	vm.resizable = false;
 	set_video_mode(vm);
 	event_count = 0;
+
+	_set_logger(memnew(SyslogLogger));
 };
 
 OSIPhone::~OSIPhone() {

+ 1 - 0
platform/iphone/os_iphone.h

@@ -90,6 +90,7 @@ private:
 
 	virtual VideoMode get_default_video_mode() const;
 
+	virtual void initialize_logger();
 	virtual void initialize_core();
 	virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
 

+ 4 - 0
platform/javascript/os_javascript.cpp

@@ -85,6 +85,10 @@ void OS_JavaScript::initialize_core() {
 	FileAccess::make_default<FileAccessBufferedFA<FileAccessUnix> >(FileAccess::ACCESS_RESOURCES);
 }
 
+void OS_JavaScript::initialize_logger() {
+	_set_logger(memnew(StdLogger));
+}
+
 void OS_JavaScript::set_opengl_extensions(const char *p_gl_extensions) {
 
 	ERR_FAIL_COND(!p_gl_extensions);

+ 1 - 5
platform/javascript/os_javascript.h

@@ -92,6 +92,7 @@ public:
 	virtual int get_audio_driver_count() const;
 	virtual const char *get_audio_driver_name(int p_driver) const;
 
+	virtual void initialize_logger();
 	virtual void initialize_core();
 	virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
 
@@ -104,11 +105,6 @@ public:
 
 	//static OS* get_singleton();
 
-	virtual void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
-
-		OS::print_error(p_function, p_file, p_line, p_code, p_rationale, p_type);
-	}
-
 	virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
 
 	virtual void set_mouse_mode(MouseMode p_mode);

+ 1 - 2
platform/osx/os_osx.h

@@ -127,6 +127,7 @@ protected:
 	virtual const char *get_video_driver_name(int p_driver) const;
 	virtual VideoMode get_default_video_mode() const;
 
+	virtual void initialize_logger();
 	virtual void initialize_core();
 	virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
 	virtual void finalize();
@@ -141,8 +142,6 @@ public:
 
 	virtual String get_name();
 
-	virtual void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR);
-
 	virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
 
 	virtual void set_cursor_shape(CursorShape p_shape);

+ 58 - 32
platform/osx/os_osx.mm

@@ -1145,43 +1145,67 @@ String OS_OSX::get_name() {
 	return "OSX";
 }
 
-void OS_OSX::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
-
 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
-	if (!_print_error_enabled)
-		return;
-
-	const char *err_details;
-	if (p_rationale && p_rationale[0])
-		err_details = p_rationale;
-	else
-		err_details = p_code;
+class OSXTerminalLogger : public StdLogger {
+public:
+	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR) {
+		if (!should_log(true)) {
+			return;
+		}
 
-	switch (p_type) {
-		case ERR_ERROR:
-			os_log_error(OS_LOG_DEFAULT, "ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", p_function, err_details, p_file, p_line);
-			print("\E[1;31mERROR: %s: \E[0m\E[1m%s\n", p_function, err_details);
-			print("\E[0;31m   At: %s:%i.\E[0m\n", p_file, p_line);
-			break;
-		case ERR_WARNING:
-			os_log_info(OS_LOG_DEFAULT, "WARNING: %{public}s: %{public}s\nAt: %{public}s:%i.", p_function, err_details, p_file, p_line);
-			print("\E[1;33mWARNING: %s: \E[0m\E[1m%s\n", p_function, err_details);
-			print("\E[0;33m   At: %s:%i.\E[0m\n", p_file, p_line);
-			break;
-		case ERR_SCRIPT:
-			os_log_error(OS_LOG_DEFAULT, "SCRIPT ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", p_function, err_details, p_file, p_line);
-			print("\E[1;35mSCRIPT ERROR: %s: \E[0m\E[1m%s\n", p_function, err_details);
-			print("\E[0;35m   At: %s:%i.\E[0m\n", p_file, p_line);
-			break;
-		case ERR_SHADER:
-			os_log_error(OS_LOG_DEFAULT, "SHADER ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.", p_function, err_details, p_file, p_line);
-			print("\E[1;36mSHADER ERROR: %s: \E[0m\E[1m%s\n", p_function, err_details);
-			print("\E[0;36m   At: %s:%i.\E[0m\n", p_file, p_line);
-			break;
+		const char *err_details;
+		if (p_rationale && p_rationale[0])
+			err_details = p_rationale;
+		else
+			err_details = p_code;
+
+		switch (p_type) {
+			case ERR_WARNING:
+				os_log_info(OS_LOG_DEFAULT,
+						"WARNING: %{public}s: %{public}s\nAt: %{public}s:%i.",
+						p_function, err_details, p_file, p_line);
+				logf_error("\E[1;33mWARNING: %s: \E[0m\E[1m%s\n", p_function,
+						err_details);
+				logf_error("\E[0;33m   At: %s:%i.\E[0m\n", p_file, p_line);
+				break;
+			case ERR_SCRIPT:
+				os_log_error(OS_LOG_DEFAULT,
+						"SCRIPT ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.",
+						p_function, err_details, p_file, p_line);
+				logf_error("\E[1;35mSCRIPT ERROR: %s: \E[0m\E[1m%s\n", p_function,
+						err_details);
+				logf_error("\E[0;35m   At: %s:%i.\E[0m\n", p_file, p_line);
+				break;
+			case ERR_SHADER:
+				os_log_error(OS_LOG_DEFAULT,
+						"SHADER ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.",
+						p_function, err_details, p_file, p_line);
+				logf_error("\E[1;36mSHADER ERROR: %s: \E[0m\E[1m%s\n", p_function,
+						err_details);
+				logf_error("\E[0;36m   At: %s:%i.\E[0m\n", p_file, p_line);
+				break;
+			case ERR_ERROR:
+			default:
+				os_log_error(OS_LOG_DEFAULT,
+						"ERROR: %{public}s: %{public}s\nAt: %{public}s:%i.",
+						p_function, err_details, p_file, p_line);
+				logf_error("\E[1;31mERROR: %s: \E[0m\E[1m%s\n", p_function, err_details);
+				logf_error("\E[0;31m   At: %s:%i.\E[0m\n", p_file, p_line);
+				break;
+		}
 	}
+};
+
 #else
-	OS_Unix::print_error(p_function, p_file, p_line, p_code, p_rationale, p_type);
+
+typedef UnixTerminalLogger OSXTerminalLogger;
 #endif
+
+void OS_OSX::initialize_logger() {
+	Vector<Logger *> loggers;
+	loggers.push_back(memnew(OSXTerminalLogger));
+	loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt")));
+	_set_logger(memnew(CompositeLogger(loggers)));
 }
 
 void OS_OSX::alert(const String &p_alert, const String &p_title) {
@@ -2016,6 +2040,8 @@ OS_OSX::OS_OSX() {
 	window_size = Vector2(1024, 600);
 	zoomed = false;
 	display_scale = 1.0;
+
+	_set_logger(memnew(OSXTerminalLogger));
 }
 
 bool OS_OSX::_check_internal_feature_support(const String &p_feature) {

+ 10 - 50
platform/uwp/os_uwp.cpp

@@ -40,6 +40,7 @@
 #include "platform/windows/packet_peer_udp_winsock.h"
 #include "platform/windows/stream_peer_winsock.h"
 #include "platform/windows/tcp_server_winsock.h"
+#include "platform/windows/windows_terminal_logger.h"
 #include "project_settings.h"
 #include "servers/audio_server.h"
 #include "servers/visual/visual_server_raster.h"
@@ -182,6 +183,13 @@ void OSUWP::initialize_core() {
 	cursor_shape = CURSOR_ARROW;
 }
 
+void OSUWP::initialize_logger() {
+	Vector<Logger *> loggers;
+	loggers.push_back(memnew(WindowsTerminalLogger));
+	loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt")));
+	_set_logger(memnew(CompositeLogger(loggers)));
+}
+
 bool OSUWP::can_draw() const {
 
 	return !minimized;
@@ -371,32 +379,6 @@ void OSUWP::finalize() {
 void OSUWP::finalize_core() {
 }
 
-void OSUWP::vprint(const char *p_format, va_list p_list, bool p_stderr) {
-
-	char buf[16384 + 1];
-	int len = vsnprintf(buf, 16384, p_format, p_list);
-	if (len <= 0)
-		return;
-	buf[len] = 0;
-
-	int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, len, NULL, 0);
-	if (wlen < 0)
-		return;
-
-	wchar_t *wbuf = (wchar_t *)malloc((len + 1) * sizeof(wchar_t));
-	MultiByteToWideChar(CP_UTF8, 0, buf, len, wbuf, wlen);
-	wbuf[wlen] = 0;
-
-	if (p_stderr)
-		fwprintf(stderr, L"%s", wbuf);
-	else
-		wprintf(L"%s", wbuf);
-
-	free(wbuf);
-
-	fflush(stdout);
-};
-
 void OSUWP::alert(const String &p_alert, const String &p_title) {
 
 	Platform::String ^ alert = ref new Platform::String(p_alert.c_str());
@@ -520,30 +502,6 @@ OS::VideoMode OSUWP::get_video_mode(int p_screen) const {
 void OSUWP::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
 }
 
-void OSUWP::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
-
-	const char *err_details;
-	if (p_rationale && p_rationale[0])
-		err_details = p_rationale;
-	else
-		err_details = p_code;
-
-	switch (p_type) {
-		case ERR_ERROR:
-			print("ERROR: %s: %s\n", p_function, err_details);
-			print("   At: %s:%i\n", p_file, p_line);
-			break;
-		case ERR_WARNING:
-			print("WARNING: %s: %s\n", p_function, err_details);
-			print("     At: %s:%i\n", p_file, p_line);
-			break;
-		case ERR_SCRIPT:
-			print("SCRIPT ERROR: %s: %s\n", p_function, err_details);
-			print("          At: %s:%i\n", p_file, p_line);
-			break;
-	}
-}
-
 String OSUWP::get_name() {
 
 	return "UWP";
@@ -890,6 +848,8 @@ OSUWP::OSUWP() {
 	mouse_mode_changed = CreateEvent(NULL, TRUE, FALSE, L"os_mouse_mode_changed");
 
 	AudioDriverManager::add_driver(&audio_driver);
+
+	_set_logger(memnew(WindowsTerminalLogger));
 }
 
 OSUWP::~OSUWP() {

+ 1 - 3
platform/uwp/os_uwp.h

@@ -163,6 +163,7 @@ protected:
 	virtual int get_audio_driver_count() const;
 	virtual const char *get_audio_driver_name(int p_driver) const;
 
+	virtual void initialize_logger();
 	virtual void initialize_core();
 	virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
 
@@ -180,9 +181,6 @@ public:
 	// Event to send to the app wrapper
 	HANDLE mouse_mode_changed;
 
-	void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type);
-
-	virtual void vprint(const char *p_format, va_list p_list, bool p_stderr = false);
 	virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
 	String get_stdin_string(bool p_block);
 

+ 1 - 0
platform/windows/SCsub

@@ -19,6 +19,7 @@ common_win = [
     "stream_peer_winsock.cpp",
     "joypad.cpp",
     "power_windows.cpp",
+    "windows_terminal_logger.cpp"
 ]
 
 restarget = "godot_res" + env["OBJSUFFIX"]

+ 10 - 133
platform/windows/os_windows.cpp

@@ -48,6 +48,7 @@
 #include "servers/visual/visual_server_wrap_mt.h"
 #include "stream_peer_winsock.h"
 #include "tcp_server_winsock.h"
+#include "windows_terminal_logger.h"
 
 #include <process.h>
 #include <regstr.h>
@@ -205,6 +206,13 @@ void OS_Windows::initialize_core() {
 	cursor_shape = CURSOR_ARROW;
 }
 
+void OS_Windows::initialize_logger() {
+	Vector<Logger *> loggers;
+	loggers.push_back(memnew(WindowsTerminalLogger));
+	loggers.push_back(memnew(RotatedFileLogger("user://logs/log.txt")));
+	_set_logger(memnew(CompositeLogger(loggers)));
+}
+
 bool OS_Windows::can_draw() const {
 
 	return !minimized;
@@ -1231,38 +1239,6 @@ void OS_Windows::finalize_core() {
 	StreamPeerWinsock::cleanup();
 }
 
-void OS_Windows::vprint(const char *p_format, va_list p_list, bool p_stderr) {
-
-	const unsigned int BUFFER_SIZE = 16384;
-	char buf[BUFFER_SIZE + 1]; // +1 for the terminating character
-	int len = vsnprintf(buf, BUFFER_SIZE, p_format, p_list);
-	if (len <= 0)
-		return;
-	if (len >= BUFFER_SIZE)
-		len = BUFFER_SIZE; // Output is too big, will be truncated
-	buf[len] = 0;
-
-	int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, len, NULL, 0);
-	if (wlen < 0)
-		return;
-
-	wchar_t *wbuf = (wchar_t *)malloc((len + 1) * sizeof(wchar_t));
-	MultiByteToWideChar(CP_UTF8, 0, buf, len, wbuf, wlen);
-	wbuf[wlen] = 0;
-
-	if (p_stderr)
-		fwprintf(stderr, L"%ls", wbuf);
-	else
-		wprintf(L"%ls", wbuf);
-
-#ifdef STDOUT_FILE
-//vwfprintf(stdo,p_format,p_list);
-#endif
-	free(wbuf);
-
-	fflush(stdout);
-};
-
 void OS_Windows::alert(const String &p_alert, const String &p_title) {
 
 	if (!is_no_window_mode_enabled())
@@ -1676,107 +1652,6 @@ void OS_Windows::request_attention() {
 	FlashWindowEx(&info);
 }
 
-void OS_Windows::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
-
-	HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE);
-	if (!hCon || hCon == INVALID_HANDLE_VALUE) {
-
-		const char *err_details;
-		if (p_rationale && p_rationale[0])
-			err_details = p_rationale;
-		else
-			err_details = p_code;
-
-		switch (p_type) {
-			case ERR_ERROR:
-				print("ERROR: %s: %s\n", p_function, err_details);
-				print("   At: %s:%i\n", p_file, p_line);
-				break;
-			case ERR_WARNING:
-				print("WARNING: %s: %s\n", p_function, err_details);
-				print("     At: %s:%i\n", p_file, p_line);
-				break;
-			case ERR_SCRIPT:
-				print("SCRIPT ERROR: %s: %s\n", p_function, err_details);
-				print("          At: %s:%i\n", p_file, p_line);
-				break;
-			case ERR_SHADER:
-				print("SHADER ERROR: %s: %s\n", p_function, err_details);
-				print("          At: %s:%i\n", p_file, p_line);
-				break;
-		}
-
-	} else {
-
-		CONSOLE_SCREEN_BUFFER_INFO sbi; //original
-		GetConsoleScreenBufferInfo(hCon, &sbi);
-
-		WORD current_fg = sbi.wAttributes & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
-		WORD current_bg = sbi.wAttributes & (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY);
-
-		uint32_t basecol = 0;
-		switch (p_type) {
-			case ERR_ERROR: basecol = FOREGROUND_RED; break;
-			case ERR_WARNING: basecol = FOREGROUND_RED | FOREGROUND_GREEN; break;
-			case ERR_SCRIPT: basecol = FOREGROUND_RED | FOREGROUND_BLUE; break;
-			case ERR_SHADER: basecol = FOREGROUND_GREEN | FOREGROUND_BLUE; break;
-		}
-
-		basecol |= current_bg;
-
-		if (p_rationale && p_rationale[0]) {
-
-			SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY);
-			switch (p_type) {
-				case ERR_ERROR: print("ERROR: "); break;
-				case ERR_WARNING: print("WARNING: "); break;
-				case ERR_SCRIPT: print("SCRIPT ERROR: "); break;
-				case ERR_SHADER: print("SHADER ERROR: "); break;
-			}
-
-			SetConsoleTextAttribute(hCon, current_fg | current_bg | FOREGROUND_INTENSITY);
-			print("%s\n", p_rationale);
-
-			SetConsoleTextAttribute(hCon, basecol);
-			switch (p_type) {
-				case ERR_ERROR: print("   At: "); break;
-				case ERR_WARNING: print("     At: "); break;
-				case ERR_SCRIPT: print("          At: "); break;
-				case ERR_SHADER: print("          At: "); break;
-			}
-
-			SetConsoleTextAttribute(hCon, current_fg | current_bg);
-			print("%s:%i\n", p_file, p_line);
-
-		} else {
-
-			SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY);
-			switch (p_type) {
-				case ERR_ERROR: print("ERROR: %s: ", p_function); break;
-				case ERR_WARNING: print("WARNING: %s: ", p_function); break;
-				case ERR_SCRIPT: print("SCRIPT ERROR: %s: ", p_function); break;
-				case ERR_SHADER: print("SCRIPT ERROR: %s: ", p_function); break;
-			}
-
-			SetConsoleTextAttribute(hCon, current_fg | current_bg | FOREGROUND_INTENSITY);
-			print("%s\n", p_code);
-
-			SetConsoleTextAttribute(hCon, basecol);
-			switch (p_type) {
-				case ERR_ERROR: print("   At: "); break;
-				case ERR_WARNING: print("     At: "); break;
-				case ERR_SCRIPT: print("          At: "); break;
-				case ERR_SHADER: print("          At: "); break;
-			}
-
-			SetConsoleTextAttribute(hCon, current_fg | current_bg);
-			print("%s:%i\n", p_file, p_line);
-		}
-
-		SetConsoleTextAttribute(hCon, sbi.wAttributes);
-	}
-}
-
 String OS_Windows::get_name() {
 
 	return "Windows";
@@ -2429,6 +2304,8 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) {
 #ifdef XAUDIO2_ENABLED
 	AudioDriverManager::add_driver(&driver_xaudio2);
 #endif
+
+	_set_logger(memnew(WindowsTerminalLogger));
 }
 
 OS_Windows::~OS_Windows() {

+ 1 - 3
platform/windows/os_windows.h

@@ -152,6 +152,7 @@ protected:
 	virtual int get_audio_driver_count() const;
 	virtual const char *get_audio_driver_name(int p_driver) const;
 
+	virtual void initialize_logger();
 	virtual void initialize_core();
 	virtual void initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
 
@@ -180,9 +181,6 @@ protected:
 public:
 	LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
 
-	void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type);
-
-	virtual void vprint(const char *p_format, va_list p_list, bool p_stderr = false);
 	virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
 	String get_stdin_string(bool p_block);
 

+ 157 - 0
platform/windows/windows_terminal_logger.cpp

@@ -0,0 +1,157 @@
+/*************************************************************************/
+/*  windows_terminal_logger.cpp                                          */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "windows_terminal_logger.h"
+
+#ifdef WINDOWS_ENABLED
+
+#include <stdio.h>
+#include <windows.h>
+
+void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_err) {
+	if (!should_log(p_err)) {
+		return;
+	}
+
+	const unsigned int BUFFER_SIZE = 16384;
+	char buf[BUFFER_SIZE + 1]; // +1 for the terminating character
+	int len = vsnprintf(buf, BUFFER_SIZE, p_format, p_list);
+	if (len <= 0)
+		return;
+	if (len >= BUFFER_SIZE)
+		len = BUFFER_SIZE; // Output is too big, will be truncated
+	buf[len] = 0;
+
+	int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, len, NULL, 0);
+	if (wlen < 0)
+		return;
+
+	wchar_t *wbuf = (wchar_t *)malloc((len + 1) * sizeof(wchar_t));
+	MultiByteToWideChar(CP_UTF8, 0, buf, len, wbuf, wlen);
+	wbuf[wlen] = 0;
+
+	if (p_err)
+		fwprintf(stderr, L"%ls", wbuf);
+	else
+		wprintf(L"%ls", wbuf);
+
+	free(wbuf);
+
+#ifdef DEBUG_ENABLED
+	fflush(stdout);
+#endif
+}
+
+void WindowsTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
+	if (!should_log(true)) {
+		return;
+	}
+
+#ifndef UWP_ENABLED
+	HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE);
+	if (!hCon || hCon == INVALID_HANDLE_VALUE) {
+#endif
+		StdLogger::log_error(p_function, p_file, p_line, p_code, p_rationale, p_type);
+#ifndef UWP_ENABLED
+	} else {
+
+		CONSOLE_SCREEN_BUFFER_INFO sbi; //original
+		GetConsoleScreenBufferInfo(hCon, &sbi);
+
+		WORD current_fg = sbi.wAttributes & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
+		WORD current_bg = sbi.wAttributes & (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY);
+
+		uint32_t basecol = 0;
+		switch (p_type) {
+			case ERR_ERROR: basecol = FOREGROUND_RED; break;
+			case ERR_WARNING: basecol = FOREGROUND_RED | FOREGROUND_GREEN; break;
+			case ERR_SCRIPT: basecol = FOREGROUND_RED | FOREGROUND_BLUE; break;
+			case ERR_SHADER: basecol = FOREGROUND_GREEN | FOREGROUND_BLUE; break;
+		}
+
+		basecol |= current_bg;
+
+		if (p_rationale && p_rationale[0]) {
+
+			SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY);
+			switch (p_type) {
+				case ERR_ERROR: logf("ERROR: "); break;
+				case ERR_WARNING: logf("WARNING: "); break;
+				case ERR_SCRIPT: logf("SCRIPT ERROR: "); break;
+				case ERR_SHADER: logf("SHADER ERROR: "); break;
+			}
+
+			SetConsoleTextAttribute(hCon, current_fg | current_bg | FOREGROUND_INTENSITY);
+			logf("%s\n", p_rationale);
+
+			SetConsoleTextAttribute(hCon, basecol);
+			switch (p_type) {
+				case ERR_ERROR: logf("   At: "); break;
+				case ERR_WARNING: logf("     At: "); break;
+				case ERR_SCRIPT: logf("          At: "); break;
+				case ERR_SHADER: logf("          At: "); break;
+			}
+
+			SetConsoleTextAttribute(hCon, current_fg | current_bg);
+			logf("%s:%i\n", p_file, p_line);
+
+		} else {
+
+			SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY);
+			switch (p_type) {
+				case ERR_ERROR: logf("ERROR: %s: ", p_function); break;
+				case ERR_WARNING: logf("WARNING: %s: ", p_function); break;
+				case ERR_SCRIPT: logf("SCRIPT ERROR: %s: ", p_function); break;
+				case ERR_SHADER: logf("SCRIPT ERROR: %s: ", p_function); break;
+			}
+
+			SetConsoleTextAttribute(hCon, current_fg | current_bg | FOREGROUND_INTENSITY);
+			logf("%s\n", p_code);
+
+			SetConsoleTextAttribute(hCon, basecol);
+			switch (p_type) {
+				case ERR_ERROR: logf("   At: "); break;
+				case ERR_WARNING: logf("     At: "); break;
+				case ERR_SCRIPT: logf("          At: "); break;
+				case ERR_SHADER: logf("          At: "); break;
+			}
+
+			SetConsoleTextAttribute(hCon, current_fg | current_bg);
+			logf("%s:%i\n", p_file, p_line);
+		}
+
+		SetConsoleTextAttribute(hCon, sbi.wAttributes);
+	}
+#endif
+}
+
+WindowsTerminalLogger::~WindowsTerminalLogger() {}
+
+#endif

+ 47 - 0
platform/windows/windows_terminal_logger.h

@@ -0,0 +1,47 @@
+/*************************************************************************/
+/*  windows_terminal_logger.h                                            */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef WINDOWS_TERMINAL_LOGGER_H
+#define WINDOWS_TERMINAL_LOGGER_H
+
+#ifdef WINDOWS_ENABLED
+
+#include "io/logger.h"
+
+class WindowsTerminalLogger : public StdLogger {
+public:
+	virtual void logv(const char *p_format, va_list p_list, bool p_err);
+	virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR);
+	virtual ~WindowsTerminalLogger();
+};
+
+#endif
+
+#endif