Compare commits
5 Commits
8afaa3cce3
...
ea1f25d26b
Author | SHA1 | Date | |
---|---|---|---|
ea1f25d26b | |||
6a11db9a2e | |||
bf2847cc09 | |||
9db13726b5 | |||
2f5b662a37 |
@ -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
|
||||
...
|
@ -1,5 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <ctime>
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <cstdarg>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 it’s 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!");
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user