i think logging is working. need to make macros and test

main
Brett 2025-03-09 15:06:31 -04:00
parent 07a11656fa
commit 60536fd602
6 changed files with 274 additions and 90 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
include(cmake/color.cmake) include(cmake/color.cmake)
set(BLT_VERSION 5.2.2) set(BLT_VERSION 5.2.3)
set(BLT_TARGET BLT) set(BLT_TARGET BLT)

View File

@ -31,9 +31,6 @@
namespace blt::logging namespace blt::logging
{ {
namespace detail
{}
struct logger_t struct logger_t
{ {
explicit logger_t(std::ostream& stream): m_stream(stream) explicit logger_t(std::ostream& stream): m_stream(stream)
@ -173,6 +170,8 @@ namespace blt::logging
void set_thread_name(const std::string& name); void set_thread_name(const std::string& name);
const std::string& get_thread_name();
template <typename... Args> template <typename... Args>
void print(std::string fmt, Args&&... args) void print(std::string fmt, Args&&... args)
{ {
@ -200,6 +199,22 @@ namespace blt::logging
print(stream, std::move(fmt), std::forward<Args>(args)...); print(stream, std::move(fmt), std::forward<Args>(args)...);
stream << std::endl; stream << std::endl;
} }
template<typename... Args>
void log(log_level_t level, const char* file, const i32 line, std::string fmt, Args&&... args)
{
auto& logger = get_global_logger();
auto& config = get_global_config();
auto user_str = logger.log(std::move(fmt), std::forward<Args>(args)...);
auto log_fmt_str = config.generate(user_str, get_thread_name(), level, file, line);
if (log_fmt_str)
print(std::move(*log_fmt_str));
}
namespace detail
{}
} }
#define BLT_LOG(level, fmt, ...) blt::logging::log(level, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
#endif // BLT_LOGGING_LOGGING_H #endif // BLT_LOGGING_LOGGING_H

View File

@ -20,6 +20,7 @@
#define BLT_LOGGING_LOGGING_CONFIG_H #define BLT_LOGGING_LOGGING_CONFIG_H
#include <array> #include <array>
#include <optional>
#include <string> #include <string>
#include <vector> #include <vector>
#include <blt/fs/fwddecl.h> #include <blt/fs/fwddecl.h>
@ -44,10 +45,12 @@ namespace blt::logging
inline constexpr auto SECOND = "{SECOND}"; inline constexpr auto SECOND = "{SECOND}";
// Current Millisecond // Current Millisecond
inline constexpr auto MILLISECOND = "{MS}"; inline constexpr auto MILLISECOND = "{MS}";
// Current Nanosecond time, This is direct output of nanotime // Current Nanosecond
inline constexpr auto NANOSECOND = "{NS}"; inline constexpr auto NANOSECOND = "{NS}";
// Current Unix time in milliseconds // Current Unix time in milliseconds
inline constexpr auto UNIX_TIME = "{UNIX}"; inline constexpr auto UNIX_TIME = "{UNIX}";
// Current Unix time in nanosecond
inline constexpr auto UNIX_TIME_NANO = "{UNIX_NANO}";
// Formatted ISO year-month-day in a single variable // Formatted ISO year-month-day in a single variable
inline constexpr auto ISO_YEAR = "{ISO_YEAR}"; inline constexpr auto ISO_YEAR = "{ISO_YEAR}";
// Formatted hour:minute:second in a single variable // Formatted hour:minute:second in a single variable
@ -86,6 +89,7 @@ namespace blt::logging
MS, MS,
NS, NS,
UNIX, UNIX,
UNIX_NANO,
ISO_YEAR, ISO_YEAR,
TIME, TIME,
FULL_TIME, FULL_TIME,
@ -130,83 +134,87 @@ namespace blt::logging
logging_config_t& add_log_output(fs::writer_t* writer) logging_config_t& add_log_output(fs::writer_t* writer)
{ {
log_outputs.push_back(writer); m_log_outputs.push_back(writer);
return *this; return *this;
} }
logging_config_t& set_log_format(std::string format) logging_config_t& set_log_format(std::string format)
{ {
log_format = std::move(format); m_log_format = std::move(format);
compile(); compile();
return *this; return *this;
} }
logging_config_t& set_error_color(std::string color) logging_config_t& set_error_color(std::string color)
{ {
error_color = std::move(color); m_error_color = std::move(color);
compile(); compile();
return *this; return *this;
} }
logging_config_t& set_log_level_colors(std::array<std::string, LOG_LEVEL_COUNT> colors) logging_config_t& set_log_level_colors(std::array<std::string, LOG_LEVEL_COUNT> colors)
{ {
log_level_colors = std::move(colors); m_log_level_colors = std::move(colors);
compile(); compile();
return *this; return *this;
} }
logging_config_t& set_log_level_names(std::array<std::string, LOG_LEVEL_COUNT> names) logging_config_t& set_log_level_names(std::array<std::string, LOG_LEVEL_COUNT> names)
{ {
log_level_names = std::move(names); m_log_level_names = std::move(names);
return *this; return *this;
} }
logging_config_t& set_level(const log_level_t level) logging_config_t& set_level(const log_level_t level)
{ {
this->level = level; this->m_level = level;
return *this; return *this;
} }
logging_config_t& set_use_color(const bool use_color) logging_config_t& set_use_color(const bool use_color)
{ {
this->use_color = use_color; this->m_use_color = use_color;
compile(); compile();
return *this; return *this;
} }
logging_config_t& set_print_full_name(const bool print_full_name) logging_config_t& set_print_full_name(const bool print_full_name)
{ {
this->print_full_name = print_full_name; this->m_print_full_name = print_full_name;
return *this; return *this;
} }
logging_config_t& set_ensure_alignment(const bool ensure_alignment) logging_config_t& set_ensure_alignment(const bool ensure_alignment)
{ {
this->ensure_alignment = ensure_alignment; this->m_ensure_alignment = ensure_alignment;
return *this; return *this;
} }
[[nodiscard]] std::pair<const std::vector<tags::detail::log_tag_token_t>&, const std::vector<std::string>&> get_log_tag_tokens() const [[nodiscard]] std::pair<const std::vector<tags::detail::log_tag_token_t>&, const std::vector<std::string>&> get_log_tag_tokens() const
{ {
return {log_tag_tokens, log_tag_content}; return {m_log_tag_tokens, m_log_tag_content};
} }
std::optional<std::string> generate(const std::string& user_str, const std::string& thread_name, log_level_t level, const char* file,
i32 line) const;
private: private:
std::vector<std::string> log_tag_content; std::vector<std::string> m_log_tag_content;
std::vector<tags::detail::log_tag_token_t> log_tag_tokens; std::vector<tags::detail::log_tag_token_t> m_log_tag_tokens;
// wrappers for streams exist in blt/fs/stream_wrappers.h // wrappers for streams exist in blt/fs/stream_wrappers.h
std::vector<fs::writer_t*> log_outputs = get_default_log_outputs(); std::vector<fs::writer_t*> m_log_outputs = get_default_log_outputs();
std::string log_format = get_default_log_format(); std::string m_log_format = get_default_log_format();
std::string error_color = get_default_error_color(); std::string m_error_color = get_default_error_color();
std::array<std::string, LOG_LEVEL_COUNT> log_level_colors = get_default_log_level_colors(); std::array<std::string, LOG_LEVEL_COUNT> m_log_level_colors = get_default_log_level_colors();
std::array<std::string, LOG_LEVEL_COUNT> log_level_names = get_default_log_level_names(); std::array<std::string, LOG_LEVEL_COUNT> m_log_level_names = get_default_log_level_names();
log_level_t level = log_level_t::TRACE; log_level_t m_level = log_level_t::TRACE;
bool use_color = true;
bool m_use_color = true;
// if true prints the whole path to the file (eg /home/user/.../.../project/src/source.cpp:line#) // if true prints the whole path to the file (eg /home/user/.../.../project/src/source.cpp:line#)
bool print_full_name = false; bool m_print_full_name = false;
// this will attempt to use the maximum possible size for each printed element, then align to that. // this will attempt to use the maximum possible size for each printed element, then align to that.
// This creates output where the user message always starts at the same column. // This creates output where the user message always starts at the same column.
bool ensure_alignment = true; bool m_ensure_alignment = true;
static std::string get_default_log_format(); static std::string get_default_log_format();
static std::vector<fs::writer_t*> get_default_log_outputs(); static std::vector<fs::writer_t*> get_default_log_outputs();

@ -1 +1 @@
Subproject commit 7ef2e733416953b222851f9a360d7fc72d068ee5 Subproject commit 93201da2ba5a6aba0a6e57ada64973555629b3e3

View File

@ -30,8 +30,6 @@ namespace blt::logging
struct global_context_t struct global_context_t
{ {
logging_config_t global_config; logging_config_t global_config;
std::mutex thread_name_mutex;
hashmap_t<std::thread::id, std::string> thread_names;
}; };
static global_context_t global_context; static global_context_t global_context;
@ -39,6 +37,7 @@ namespace blt::logging
struct logging_thread_context_t struct logging_thread_context_t
{ {
std::stringstream stream; std::stringstream stream;
std::string thread_name;
logger_t logger{stream}; logger_t logger{stream};
}; };
@ -233,7 +232,11 @@ namespace blt::logging
void set_thread_name(const std::string& name) void set_thread_name(const std::string& name)
{ {
std::scoped_lock lock{global_context.thread_name_mutex}; get_thread_context().thread_name = name;
global_context.thread_names[std::this_thread::get_id()] = name; }
const std::string& get_thread_name()
{
return get_thread_context().thread_name;
} }
} }

