diff --git a/include/Nazara/Core/AbstractLogger.docx b/include/Nazara/Core/AbstractLogger.docx new file mode 100644 index 000000000..cdc37f3f8 --- /dev/null +++ b/include/Nazara/Core/AbstractLogger.docx @@ -0,0 +1,9 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +/*! +* \class Nz::AbstractLogger +* \brief Logger interface +*/ + diff --git a/include/Nazara/Core/AbstractLogger.hpp b/include/Nazara/Core/AbstractLogger.hpp new file mode 100644 index 000000000..d4cd772f5 --- /dev/null +++ b/include/Nazara/Core/AbstractLogger.hpp @@ -0,0 +1,31 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_ABSTRACTLOGGER_HPP +#define NAZARA_ABSTRACTLOGGER_HPP + +#include +#include +#include + +namespace Nz +{ + class NAZARA_CORE_API AbstractLogger + { + public: + AbstractLogger() = default; + virtual ~AbstractLogger(); + + virtual void EnableStdReplication(bool enable) = 0; + + virtual bool IsStdReplicationEnabled() = 0; + + virtual void Write(const String& string) = 0; + virtual void WriteError(ErrorType type, const String& error, unsigned int line = 0, const char* file = nullptr, const char* function = nullptr); + }; +} + +#endif // NAZARA_ABSTRACTLOGGER_HPP diff --git a/include/Nazara/Core/FileLogger.hpp b/include/Nazara/Core/FileLogger.hpp new file mode 100644 index 000000000..d0e4fe2aa --- /dev/null +++ b/include/Nazara/Core/FileLogger.hpp @@ -0,0 +1,46 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_FILELOGGER_HPP +#define NAZARA_FILELOGGER_HPP + +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_CORE_API FileLogger : public AbstractLogger + { + public: + FileLogger(const String& logPath = "NazaraLog.log"); + FileLogger(const FileLogger&) = default; + FileLogger(FileLogger&&) = default; + ~FileLogger(); + + void EnableTimeLogging(bool enable); + void EnableStdReplication(bool enable) override; + + bool IsStdReplicationEnabled() override; + bool IsTimeLoggingEnabled(); + + void Write(const String& string) override; + void WriteError(ErrorType type, const String& error, unsigned int line = 0, const char* file = nullptr, const char* function = nullptr) override; + + FileLogger& operator=(const FileLogger&) = default; + FileLogger& operator=(FileLogger&&) = default; + + private: + File m_outputFile; + StdLogger m_stdLogger; + bool m_forceStdOutput; + bool m_stdReplicationEnabled; + bool m_timeLoggingEnabled; + }; +} + +#endif // NAZARA_FILELOGGER_HPP diff --git a/include/Nazara/Core/Log.hpp b/include/Nazara/Core/Log.hpp index 27ae6577d..fa8e9b6d0 100644 --- a/include/Nazara/Core/Log.hpp +++ b/include/Nazara/Core/Log.hpp @@ -9,7 +9,9 @@ #include #include +#include #include +#include #if NAZARA_CORE_THREADSAFE && NAZARA_THREADSAFETY_LOG #include @@ -23,48 +25,37 @@ #define NazaraDebug(txt) #endif -#define NazaraLog Nz::Log::Instance() -#define NazaraNotice(txt) NazaraLog->Write(txt) +#define NazaraNotice(txt) Nz::Log::Write(txt) namespace Nz { - class File; + class AbstractLogger; class NAZARA_CORE_API Log { + friend class Core; + public: - void Enable(bool enable); - void EnableAppend(bool enable); - void EnableDateTime(bool enable); + static void Enable(bool enable); - String GetFile() const; + static AbstractLogger* GetLogger(); - bool IsEnabled() const; + static bool IsEnabled(); - void SetFile(const String& filePath); + static void SetLogger(AbstractLogger* logger); - void Write(const String& string); - void WriteError(ErrorType type, const String& error); - void WriteError(ErrorType type, const String& error, unsigned int line, const String& file, const String& func); + static void Write(const String& string); + static void WriteError(ErrorType type, const String& error, unsigned int line = 0, const char* file = nullptr, const char* function = nullptr); - static Log* Instance(); + NazaraStaticSignal(OnLogWrite, const String& /*string*/); + NazaraStaticSignal(OnLogWriteError, ErrorType /*type*/, const String& /*error*/, unsigned int /*line*/, const char* /*file*/, const char* /*function*/); private: - Log(); - Log(const Log&) = delete; - Log(Log&&) = delete; - ~Log(); + static bool Initialize(); + static void Uninitialize(); - Log& operator=(const Log&) = delete; - Log& operator=(Log&&) = delete; - - NazaraMutexAttrib(m_mutex, mutable) - - String m_filePath; - File* m_file; - bool m_append; - bool m_enabled; - bool m_writeTime; + static AbstractLogger* s_logger; + static bool s_enabled; }; } diff --git a/include/Nazara/Core/Signal.hpp b/include/Nazara/Core/Signal.hpp index bd6d3ab9e..613502f80 100644 --- a/include/Nazara/Core/Signal.hpp +++ b/include/Nazara/Core/Signal.hpp @@ -11,8 +11,12 @@ #include #include -#define NazaraSignal(SignalName, ...) using SignalName ## Type = Nz::Signal<__VA_ARGS__>; \ - mutable SignalName ## Type SignalName +#define NazaraDetailSignal(Keyword, SignalName, ...) using SignalName ## Type = Nz::Signal<__VA_ARGS__>; \ + Keyword SignalName ## Type SignalName + +#define NazaraSignal(SignalName, ...) NazaraDetailSignal(mutable, SignalName, __VA_ARGS__) +#define NazaraStaticSignal(SignalName, ...) NazaraDetailSignal(static, SignalName, __VA_ARGS__) +#define NazaraStaticSignalImpl(Class, SignalName) Class :: SignalName ## Type Class :: SignalName #define NazaraSlotType(Class, SignalName) Class::SignalName ## Type::ConnectionGuard #define NazaraSlot(Class, SignalName, SlotName) NazaraSlotType(Class, SignalName) SlotName diff --git a/include/Nazara/Core/StdLogger.hpp b/include/Nazara/Core/StdLogger.hpp new file mode 100644 index 000000000..430b5a7af --- /dev/null +++ b/include/Nazara/Core/StdLogger.hpp @@ -0,0 +1,35 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_STDLOGGER_HPP +#define NAZARA_STDLOGGER_HPP + +#include +#include + +namespace Nz +{ + class NAZARA_CORE_API StdLogger : public AbstractLogger + { + public: + StdLogger() = default; + StdLogger(const StdLogger&) = default; + StdLogger(StdLogger&&) = default; + ~StdLogger(); + + void EnableStdReplication(bool enable) override; + + bool IsStdReplicationEnabled() override; + + void Write(const String& string) override; + void WriteError(ErrorType type, const String& error, unsigned int line = 0, const char* file = nullptr, const char* function = nullptr) override; + + StdLogger& operator=(const StdLogger&) = default; + StdLogger& operator=(StdLogger&&) = default; + }; +} + +#endif // NAZARA_STDLOGGER_HPP diff --git a/src/Nazara/Core/AbstractLogger.cpp b/src/Nazara/Core/AbstractLogger.cpp new file mode 100644 index 000000000..1397d3592 --- /dev/null +++ b/src/Nazara/Core/AbstractLogger.cpp @@ -0,0 +1,33 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + namespace + { + const char* errorType[] = { + "Assert failed: ", // ErrorType_AssertFailed + "Internal error: ", // ErrorType_Internal + "Error: ", // ErrorType_Normal + "Warning: " // ErrorType_Warning + }; + + static_assert(sizeof(errorType) / sizeof(const char*) == ErrorType_Max + 1, "Error type array is incomplete"); + } + + AbstractLogger::~AbstractLogger() = default; + + void AbstractLogger::WriteError(ErrorType type, const String& error, unsigned int line, const char* file, const char* function) + { + StringStream stream; + stream << errorType[type] << error; + + if (line != 0 && file && function) + stream << " (" << file << ':' << line << ": " << function << ')'; + } +} diff --git a/src/Nazara/Core/Core.cpp b/src/Nazara/Core/Core.cpp index a189a2b94..142ffa909 100644 --- a/src/Nazara/Core/Core.cpp +++ b/src/Nazara/Core/Core.cpp @@ -23,6 +23,8 @@ namespace Nz s_moduleReferenceCounter++; + Log::Initialize(); + NazaraNotice("Initialized: Core"); return true; } @@ -47,6 +49,7 @@ namespace Nz s_moduleReferenceCounter = 0; HardwareInfo::Uninitialize(); + Log::Uninitialize(); PluginManager::Uninitialize(); TaskScheduler::Uninitialize(); diff --git a/src/Nazara/Core/Error.cpp b/src/Nazara/Core/Error.cpp index 07b8521b6..805e60190 100644 --- a/src/Nazara/Core/Error.cpp +++ b/src/Nazara/Core/Error.cpp @@ -85,7 +85,7 @@ namespace Nz void Error::Trigger(ErrorType type, const String& error) { if (type == ErrorType_AssertFailed || (s_flags & ErrorFlag_Silent) == 0 || (s_flags & ErrorFlag_SilentDisabled) != 0) - NazaraLog->WriteError(type, error); + Log::WriteError(type, error); s_lastError = error; s_lastErrorFile = ""; @@ -105,7 +105,7 @@ namespace Nz void Error::Trigger(ErrorType type, const String& error, unsigned int line, const char* file, const char* function) { if (type == ErrorType_AssertFailed || (s_flags & ErrorFlag_Silent) == 0 || (s_flags & ErrorFlag_SilentDisabled) != 0) - NazaraLog->WriteError(type, error, line, file, function); + Log::WriteError(type, error, line, file, function); s_lastError = error; s_lastErrorFile = file; diff --git a/src/Nazara/Core/FileLogger.cpp b/src/Nazara/Core/FileLogger.cpp new file mode 100644 index 000000000..d8f23a1b1 --- /dev/null +++ b/src/Nazara/Core/FileLogger.cpp @@ -0,0 +1,101 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + FileLogger::FileLogger(const String& logPath) : + m_outputFile(logPath), + m_forceStdOutput(false), + m_stdReplicationEnabled(true), + m_timeLoggingEnabled(true) + { + } + + FileLogger::~FileLogger() = default; + + void FileLogger::EnableTimeLogging(bool enable) + { + m_timeLoggingEnabled = enable; + } + + void FileLogger::EnableStdReplication(bool enable) + { + m_stdReplicationEnabled = enable; + } + + bool FileLogger::IsStdReplicationEnabled() + { + return m_stdReplicationEnabled; + } + + bool FileLogger::IsTimeLoggingEnabled() + { + return m_timeLoggingEnabled; + } + + void FileLogger::Write(const String& string) + { + if (m_forceStdOutput || m_stdReplicationEnabled) + { + m_stdLogger.Write(string); + + if (m_forceStdOutput) + return; + } + + // To prevent infinite loops + m_forceStdOutput = true; + CallOnExit resetOnExit([this] () + { + m_forceStdOutput = false; + }); + + if (!m_outputFile.IsOpen()) + { + if (!m_outputFile.Open(OpenMode_Text | OpenMode_Truncate | OpenMode_WriteOnly)) + { + NazaraError("Failed to open output file"); + return; + } + } + + // Apply some processing before writing + StringStream stream; + if (m_timeLoggingEnabled) + { + std::array buffer; + + time_t currentTime = std::time(nullptr); + std::strftime(buffer.data(), 24, "%d/%m/%Y - %H:%M:%S: ", std::localtime(¤tTime)); + + stream << buffer.data(); + } + + stream << string << '\n'; + + m_outputFile.Write(stream); + } + + void FileLogger::WriteError(ErrorType type, const String& error, unsigned int line, const char* file, const char* function) + { + if (m_forceStdOutput || m_stdReplicationEnabled) + { + m_stdLogger.WriteError(type, error, line, file, function); + + if (m_forceStdOutput) + return; + } + + AbstractLogger::WriteError(type, error, line, file, function); + m_outputFile.Flush(); + } +} diff --git a/src/Nazara/Core/Log.cpp b/src/Nazara/Core/Log.cpp index af9f9ef14..e554b7631 100644 --- a/src/Nazara/Core/Log.cpp +++ b/src/Nazara/Core/Log.cpp @@ -3,163 +3,73 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include -#include -#include -#include -#include -#include - -#if NAZARA_CORE_DUPLICATE_LOG_TO_COUT - #include -#endif - -#if NAZARA_CORE_THREADSAFE && NAZARA_THREADSAFETY_LOG - #include -#else - #include -#endif - +#include +#include +#include #include namespace Nz { namespace { - const char* errorType[] = { - "Assert failed: ", // ErrorType_AssertFailed - "Internal error: ", // ErrorType_Internal - "Error: ", // ErrorType_Normal - "Warning: " // ErrorType_Warning - }; - - static_assert(sizeof(errorType)/sizeof(const char*) == ErrorType_Max+1, "Error type array is incomplete"); - } - - Log::Log() : - m_filePath("NazaraLog.log"), - m_file(nullptr), - m_append(false), - m_enabled(true), - m_writeTime(true) - { - } - - Log::~Log() - { - delete m_file; + StdLogger s_stdLogger; } void Log::Enable(bool enable) { - NazaraLock(m_mutex) - - if (m_enabled == enable) - return; - - m_enabled = enable; - if (!m_enabled && m_file) - { - delete m_file; - m_file = nullptr; - } + s_enabled = enable; } - void Log::EnableAppend(bool enable) + AbstractLogger* Log::GetLogger() { - NazaraLock(m_mutex) - - m_append = enable; - if (!m_append && m_file) - { - m_file->Delete(); - m_file = nullptr; - } + return s_logger; } - void Log::EnableDateTime(bool enable) + bool Log::IsEnabled() { - NazaraLock(m_mutex) - - m_writeTime = enable; + return s_enabled; } - String Log::GetFile() const + void Log::SetLogger(AbstractLogger* logger) { - NazaraLock(m_mutex) + if (s_logger != &s_stdLogger) + delete s_logger; - if (m_file) - return m_file->GetPath(); - else - return String(); - } - - bool Log::IsEnabled() const - { - NazaraLock(m_mutex) - - return m_enabled; - } - - void Log::SetFile(const String& filePath) - { - NazaraLock(m_mutex) - - m_filePath = filePath; - if (m_file) - m_file->SetFile(filePath); + s_logger = logger; + if (!s_logger) + s_logger = &s_stdLogger; } void Log::Write(const String& string) { - NazaraLock(m_mutex) - - if (m_enabled) - { - if (!m_file) - m_file = new File(m_filePath, OpenMode_Text | OpenMode_WriteOnly | ((m_append) ? OpenMode_Append : OpenMode_Truncate)); - - String line; - - if (m_writeTime) - { - line.Reserve(23 + string.GetSize() + 1); - line.Set(23, '\0'); // Buffer non-initialisé - - time_t currentTime = std::time(nullptr); - std::strftime(&line[0], 24, "%d/%m/%Y - %H:%M:%S: ", std::localtime(¤tTime)); - } - else - line.Reserve(string.GetSize() + 1); - - line += string; - line += '\n'; - - if (m_file->IsOpen()) - m_file->Write(line); - - #if NAZARA_CORE_DUPLICATE_LOG_TO_COUT - std::fputs(line.GetConstBuffer(), stdout); - #endif - } + if (s_enabled) + s_logger->Write(string); + + OnLogWrite(string); } - void Log::WriteError(ErrorType type, const String& error) + void Log::WriteError(ErrorType type, const String& error, unsigned int line, const char* file, const char* function) { - StringStream stream; - stream << errorType[type] << error; - Write(stream); + if (s_enabled) + s_logger->WriteError(type, error, line, file, function); + + OnLogWriteError(type, error, line, file, function); } - void Log::WriteError(ErrorType type, const String& error, unsigned int line, const String& file, const String& func) + bool Log::Initialize() { - StringStream stream; - stream << errorType[type] << error << " (" << file << ':' << line << ": " << func << ')'; - Write(stream); + SetLogger(new FileLogger()); + return true; } - Log* Log::Instance() + void Log::Uninitialize() { - static Log log; - return &log; + SetLogger(nullptr); } + + NazaraStaticSignalImpl(Log, OnLogWrite); + NazaraStaticSignalImpl(Log, OnLogWriteError); + + AbstractLogger* Log::s_logger = &s_stdLogger; + bool Log::s_enabled = true; } diff --git a/src/Nazara/Core/StdLogger.cpp b/src/Nazara/Core/StdLogger.cpp new file mode 100644 index 000000000..49752db26 --- /dev/null +++ b/src/Nazara/Core/StdLogger.cpp @@ -0,0 +1,86 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ + namespace + { + const char* errorType[] = { + "Assert failed", // ErrorType_AssertFailed + "Internal error", // ErrorType_Internal + "Error", // ErrorType_Normal + "Warning" // ErrorType_Warning + }; + + static_assert(sizeof(errorType) / sizeof(const char*) == ErrorType_Max + 1, "Error type array is incomplete"); + } + + /*! + * \class Nz::StdLogger + * \brief Logger writing to standard output (stdout, stderr) + */ + + StdLogger::~StdLogger() = default; + + /*! + * \brief Enable replication to standard output + * + * Does nothing, as the std logger always write to standard output + */ + + void StdLogger::EnableStdReplication(bool enable) + { + NazaraUnused(enable); + // We always replicate to std, that's our purpose + } + + /*! + * \brief Get the standard output replication status + * + * Always returns true + */ + + bool StdLogger::IsStdReplicationEnabled() + { + return true; + } + + /*! + * Write to the console + * \param string The log to write to the console + * + * \see WriteError + */ + + void StdLogger::Write(const String& string) + { + fputs(string.GetConstBuffer(), stdout); + fputc('\n', stdout); + } + + /*! + * Write an error to the console + * \param type The error type + * \param error The error text + * \param line The line the error occurred + * \param file The file the error occurred + * \param function The function the error occurred + * + * \see Write + */ + + void StdLogger::WriteError(ErrorType type, const String& error, unsigned int line, const char* file, const char* function) + { + fprintf(stderr, "%s: %s", errorType[type], error.GetConstBuffer()); + + if (line != 0 && file && function) + fprintf(stderr, " (in %s at %s:%d)", function, file, line); + + fputc('\n', stderr); + } +}