Logger
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
set(SRC_FILES
|
set(SRC_FILES
|
||||||
"imp/cpp/IACore.cpp"
|
"imp/cpp/IACore.cpp"
|
||||||
|
"imp/cpp/Logger.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(IACore STATIC ${SRC_FILES})
|
add_library(IACore STATIC ${SRC_FILES})
|
||||||
|
|||||||
61
Src/IACore/imp/cpp/Logger.cpp
Normal file
61
Src/IACore/imp/cpp/Logger.cpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||||
|
// Copyright (C) 2025 IAS (ias@iasoft.dev)
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <IACore/Logger.hpp>
|
||||||
|
#include <IACore/File.hpp>
|
||||||
|
|
||||||
|
namespace IACore
|
||||||
|
{
|
||||||
|
Logger::ELogLevel Logger::s_logLevel{Logger::ELogLevel::WARN};
|
||||||
|
HRTimePoint Logger::s_startTime{};
|
||||||
|
IACore::File *Logger::s_logFile{};
|
||||||
|
|
||||||
|
VOID Logger::Initialize()
|
||||||
|
{
|
||||||
|
s_startTime = HRClock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID Logger::Terminate()
|
||||||
|
{
|
||||||
|
if (s_logFile)
|
||||||
|
{
|
||||||
|
s_logFile->Close();
|
||||||
|
delete s_logFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL Logger::EnableLoggingToDisk(IN PCCHAR filePath)
|
||||||
|
{
|
||||||
|
if (s_logFile)
|
||||||
|
return true;
|
||||||
|
s_logFile = new IACore::File(filePath, IACore::File::EOpenFlags::Write);
|
||||||
|
return s_logFile->IsOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID Logger::SetLogLevel(IN ELogLevel logLevel)
|
||||||
|
{
|
||||||
|
s_logLevel = logLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID Logger::LogInternal(IN PCCHAR prefix, IN String&& msg)
|
||||||
|
{
|
||||||
|
std::chrono::duration<double> elapsed = HRClock::now() - s_startTime;
|
||||||
|
double timestamp = elapsed.count();
|
||||||
|
const auto outLine = std::format("[{:>8.3f}]: [{}]: {}\n", timestamp, prefix, msg);
|
||||||
|
const auto outStream = s_logFile ? s_logFile->GetStreamHandle() : &std::cout;
|
||||||
|
outStream->write(outLine.data(), outLine.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -21,14 +21,17 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
namespace IACore {
|
namespace IACore
|
||||||
|
{
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
class File {
|
class File
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
// Modern mapping of flags to standard IO streams
|
// Modern mapping of flags to standard IO streams
|
||||||
enum class EOpenFlags : UINT32 {
|
enum class EOpenFlags : UINT32
|
||||||
|
{
|
||||||
Read = 1 << 0, // std::ios::in
|
Read = 1 << 0, // std::ios::in
|
||||||
Write = 1 << 1, // std::ios::out
|
Write = 1 << 1, // std::ios::out
|
||||||
Binary = 1 << 2, // std::ios::binary
|
Binary = 1 << 2, // std::ios::binary
|
||||||
@ -42,18 +45,22 @@ namespace IACore {
|
|||||||
|
|
||||||
// Reads entire file into a binary vector
|
// Reads entire file into a binary vector
|
||||||
NO_DISCARD("Handle the error")
|
NO_DISCARD("Handle the error")
|
||||||
STATIC tl::expected<Vector<UINT8>, String> ReadToVector(CONST String& path) {
|
|
||||||
|
STATIC tl::expected<Vector<UINT8>, String> ReadToVector(CONST String &path)
|
||||||
|
{
|
||||||
// 1. Check File Existence & Size
|
// 1. Check File Existence & Size
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
auto fileSize = fs::file_size(path, ec);
|
auto fileSize = fs::file_size(path, ec);
|
||||||
|
|
||||||
if (ec) {
|
if (ec)
|
||||||
|
{
|
||||||
return tl::make_unexpected(String("File not found or inaccessible: ") + path);
|
return tl::make_unexpected(String("File not found or inaccessible: ") + path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Open Stream
|
// 2. Open Stream
|
||||||
std::ifstream file(path, std::ios::binary);
|
std::ifstream file(path, std::ios::binary);
|
||||||
if (!file.is_open()) {
|
if (!file.is_open())
|
||||||
|
{
|
||||||
return tl::make_unexpected(String("Failed to open file handle: ") + path);
|
return tl::make_unexpected(String("Failed to open file handle: ") + path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,10 +76,13 @@ namespace IACore {
|
|||||||
|
|
||||||
// Reads entire file into a String
|
// Reads entire file into a String
|
||||||
NO_DISCARD("Handle the error")
|
NO_DISCARD("Handle the error")
|
||||||
STATIC tl::expected<String, String> ReadToString(CONST String& path) {
|
|
||||||
|
STATIC tl::expected<String, String> ReadToString(CONST String &path)
|
||||||
|
{
|
||||||
// Reuse the binary logic to avoid code duplication, then cast
|
// Reuse the binary logic to avoid code duplication, then cast
|
||||||
auto result = ReadToVector(path);
|
auto result = ReadToVector(path);
|
||||||
if (!result) {
|
if (!result)
|
||||||
|
{
|
||||||
return tl::make_unexpected(result.error());
|
return tl::make_unexpected(result.error());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,23 +99,28 @@ namespace IACore {
|
|||||||
|
|
||||||
// Old: ExtractFilenameFromPath<true/false>
|
// Old: ExtractFilenameFromPath<true/false>
|
||||||
// New: true -> filename(), false -> stem()
|
// New: true -> filename(), false -> stem()
|
||||||
template<BOOL includeExtension = true>
|
template<BOOL includeExtension = true> STATIC String ExtractFilename(CONST String &pathStr)
|
||||||
STATIC String ExtractFilename(CONST String& pathStr) {
|
{
|
||||||
fs::path p(pathStr);
|
fs::path p(pathStr);
|
||||||
if CONSTEXPR (includeExtension) {
|
if CONSTEXPR (includeExtension)
|
||||||
|
{
|
||||||
return p.filename().string(); // "sprite.png"
|
return p.filename().string(); // "sprite.png"
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
return p.stem().string(); // "sprite"
|
return p.stem().string(); // "sprite"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Old: ExtractDirectoryFromPath
|
// Old: ExtractDirectoryFromPath
|
||||||
STATIC String ExtractDirectory(CONST String& pathStr) {
|
STATIC String ExtractDirectory(CONST String &pathStr)
|
||||||
|
{
|
||||||
fs::path p(pathStr);
|
fs::path p(pathStr);
|
||||||
return p.parent_path().string(); // "assets/textures"
|
return p.parent_path().string(); // "assets/textures"
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC BOOL Exists(CONST String& pathStr) {
|
STATIC BOOL Exists(CONST String &pathStr)
|
||||||
|
{
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
return fs::exists(pathStr, ec);
|
return fs::exists(pathStr, ec);
|
||||||
}
|
}
|
||||||
@ -117,58 +132,84 @@ namespace IACore {
|
|||||||
|
|
||||||
File() = default;
|
File() = default;
|
||||||
|
|
||||||
// RAII Constructor
|
#ifdef __cplusplus
|
||||||
File(CONST String& path, UINT32 flags) {
|
File(CONST String &path, EOpenFlags flags)
|
||||||
|
#else
|
||||||
|
File(CONST String &path, UINT32 flags)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
UNUSED(Open(path, flags));
|
UNUSED(Open(path, flags));
|
||||||
}
|
}
|
||||||
|
|
||||||
~File() {
|
~File()
|
||||||
|
{
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns void (success) or error String
|
#ifdef __cplusplus
|
||||||
tl::expected<void, String> Open(CONST String& path, UINT32 flags) {
|
tl::expected<void, String> Open(CONST String &path, EOpenFlags flags)
|
||||||
|
#else
|
||||||
|
tl::expected<void, String> Open(CONST String &path, UINT32 flags)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
Close(); // Ensure previous handle is closed
|
Close(); // Ensure previous handle is closed
|
||||||
|
|
||||||
std::ios_base::openmode mode = static_cast<std::ios_base::openmode>(0);
|
std::ios_base::openmode mode = static_cast<std::ios_base::openmode>(0);
|
||||||
if (flags & (UINT32)EOpenFlags::Read) mode |= std::ios::in;
|
if ((UINT32) flags & (UINT32) EOpenFlags::Read)
|
||||||
if (flags & (UINT32)EOpenFlags::Write) mode |= std::ios::out;
|
mode |= std::ios::in;
|
||||||
if (flags & (UINT32)EOpenFlags::Binary) mode |= std::ios::binary;
|
if ((UINT32) flags & (UINT32) EOpenFlags::Write)
|
||||||
if (flags & (UINT32)EOpenFlags::Append) mode |= std::ios::app;
|
mode |= std::ios::out;
|
||||||
if (flags & (UINT32)EOpenFlags::Trunc) mode |= std::ios::trunc;
|
if ((UINT32) flags & (UINT32) EOpenFlags::Binary)
|
||||||
|
mode |= std::ios::binary;
|
||||||
|
if ((UINT32) flags & (UINT32) EOpenFlags::Append)
|
||||||
|
mode |= std::ios::app;
|
||||||
|
if ((UINT32) flags & (UINT32) EOpenFlags::Trunc)
|
||||||
|
mode |= std::ios::trunc;
|
||||||
|
|
||||||
m_fs.open(path, mode);
|
m_fs.open(path, mode);
|
||||||
|
|
||||||
if (!m_fs.is_open()) {
|
if (!m_fs.is_open())
|
||||||
|
{
|
||||||
return tl::make_unexpected(String("Failed to open file: ") + path);
|
return tl::make_unexpected(String("Failed to open file: ") + path);
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID Close() {
|
VOID Close()
|
||||||
if (m_fs.is_open()) {
|
{
|
||||||
|
if (m_fs.is_open())
|
||||||
|
{
|
||||||
m_fs.close();
|
m_fs.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL IsOpen() CONST {
|
BOOL IsOpen() CONST
|
||||||
|
{
|
||||||
return m_fs.is_open();
|
return m_fs.is_open();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns number of bytes read
|
// Returns number of bytes read
|
||||||
SIZE_T Read(PVOID buffer, SIZE_T size) {
|
SIZE_T Read(PVOID buffer, SIZE_T size)
|
||||||
if (!m_fs.is_open()) return 0;
|
{
|
||||||
|
if B_UNLIKELY (!m_fs.is_open())
|
||||||
|
return 0;
|
||||||
m_fs.read(REINTERPRET(buffer, char *), size);
|
m_fs.read(REINTERPRET(buffer, char *), size);
|
||||||
return static_cast<SIZE_T>(m_fs.gcount());
|
return static_cast<SIZE_T>(m_fs.gcount());
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID Write(CONST PVOID buffer, SIZE_T size) {
|
VOID Write(CONST PVOID buffer, SIZE_T size)
|
||||||
if (m_fs.is_open()) {
|
{
|
||||||
|
if B_UNLIKELY (!m_fs.is_open())
|
||||||
|
return;
|
||||||
m_fs.write(REINTERPRET(buffer, const char *), size);
|
m_fs.write(REINTERPRET(buffer, const char *), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::fstream *GetStreamHandle()
|
||||||
|
{
|
||||||
|
return &m_fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::fstream m_fs;
|
std::fstream m_fs;
|
||||||
};
|
};
|
||||||
}
|
} // namespace IACore
|
||||||
@ -22,29 +22,6 @@
|
|||||||
|
|
||||||
namespace IACore
|
namespace IACore
|
||||||
{
|
{
|
||||||
template<typename... Args>
|
|
||||||
VOID LogInfo(Args... args)
|
|
||||||
{
|
|
||||||
StringStream ss;
|
|
||||||
UNUSED((ss << ... << args));
|
|
||||||
printf( __CC_WHITE "[INFO]: %s" __CC_DEFAULT "\n", ss.str().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
VOID LogWarn(Args... args)
|
|
||||||
{
|
|
||||||
StringStream ss;
|
|
||||||
UNUSED((ss << ... << args));
|
|
||||||
printf( __CC_YELLOW "[WARN]: %s" __CC_DEFAULT "\n", ss.str().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
VOID LogError(Args... args)
|
|
||||||
{
|
|
||||||
StringStream ss;
|
|
||||||
UNUSED((ss << ... << args));
|
|
||||||
printf( __CC_RED "[ERROR]: %s" __CC_DEFAULT "\n", ss.str().c_str());
|
|
||||||
}
|
|
||||||
} // namespace IACore
|
} // namespace IACore
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
117
Src/IACore/inc/IACore/Logger.hpp
Normal file
117
Src/IACore/inc/IACore/Logger.hpp
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
// IACore-OSS; The Core Library for All IA Open Source Projects
|
||||||
|
// Copyright (C) 2025 IAS (ias@iasoft.dev)
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <IACore/PCH.hpp>
|
||||||
|
|
||||||
|
namespace IACore
|
||||||
|
{
|
||||||
|
class File;
|
||||||
|
|
||||||
|
class Logger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class ELogLevel
|
||||||
|
{
|
||||||
|
VERBOSE,
|
||||||
|
INFO,
|
||||||
|
WARN,
|
||||||
|
ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
STATIC VOID Initialize();
|
||||||
|
STATIC VOID Terminate();
|
||||||
|
|
||||||
|
STATIC BOOL EnableLoggingToDisk(IN PCCHAR filePath);
|
||||||
|
STATIC VOID SetLogLevel(IN ELogLevel logLevel);
|
||||||
|
|
||||||
|
template<typename... Args> STATIC VOID Status(FormatterString<Args...> fmt, Args &&...args)
|
||||||
|
{
|
||||||
|
LogStatus(std::vformat(fmt.get(), std::make_format_args(args...)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args> STATIC VOID Info(FormatterString<Args...> fmt, Args &&...args)
|
||||||
|
{
|
||||||
|
LogInfo(std::vformat(fmt.get(), std::make_format_args(args...)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args> STATIC VOID Warn(FormatterString<Args...> fmt, Args &&...args)
|
||||||
|
{
|
||||||
|
LogWarn(std::vformat(fmt.get(), std::make_format_args(args...)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args> STATIC VOID Error(FormatterString<Args...> fmt, Args &&...args)
|
||||||
|
{
|
||||||
|
LogError(std::vformat(fmt.get(), std::make_format_args(args...)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
#if IA_ENABLE_LOGGING > 0
|
||||||
|
STATIC VOID LogStatus(IN String &&msg)
|
||||||
|
{
|
||||||
|
if (s_logLevel <= ELogLevel::VERBOSE)
|
||||||
|
LogInternal("STATUS", IA_MOVE(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC VOID LogInfo(IN String &&msg)
|
||||||
|
{
|
||||||
|
if (s_logLevel <= ELogLevel::INFO)
|
||||||
|
LogInternal("INFO", IA_MOVE(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC VOID LogWarn(IN String &&msg)
|
||||||
|
{
|
||||||
|
if (s_logLevel <= ELogLevel::WARN)
|
||||||
|
LogInternal("WARN", IA_MOVE(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC VOID LogError(IN String &&msg)
|
||||||
|
{
|
||||||
|
if (s_logLevel <= ELogLevel::ERROR)
|
||||||
|
LogInternal("ERROR", IA_MOVE(msg));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
STATIC VOID LogStatus(IN String &&msg)
|
||||||
|
{
|
||||||
|
UNUSED(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC VOID LogInfo(IN String &&msg)
|
||||||
|
{
|
||||||
|
UNUSED(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC VOID LogWarn(IN String &&msg)
|
||||||
|
{
|
||||||
|
UNUSED(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC VOID LogError(IN String &&msg)
|
||||||
|
{
|
||||||
|
UNUSED(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
STATIC VOID LogInternal(IN PCCHAR prefix, IN String &&msg);
|
||||||
|
|
||||||
|
private:
|
||||||
|
STATIC ELogLevel s_logLevel;
|
||||||
|
STATIC HRTimePoint s_startTime;
|
||||||
|
STATIC IACore::File *s_logFile;
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -29,6 +29,8 @@
|
|||||||
# include <limits>
|
# include <limits>
|
||||||
# include <cstring>
|
# include <cstring>
|
||||||
# include <cstddef>
|
# include <cstddef>
|
||||||
|
# include <chrono>
|
||||||
|
# include <iostream>
|
||||||
# include <concepts>
|
# include <concepts>
|
||||||
# include <functional>
|
# include <functional>
|
||||||
# include <type_traits>
|
# include <type_traits>
|
||||||
@ -526,11 +528,9 @@ using String = std::string;
|
|||||||
using StringView = std::string_view;
|
using StringView = std::string_view;
|
||||||
using StringStream = std::stringstream;
|
using StringStream = std::stringstream;
|
||||||
|
|
||||||
template<typename... Args> STATIC String BuildString(Args &&...args)
|
using HRClock = std::chrono::high_resolution_clock;
|
||||||
{
|
using HRTimePoint = std::chrono::time_point<HRClock>;
|
||||||
std::stringstream ss;
|
|
||||||
(ss << ... << args);
|
template<typename... Args> using FormatterString = std::format_string<Args...>;
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user