View File

@ -15,11 +15,14 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <ctime>
#include <iostream> #include <iostream>
#include <blt/fs/stream_wrappers.h> #include <blt/fs/stream_wrappers.h>
#include <blt/logging/ansi.h> #include <blt/logging/ansi.h>
#include <blt/logging/logging_config.h> #include <blt/logging/logging_config.h>
#include <blt/std/hashmap.h> #include <blt/std/hashmap.h>
#include <blt/std/system.h>
#include <blt/std/time.h>
namespace blt::logging namespace blt::logging
{ {
@ -37,6 +40,7 @@ namespace blt::logging
map[MILLISECOND] = log_tag_token_t::MS; map[MILLISECOND] = log_tag_token_t::MS;
map[NANOSECOND] = log_tag_token_t::NS; map[NANOSECOND] = log_tag_token_t::NS;
map[UNIX_TIME] = log_tag_token_t::UNIX; map[UNIX_TIME] = log_tag_token_t::UNIX;
map[UNIX_TIME_NANO] = log_tag_token_t::UNIX_NANO;
map[ISO_YEAR] = log_tag_token_t::ISO_YEAR; map[ISO_YEAR] = log_tag_token_t::ISO_YEAR;
map[TIME] = log_tag_token_t::TIME; map[TIME] = log_tag_token_t::TIME;
map[FULL_TIME] = log_tag_token_t::FULL_TIME; map[FULL_TIME] = log_tag_token_t::FULL_TIME;
@ -51,89 +55,243 @@ namespace blt::logging
map[STR] = log_tag_token_t::STR; map[STR] = log_tag_token_t::STR;
return map; return map;
} }
std::array<bool, static_cast<u8>(log_tag_token_t::CONTENT)> tag_known_values{
// year
false,
// month
false,
// day
false,
// hour
false,
// minute
false,
// second
false,
// ms
false,
// ns
false,
// unix
false,
// iso year
false,
// time
false,
// full_time
false,
// lc
true,
// ec
true,
// conditional error
false,
// reset
true,
// log level
false,
// thread_name
false,
// file
false,
// line
false,
// str
false
};
} }
void logging_config_t::compile() void logging_config_t::compile()
{ {
static hashmap_t<std::string_view, tags::detail::log_tag_token_t> tag_map = tags::detail::make_map(); static hashmap_t<std::string_view, tags::detail::log_tag_token_t> tag_map = tags::detail::make_map();
log_tag_content.clear(); m_log_tag_content.clear();
log_tag_tokens.clear(); m_log_tag_tokens.clear();
size_t i = 0; size_t i = 0;
for (; i < log_format.size(); ++i) for (; i < m_log_format.size(); ++i)
{ {
size_t start = i; size_t start = i;
while (i < log_format.size() && log_format[i] != '{') while (i < m_log_format.size() && m_log_format[i] != '{')
++i; ++i;
if (i == log_format.size() || (i < log_format.size() && (i - start) > 0)) if (i == m_log_format.size() || (i < m_log_format.size() && (i - start) > 0))
{ {
log_tag_content.emplace_back(std::string_view(log_format.data() + start, i - start)); m_log_tag_content.emplace_back(std::string_view(m_log_format.data() + start, i - start));
log_tag_tokens.emplace_back(tags::detail::log_tag_token_t::CONTENT); m_log_tag_tokens.emplace_back(tags::detail::log_tag_token_t::CONTENT);
if (i == log_format.size()) if (i == m_log_format.size())
break; break;
} }
start = i; start = i;
while (i < log_format.size() && log_format[i] != '}') while (i < m_log_format.size() && m_log_format[i] != '}')
++i; ++i;
const auto tag = std::string_view(log_format.data() + start, i - start + 1); const auto tag = std::string_view(m_log_format.data() + start, i - start + 1);
auto it = tag_map.find(tag); auto it = tag_map.find(tag);
if (it == tag_map.end()) if (it == tag_map.end())
throw std::runtime_error("Invalid log tag: " + std::string(tag)); throw std::runtime_error("Invalid log tag: " + std::string(tag));
log_tag_tokens.emplace_back(it->second); m_log_tag_tokens.emplace_back(it->second);
} }
if (i < log_format.size()) if (i < m_log_format.size())
{ {
log_tag_content.emplace_back(std::string_view(log_format.data() + i, log_format.size() - i)); m_log_tag_content.emplace_back(std::string_view(m_log_format.data() + i, m_log_format.size() - i));
log_tag_tokens.emplace_back(tags::detail::log_tag_token_t::CONTENT); m_log_tag_tokens.emplace_back(tags::detail::log_tag_token_t::CONTENT);
} }
} }
std::string add_year(const tm* current_time)
{
return std::to_string(current_time->tm_year + 1900);
}
std::string add_month(const tm* current_time, const bool ensure_alignment)
{
auto str = std::to_string(current_time->tm_mon + 1);
if (ensure_alignment && str.size() < 2)
str.insert(str.begin(), '0');
return str;
}
std::string add_day(const tm* current_time, const bool ensure_alignment)
{
auto str = std::to_string(current_time->tm_mday);
if (ensure_alignment && str.size() < 2)
str.insert(str.begin(), '0');
return str;
}
std::string add_hour(const tm* current_time, const bool ensure_alignment)
{
auto str = std::to_string(current_time->tm_hour);
if (ensure_alignment && str.size() < 2)
str.insert(str.begin(), '0');
return str;
}
std::string add_minute(const tm* current_time, const bool ensure_alignment)
{
auto str = std::to_string(current_time->tm_min);
if (ensure_alignment && str.size() < 2)
str.insert(str.begin(), '0');
return str;
}
std::string add_second(const tm* current_time, const bool ensure_alignment)
{
auto str = std::to_string(current_time->tm_sec);
if (ensure_alignment && str.size() < 2)
str.insert(str.begin(), '0');
return str;
}
std::optional<std::string> logging_config_t::generate(const std::string& user_str, const std::string& thread_name, const log_level_t level,
const char* file, const i32 line) const
{
if (level < m_level)
return {};
std::string fmt;
const std::time_t time = std::time(nullptr);
const auto current_time = std::localtime(&time);
const auto millis_time = system::getCurrentTimeMilliseconds();
const auto nano_time = system::getCurrentTimeNanoseconds();
size_t content = 0;
for (const auto& log_tag_token : m_log_tag_tokens)
{
switch (log_tag_token)
{
case tags::detail::log_tag_token_t::YEAR:
fmt += add_year(current_time);
break;
case tags::detail::log_tag_token_t::MONTH:
fmt += add_month(current_time, m_ensure_alignment);
break;
case tags::detail::log_tag_token_t::DAY:
fmt += add_day(current_time, m_ensure_alignment);
break;
case tags::detail::log_tag_token_t::HOUR:
fmt += add_hour(current_time, m_ensure_alignment);
break;
case tags::detail::log_tag_token_t::MINUTE:
fmt += add_minute(current_time, m_ensure_alignment);
break;
case tags::detail::log_tag_token_t::SECOND:
fmt += add_second(current_time, m_ensure_alignment);
break;
case tags::detail::log_tag_token_t::MS:
{
auto str = std::to_string(millis_time % 1000);
if (m_ensure_alignment)
{
for (size_t i = str.size(); i < 4; ++i)
str.insert(str.begin(), '0');
}
fmt += str;
break;
}
case tags::detail::log_tag_token_t::NS:
{
auto str = std::to_string(nano_time % 1000);
if (m_ensure_alignment)
{
for (size_t i = str.size(); i < 4; ++i)
str.insert(str.begin(), '0');
}
fmt += str;
break;
}
case tags::detail::log_tag_token_t::UNIX:
{
auto str = std::to_string(millis_time % 1000);
if (m_ensure_alignment)
{
for (size_t i = str.size(); i < 4; ++i)
str.insert(str.begin(), '0');
}
fmt += str;
break;
}
case tags::detail::log_tag_token_t::UNIX_NANO:
{
auto str = std::to_string(nano_time);
if (m_ensure_alignment)
{
for (size_t i = str.size(); i < 4; ++i)
str.insert(str.begin(), '0');
}
fmt += str;
break;
}
case tags::detail::log_tag_token_t::ISO_YEAR:
{
fmt += add_year(current_time);
fmt += '-';
fmt += add_month(current_time, m_ensure_alignment);
fmt += '-';
fmt += add_day(current_time, m_ensure_alignment);
break;
}
case tags::detail::log_tag_token_t::TIME:
fmt += add_hour(current_time, m_ensure_alignment);
fmt += ':';
fmt += add_minute(current_time, m_ensure_alignment);
fmt += ':';
fmt += add_second(current_time, m_ensure_alignment);
break;
case tags::detail::log_tag_token_t::FULL_TIME:
fmt += add_year(current_time);
fmt += '-';
fmt += add_month(current_time, m_ensure_alignment);
fmt += '-';
fmt += add_day(current_time, m_ensure_alignment);
fmt += ' ';
fmt += add_hour(current_time, m_ensure_alignment);
fmt += ':';
fmt += add_minute(current_time, m_ensure_alignment);
fmt += ':';
fmt += add_second(current_time, m_ensure_alignment);
break;
case tags::detail::log_tag_token_t::LC:
if (!m_use_color)
break;
fmt += m_log_level_colors[static_cast<u8>(level)];
break;
case tags::detail::log_tag_token_t::EC:
if (!m_use_color)
break;
fmt += m_error_color;
break;
case tags::detail::log_tag_token_t::CEC:
if (!m_use_color)
break;
if (static_cast<u8>(level) >= static_cast<u8>(log_level_t::ERROR))
fmt += m_error_color;
break;
case tags::detail::log_tag_token_t::RESET:
if (!m_use_color)
break;
fmt += build(ansi::color::color_mode::RESET_ALL);
break;
case tags::detail::log_tag_token_t::LL:
fmt += m_log_level_names[static_cast<u8>(level)];
break;
case tags::detail::log_tag_token_t::TN:
fmt += thread_name;
break;
case tags::detail::log_tag_token_t::FILE:
fmt += file;
break;
case tags::detail::log_tag_token_t::LINE:
fmt += std::to_string(line);
break;
case tags::detail::log_tag_token_t::STR:
fmt += user_str;
break;
case tags::detail::log_tag_token_t::CONTENT:
fmt += m_log_tag_content[content++];
break;
}
}
return fmt;
}
std::string logging_config_t::get_default_log_format() std::string logging_config_t::get_default_log_format()
{ {
return build(fg(ansi::color::color8_bright::CYAN)) + "[" + tags::FULL_TIME + "]" + tags::RESET + " " + tags::LOG_COLOR + "[" + tags::LOG_LEVEL return build(fg(ansi::color::color8_bright::CYAN)) + "[" + tags::FULL_TIME + "]" + tags::RESET + " " + tags::LOG_COLOR + "[" + tags::LOG_LEVEL