Compare commits

...

5 Commits

4 changed files with 234 additions and 72 deletions

View File

@ -1,58 +0,0 @@
# Generated from CLion C/C++ Code Style settings
---
Language: Cpp
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignOperands: true
AlignTrailingComments: false
AlwaysBreakTemplateDeclarations: Yes
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBraces: Custom
BreakConstructorInitializers: AfterColon
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 120
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ContinuationIndentWidth: 8
IncludeCategories:
- Regex: '^<.*'
Priority: 1
- Regex: '^".*'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: true
IndentWidth: 4
InsertNewlineAtEOF: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 2
NamespaceIndentation: All
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: false
SpaceBeforeRangeBasedForLoopColon: false
SpaceInEmptyParentheses: false
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
TabWidth: 4
...

View File

@ -1,5 +1,12 @@
#pragma once
#include <iostream>
#include <vector>
#include <ctime>
#include <format>
#include <memory>
#include <string>
#include <vector>
#include <chrono>
#include <cstdarg>
#include <iomanip>
#include <sstream>
#include <iostream>

View File

@ -6,13 +6,166 @@
#include <pch.hpp>
class Logger {
public:
void logVersion() {
std::cout << "ICEngine Version: " << PROJECT_VERSION << std::endl;
}
namespace ICEngine {
void logCrash(const std::string& errorMessage) {
std::cerr << "Crash detected in version " << PROJECT_VERSION << ": " << errorMessage << std::endl;
}
};
enum LogLevel {
OFF = 0,
TRACE,
DEBUG,
WARN,
ERROR,
CRITICAL,
FATAL
};
class Logger {
private:
Logger(const char *name, std::ostream *stream) : name(name) {
if (stream) {
streams.push_back(stream);
}
}
friend class Log;
public:
void SetLevel(LogLevel level) {
this->level = level;
}
void AddStream(std::ostream * stream) {
this->streams.push_back(stream);
}
template<typename... Args>
void Log(LogLevel level, const char *file, int line, const char *func, const std::string &formatStr, Args &&... args) {
// TODO: Create a crash handler and manage fatal messages separately (creates crashdump)
// TODO: Allow user to create their own class that inherits Fatal Handler so they can implement it themselves
if (level < this->level) return;
std::string formattedMessage = format(formatStr, std::forward<Args>(args)...);
std::string logMessage = formatMessage(level, file, line, func, formattedMessage);
for (auto stream: streams) {
(*stream) << logMessage << std::endl;
}
}
private:
const char *name;
std::vector<std::ostream *> streams;
LogLevel level = DEBUG;
static const char *levelToString(LogLevel level) {
switch (level) {
case TRACE: return "TRACE";
case DEBUG: return "DEBUG";
case WARN: return "WARN";
case ERROR: return "ERROR";
case CRITICAL: return "CRITICAL";
case FATAL: return "FATAL";
default: return "UNKNOWN";
}
}
static std::string getCurrentTime() {
auto now = std::chrono::system_clock::now();
std::time_t time = std::chrono::system_clock::to_time_t(now);
std::ostringstream oss;
oss << std::put_time(std::localtime(&time), "%Y/%m/%d %H:%M:%S");
return oss.str();
}
static std::string truncateFilePath(const char *file) {
std::string path(file);
size_t pos = path.find_last_of("/\\");
if (pos != std::string::npos && pos > 3) {
return "..." + path.substr(pos - 3);
}
return path;
}
template<typename... Args>
std::string format(const std::string &formatStr, Args&&... args) {
std::ostringstream oss;
formatHelper(oss, formatStr, std::forward<Args>(args)...);
return oss.str();
}
void formatHelper(std::ostringstream &oss, const std::string &formatStr) {
oss << formatStr;
}
template<typename T, typename... Args>
void formatHelper(std::ostringstream &oss, const std::string &formatStr, T &&firstArg, Args&&... remainingArgs) {
size_t pos = formatStr.find("{}");
if (pos != std::string::npos) {
oss << formatStr.substr(0, pos) << std::forward<T>(firstArg);
formatHelper(oss, formatStr.substr(pos + 2), std::forward<Args>(remainingArgs)...);
} else {
oss << formatStr;
}
}
// Formats the log message with metadata (timestamp, file, line, etc.)
std::string formatMessage(LogLevel level, const char *file, int line, const char *func, const std::string &message) {
std::ostringstream oss;
oss << getCurrentTime() << " ";
oss << truncateFilePath(file) << ":" << line << ":" << func << " ";
oss << "[" << levelToString(level) << "] ";
oss << "[" << name << "]: " << message;
return oss.str();
}
};
class Log {
public:
static void Init() {
GetCoreLogger();
GetAppLogger();
}
static std::shared_ptr<Logger> &GetCoreLogger() {
static std::shared_ptr<Logger> CoreLogger(new Logger("CORE", &std::cout));
return CoreLogger;
}
static std::shared_ptr<Logger> &GetAppLogger() {
static std::shared_ptr<Logger> AppLogger(new Logger("APP", &std::cout));
return AppLogger;
}
private:
Log() = delete;
Log(const Log &) = delete;
Log &operator=(const Log &) = delete;
};
#ifndef NDEBUG
#define CORE_LOG_TRACE(message, ...) ICEngine::Log::GetCoreLogger()->Log(ICEngine::TRACE, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__)
#define CORE_LOG_DEBUG(message, ...) ICEngine::Log::GetCoreLogger()->Log(ICEngine::DEBUG, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__)
#define CORE_LOG_WARN(message, ...) ICEngine::Log::GetCoreLogger()->Log(ICEngine::WARN, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__)
#define CORE_LOG_ERROR(message, ...) ICEngine::Log::GetCoreLogger()->Log(ICEngine::ERROR, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__)
#define CORE_LOG_CRITICAL(message, ...) ICEngine::Log::GetCoreLogger()->Log(ICEngine::CRITICAL, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__)
#define CORE_LOG_FATAL(message, ...) ICEngine::Log::GetCoreLogger()->Log(ICEngine::FATAL, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__)
#define LOG_TRACE(message, ...) ICEngine::Log::GetAppLogger()->Log(ICEngine::TRACE, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__)
#define LOG_DEBUG(message, ...) ICEngine::Log::GetAppLogger()->Log(ICEngine::DEBUG, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__)
#define LOG_WARN(message, ...) ICEngine::Log::GetAppLogger()->Log(ICEngine::WARN, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__)
#define LOG_ERROR(message, ...) ICEngine::Log::GetAppLogger()->Log(ICEngine::ERROR, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__)
#define LOG_CRITICAL(message, ...) ICEngine::Log::GetAppLogger()->Log(ICEngine::CRITICAL, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__)
#define LOG_FATAL(message, ...) ICEngine::Log::GetAppLogger()->Log(ICEngine::FATAL, __FILE__, __LINE__, __FUNCTION__, message, ##__VA_ARGS__)
#else
#define CORE_LOG_TRACE(message, ...)
#define CORE_LOG_DEBUG(message, ...)
#define CORE_LOG_WARN(message, ...)
#define CORE_LOG_ERROR(message, ...)
#define CORE_LOG_CRITICAL(message, ...)
#define CORE_LOG_FATAL(message, ...)
#define LOG_TRACE(message, ...)
#define LOG_DEBUG(message, ...)
#define LOG_WARN(message, ...)
#define LOG_ERROR(message, ...)
#define LOG_CRITICAL(message, ...)
#define LOG_FATAL(message, ...)
#endif
}

