Easys
A minimalist, header-only C++ ECS library for efficient and fuss-free entity and component management.
Loading...
Searching...
No Matches
log.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <chrono>
4#include <cstdint>
5#include <fstream>
6#include <iomanip>
7#include <iostream>
8#include <mutex>
9#include <source_location>
10#include <sstream>
11#include <string_view>
12
13#include "config.hpp"
14
15namespace Easys::log {
16
17enum class LogLevel : int { EASYS_NONE = 0, EASYS_ERROR = 1, EASYS_INFO = 2, EASYS_DEBUG = 3, EASYS_TRACE = 4 };
18
19// Compile-time log level check
20template <LogLevel L>
21struct is_enabled {
22 static constexpr bool value = (EASYS_LOG_ENABLED == 1) && (static_cast<int>(L) <= EASYS_LOG_LEVEL);
23};
24
25// Thread-safe file writer with lazy initialization
27 static inline std::ofstream file_stream;
28 static inline std::mutex file_mutex;
29
30 public:
31 static void write(std::string_view message)
32 {
33 if constexpr (EASYS_LOG_TO_FILE == 1)
34 {
35 std::lock_guard<std::mutex> lock(file_mutex); // Specify template argument
36 if (!file_stream.is_open())
37 {
38 file_stream.open(EASYS_LOG_FILE_PATH, std::ios::app);
39 }
40 if (file_stream)
41 {
42 file_stream << message << std::endl;
43 file_stream.flush();
44 }
45 }
46 }
47};
48
49// Get current timestamp
50inline std::string get_timestamp()
51{
52 auto now = std::chrono::system_clock::now();
53 auto time = std::chrono::system_clock::to_time_t(now);
54 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
55
56 std::ostringstream oss;
57
58 // Safer localtime usage
59 std::tm tm;
60#ifdef _WIN32
61 localtime_s(&tm, &time);
62#else
63 localtime_r(&time, &tm);
64#endif
65
66 oss << std::put_time(&tm, "%H:%M:%S") << '.' << std::setfill('0') << std::setw(3) << ms.count();
67 return oss.str();
68}
69
70// LogLevel names
71constexpr std::string_view level_to_string(LogLevel level)
72{
73 switch (level)
74 {
76 return "ERROR";
78 return "INFO";
80 return "DEBUG";
82 return "TRACE";
83 default:
84 return "NONE";
85 }
86}
87
89 std::string_view file_name_;
90 std::string_view file_path_;
91 std::string_view function_name_;
92 std::string_view function_signature_;
93 std::uint32_t line_;
94 // std::uint32_t column_;
95
96 constexpr source_location(std::string_view file, std::uint32_t line, std::string_view funcSig,
97 std::string_view funcName) noexcept
98 {
99 // not ideal, but works good enough for now
100 const auto& start = std::min(file.find_last_of("/"), file.find_last_of("\\")) + 1;
101 const auto& substr = file.substr(start, file.size() - start);
102
103 file_path_ = file;
104 file_name_ = substr;
105 function_signature_ = funcSig;
106 function_name_ = funcName;
107 line_ = line;
108 }
109
110 constexpr explicit source_location(std::string_view funcName,
111 std::source_location l = std::source_location::current()) noexcept
112 {
113 source_location(l.file_name(), l.line(), l.function_name(), funcName);
114 }
115
116// very hacky
117#if EASYS_LOG_VERBOSITY
118 constexpr std::string_view file_name() const noexcept { return file_path_; }
119 constexpr std::string_view function_name() const noexcept { return function_signature_; }
120#else
121 // constexpr std::string_view file_path() const noexcept { return file_path_; }
122 // constexpr std::string_view function_signature() const noexcept { return function_signature_; }
123 constexpr std::string_view file_name() const noexcept { return file_name_; }
124 constexpr std::string_view function_name() const noexcept { return function_name_; }
125#endif
126
127 constexpr std::uint32_t line() const noexcept { return line_; }
128 // constexpr std::uint32_t column() const noexcept { return column; }
129};
130
131// Format log message
132inline void log_impl(LogLevel level, std::string_view message, Easys::log::source_location location)
133{
134 std::ostringstream oss;
135 oss << "[" << get_timestamp() << "] "
136 << "[" << level_to_string(level) << "] "
137 << "[" << location.file_name() << ":" << location.line() << "] "
138 << "`" << location.function_name() << "`: " << message;
139
140 auto final_message = oss.str();
141 // auto final_message = oss.view(); better?
142
143 if (level == LogLevel::EASYS_ERROR)
144 {
145 std::cerr << final_message << std::endl;
146 } else
147 {
148 std::cout << final_message << std::endl;
149 }
150
151 FileWriter::write(final_message);
152}
153
155 public:
156 EntryExitLogger(Easys::log::source_location location) : location_(location)
157 {
158 log_impl(LogLevel::EASYS_TRACE, "Entry", location_);
159 }
160
162
163 private:
165};
166
167#define EASYS_HERE Easys::log::source_location(__FILE__, __LINE__, __FUNCTION__, __func__)
168
169// Base logging macro with compile-time filtering
170#define EASYS_LOG_IMPL(level, msg) \
171 do \
172 { \
173 if constexpr (Easys::log::is_enabled<level>::value) \
174 { \
175 ::Easys::log::log_impl(level, msg, EASYS_HERE); \
176 } \
177 } while (0)
178
179// User-friendly logging macros
180#define EASYS_LOG_ERROR(msg) EASYS_LOG_IMPL(Easys::log::LogLevel::EASYS_ERROR, msg)
181#define EASYS_LOG_INFO(msg) EASYS_LOG_IMPL(Easys::log::LogLevel::EASYS_INFO, msg)
182#define EASYS_LOG_DEBUG(msg) EASYS_LOG_IMPL(Easys::log::LogLevel::EASYS_DEBUG, msg)
183#define EASYS_LOG_TRACE(msg) EASYS_LOG_IMPL(Easys::log::LogLevel::EASYS_TRACE, msg)
184#define EASYS_LOG_ENTRY_EXIT \
185 do \
186 { \
187 if constexpr (Easys::log::is_enabled<Easys::log::LogLevel::EASYS_TRACE>::value) \
188 { \
189 ::Easys::log::EntryExitLogger eel(EASYS_HERE); \
190 } \
191 } while (0)
192
193#define EASYS_E_STR(e) std::format("Entity: {}", e)
194#define EASYS_EC_STR(e) std::format("Entity: {}, Component: {}", e, typeid(T).name())
195
196} // namespace Easys::log
Definition log.hpp:154
~EntryExitLogger()
Definition log.hpp:161
EntryExitLogger(Easys::log::source_location location)
Definition log.hpp:156
Definition log.hpp:26
static void write(std::string_view message)
Definition log.hpp:31
#define EASYS_LOG_FILE_PATH
Defines the file path used for file-based logging.
Definition config.hpp:97
#define EASYS_LOG_ENABLED
Master switch for all logging functionality.
Definition config.hpp:36
#define EASYS_LOG_TO_FILE
Enables or disables logging to a file.
Definition config.hpp:85
#define EASYS_LOG_LEVEL
Sets the global logging level.
Definition config.hpp:72
Definition log.hpp:15
constexpr std::string_view level_to_string(LogLevel level)
Definition log.hpp:71
LogLevel
Definition log.hpp:17
std::string get_timestamp()
Definition log.hpp:50
void log_impl(LogLevel level, std::string_view message, Easys::log::source_location location)
Definition log.hpp:132
Definition log.hpp:21
static constexpr bool value
Definition log.hpp:22
Definition log.hpp:88
constexpr std::string_view file_name() const noexcept
Definition log.hpp:123
std::string_view file_name_
Definition log.hpp:89
std::uint32_t line_
Definition log.hpp:93
constexpr source_location(std::string_view funcName, std::source_location l=std::source_location::current()) noexcept
Definition log.hpp:110
std::string_view function_name_
Definition log.hpp:91
constexpr std::string_view function_name() const noexcept
Definition log.hpp:124
std::string_view function_signature_
Definition log.hpp:92
std::string_view file_path_
Definition log.hpp:90
constexpr source_location(std::string_view file, std::uint32_t line, std::string_view funcSig, std::string_view funcName) noexcept
Definition log.hpp:96
constexpr std::uint32_t line() const noexcept
Definition log.hpp:127