Added (very limited) support for ansi coloring to log formatter.

This commit is contained in:
2025-06-21 17:01:37 +02:00
parent 8a9df15dd0
commit c9c4eff130
3 changed files with 133 additions and 10 deletions

View File

@@ -13,6 +13,7 @@
#include "../memory/memutil.hpp"
#include "../memory/virtual_allocator.hpp"
#include "../util/annot.hpp"
#include "../util/ansi_colors.hpp"
#include "../util/concepts.hpp"
#include "../util/string.hpp"
@@ -31,7 +32,7 @@ public:
virtual ~BaseLogFormatter() noexcept = default;
virtual void format(const LogMessage& message, string_t& outFormatted) noexcept = 0;
virtual void format(const LogMessage& message, string_t& outFormatted) = 0;
};
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>,
@@ -44,13 +45,16 @@ public:
using allocator_t = TAllocator;
using base_t = BaseLogFormatter<char_t, traits_t, allocator_t>;
using typename base_t::string_t;
using string_view_t = std::basic_string_view<char_t, traits_t>;
private:
string_t mFormat;
string_t mFormatBuffer;
public:
explicit BaseSimpleLogFormatter(string_t format) MIJIN_NOEXCEPT : mFormat(std::move(format)), mFormatBuffer(mFormat.get_allocator()) {}
void format(const LogMessage& message, string_t& outFormatted) noexcept override;
void format(const LogMessage& message, string_t& outFormatted) override;
private:
void formatAnsiSequence(const LogMessage& message, string_view_t ansiName, string_t& outFormatted);
};
#define FORMATTER_SET_ARGS(chr_type) chr_type, std::char_traits<chr_type>, TAllocator
@@ -114,10 +118,8 @@ MIJIN_DEFINE_CHAR_VERSIONS_TMPL(FormattingLogSink, MIJIN_FORMATTING_SINK_COMMON_
#undef SINK_SET_ARGS
template<typename TChar, typename TTraits, allocator_type_for<TChar> TAllocator>
void BaseSimpleLogFormatter<TChar, TTraits, TAllocator>::format(const LogMessage& message, string_t& outFormatted) noexcept
void BaseSimpleLogFormatter<TChar, TTraits, TAllocator>::format(const LogMessage& message, string_t& outFormatted)
{
using string_view_t = std::basic_string_view<char_t, traits_t>;
mFormatBuffer.clear();
for (auto pos = mFormat.begin(); pos != mFormat.end(); ++pos)
{
@@ -252,6 +254,10 @@ void BaseSimpleLogFormatter<TChar, TTraits, TAllocator>::format(const LogMessage
{
formatInline(message.level->name);
}
else if (argName == MIJIN_SMART_QUOTE(char_t, "ansi"))
{
formatAnsiSequence(message, argFormat.substr(1), outFormatted);
}
else
{
MIJIN_ERROR("Invalid format argument name.");
@@ -263,6 +269,56 @@ void BaseSimpleLogFormatter<TChar, TTraits, TAllocator>::format(const LogMessage
}
}
}
template<typename TChar, typename TTraits, allocator_type_for<TChar> TAllocator>
void BaseSimpleLogFormatter<TChar, TTraits, TAllocator>::formatAnsiSequence(const LogMessage& message, string_view_t ansiName, string_t& outFormatted)
{
std::size_t numParts = 0;
const auto formatParts = splitFixed<4>(ansiName, ",", {}, &numParts);
outFormatted += MIJIN_SMART_QUOTE(char_t, "\033[");
for (std::size_t partIdx = 0; partIdx < numParts; ++partIdx)
{
const string_view_t& part = formatParts[partIdx];
if (partIdx > 0)
{
outFormatted += MIJIN_SMART_QUOTE(char_t, ',');
}
if (part == MIJIN_SMART_QUOTE(char_t, "reset"))
{
outFormatted += BaseAnsiFontEffects<char_t>::RESET;
}
else if (part == MIJIN_SMART_QUOTE(char_t, "level_color"))
{
const int levelValue = message.level->value;
if (levelValue < MIJIN_LOG_LEVEL_VALUE_VERBOSE)
{
outFormatted += BaseAnsiFontEffects<char_t>::FG_CYAN;
}
else if (levelValue < MIJIN_LOG_LEVEL_VALUE_INFO)
{
outFormatted += BaseAnsiFontEffects<char_t>::FG_WHITE;
}
else if (levelValue < MIJIN_LOG_LEVEL_VALUE_WARNING)
{
outFormatted += BaseAnsiFontEffects<char_t>::FG_BRIGHT_WHITE;
}
else if (levelValue < MIJIN_LOG_LEVEL_VALUE_ERROR)
{
outFormatted += BaseAnsiFontEffects<char_t>::FG_YELLOW;
}
else
{
outFormatted += BaseAnsiFontEffects<char_t>::FG_RED;
}
}
else
{
MIJIN_ERROR("Invalid format ansi font effect name.");
}
}
outFormatted += MIJIN_SMART_QUOTE(char_t, 'm');
}
} // namespace mijin
#endif // !defined(MIJIN_LOGGING_FORMATTING_HPP_INCLUDED)

View File

@@ -11,7 +11,7 @@ namespace mijin
{
template<MIJIN_FORMATTING_SINK_TMPL_ARGS_INIT>
requires(allocator_type<TAllocator<TChar>>)
class StdioSink : public BaseFormattingLogSink<MIJIN_FORMATTING_SINK_TMP_ARG_NAMES>
class BaseStdioSink : public BaseFormattingLogSink<MIJIN_FORMATTING_SINK_TMP_ARG_NAMES>
{
public:
using base_t = BaseFormattingLogSink<MIJIN_FORMATTING_SINK_TMP_ARG_NAMES>;
@@ -20,12 +20,18 @@ public:
using typename base_t::formatter_ptr_t;
using typename base_t::message_t;
private:
int mMinStderrLevel;
int mMinStderrLevel = MIJIN_LOG_LEVEL_VALUE_WARNING;
public:
explicit StdioSink(not_null_t<formatter_ptr_t> formatter, allocator_t allocator = {},
int minStderrLevel = MIJIN_LOG_LEVEL_VALUE_WARNING)
explicit BaseStdioSink(not_null_t<formatter_ptr_t> formatter, allocator_t allocator = {})
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<allocator_t>)
: base_t(std::move(formatter), std::move(allocator)), mMinStderrLevel(minStderrLevel) {}
: base_t(std::move(formatter), std::move(allocator)) {}
[[nodiscard]]
int getMinStderrLevel() const MIJIN_NOEXCEPT { return mMinStderrLevel; }
void setMinStderrLevel(int level) MIJIN_NOEXCEPT { mMinStderrLevel = level; }
void setMinStderrLevel(const BaseLogLevel<char_t>& level) MIJIN_NOEXCEPT { mMinStderrLevel = level.value; }
void handleMessageFormatted(const message_t& message, const char_t* formatted) MIJIN_NOEXCEPT override
{
@@ -52,6 +58,12 @@ public:
}
}
};
#define SINK_SET_ARGS(chr_type) chr_type, std::char_traits<chr_type>, TAllocator, TDeleter
MIJIN_DEFINE_CHAR_VERSIONS_TMPL(StdioSink, MIJIN_FORMATTING_SINK_COMMON_ARGS, SINK_SET_ARGS)
#undef SINK_SET_ARGS
} // namespace mijin