View File

@ -3,9 +3,69 @@
//
#include <ICEngine.hpp>
#include <fstream>
void ShowLogger();
int main() {
Logger logger;
logger.logVersion();
return 0;
ShowLogger();
return 0;
}
void ShowLogger() {
// The logger needs to be initialized before you can use it.
// This will be done by the engine, NEVER the user.
ICEngine::Log::Init();
// Before using the logger macros, you may want to configure logging options such as log levels and output streams.
// By default, the logger outputs to std::cout, but you can add more streams (e.g., files) or set the log level as needed.
// Logging Levels:
// - OFF
// - TRACE
// - DEBUG
// - WARN
// - ERROR
// - CRITICAL
// - FATAL
ICEngine::Log::GetCoreLogger()->SetLevel(ICEngine::TRACE);
ICEngine::Log::GetAppLogger()->SetLevel(ICEngine::TRACE);
// You can have multiple log streams, ie console + file etc
// You do not need to have the same streams for each logger
#include <fstream>
std::ofstream coreFileStream("core_logs.txt");
std::ofstream appFileStream("app_logs.txt");
ICEngine::Log::GetCoreLogger()->AddStream(&coreFileStream);
ICEngine::Log::GetAppLogger()->AddStream(&appFileStream);
// You can log multiple different types with formatting
int age = 30;
std::string module = "Network";
// CORE logger is intended for internal engine or core-level logging
// In most cases, you won't need to use CORE logging in game/application code,
// but its available for engine-level diagnostics if necessary
CORE_LOG_TRACE("Initializing {} with age {}", module, age); // Logs a trace-level message, useful for very detailed information
CORE_LOG_DEBUG("This is an example of a debug message"); // Logs a debug message, typically used for debugging purposes
CORE_LOG_WARN("This is an example of a warn message"); // Logs a warning, indicating a non-critical issue that should be investigated
CORE_LOG_ERROR("This is an example of an error message"); // Logs an error, signaling that something has gone wrong but the engine can still run
CORE_LOG_CRITICAL("This is an example of a critical message\n"); // Logs a critical issue, often indicating a major problem in the core system that needs immediate attention.
// CORE_LOG_FATAL: Logs a fatal error that crashes the application. Fatal errors in the core usually lead to dumping information into crash logs
// and stopping the program. Use with extreme caution and only in situations where the application can't recover
// CORE_LOG_FATAL("This is an example of a fatal message");
// These macros are designed for logging from your game or application code
// They allow you to monitor application flow, errors, and warnings from the perspective of the game logic or app layer
LOG_TRACE("App took longer to respond than expected: {} seconds", 2.34); // Logs a trace message, ideal for fine-grained, verbose debugging details
LOG_DEBUG("This is an example of a debug message"); // Logs a debug message, typically used to help trace the execution during development
LOG_WARN("This is an example of a warn message"); // Logs a warning, indicating something unexpected happened, but the application can continue running
LOG_ERROR("This is an example of an error message"); // Logs an error, signaling that an issue occurred that may require attention but is not catastrophic
LOG_CRITICAL("This is an example of a critical message"); // Logs a critical message, suggesting something very wrong happened, but the program may still attempt to run
// LOG_FATAL: Logs a fatal error in the application layer. This will trigger the Fatal Handler, which you can override
// The Fatal Handler provides a mechanism for handling unrecoverable errors gracefully, allowing you to define how the application reacts to fatal crashes
// LOG_FATAL("This is an example of log fatal crash!");
}