From 0e1964ba25e603ab564276292606c2e61e841e15 Mon Sep 17 00:00:00 2001
From: Patrick Wuttke
Date: Tue, 28 Oct 2025 12:01:50 +0100
Subject: [PATCH] Logging: made LogMessage contain a string view instead of
const char*, added BufferSink, changed message parameter of formatted
parameter to be string instead of const char*.
---
source/mijin/logging/buffer_sink.hpp | 376 +++++++++++++++++++++
source/mijin/logging/debug_output_sink.hpp | 9 +-
source/mijin/logging/filters.hpp | 6 +-
source/mijin/logging/formatting.hpp | 16 +-
source/mijin/logging/logger.hpp | 60 +++-
source/mijin/logging/stdio_sink.hpp | 17 +-
source/mijin/logging/stream_sink.hpp | 6 +-
7 files changed, 450 insertions(+), 40 deletions(-)
create mode 100644 source/mijin/logging/buffer_sink.hpp
diff --git a/source/mijin/logging/buffer_sink.hpp b/source/mijin/logging/buffer_sink.hpp
new file mode 100644
index 0000000..056e1c6
--- /dev/null
+++ b/source/mijin/logging/buffer_sink.hpp
@@ -0,0 +1,376 @@
+
+#pragma once
+
+#if !defined(MIJIN_LOGGING_BUFFER_SINK_HPP_INCLUDED)
+#define MIJIN_LOGGING_BUFFER_SINK_HPP_INCLUDED 1
+
+#include
+#include
+#include
+#include
+#include
+#include "./logger.hpp"
+
+namespace mijin
+{
+inline constexpr std::size_t BUFFER_SINK_DEFAULT_SIZE = 10 * 1024 * 1024; // default 10 MiB buffer
+
+template>
+class BaseBufferSink;
+
+namespace impl
+{
+static constexpr std::uint32_t INVALID_BUFFER_INDEX = std::numeric_limits::max();
+
+template
+struct BufferedMessageHeader
+{
+ const BaseLogChannel* channel;
+ const BaseLogLevel* level;
+ std::source_location sourceLocation;
+ std::uint32_t nextIdx = INVALID_BUFFER_INDEX;
+ std::uint32_t prevIdx = INVALID_BUFFER_INDEX;
+ std::uint32_t numChars = 0;
+};
+
+template
+struct MessageBuffer
+{
+ using char_t = TChar;
+ using traits_t = TTraits;
+ using message_t = BaseLogMessage;
+ using header_t = BufferedMessageHeader;
+ static constexpr std::size_t BUFFER_SIZE = VBufferSize;
+
+ std::array bytes;
+ mutable std::mutex mutex;
+
+ [[nodiscard]]
+ header_t& messageAt(std::uint32_t idx) MIJIN_NOEXCEPT
+ {
+ return *reinterpret_cast(bytes.data() + idx);
+ }
+
+ [[nodiscard]]
+ std::span messageText(header_t& header) MIJIN_NOEXCEPT
+ {
+ return {reinterpret_cast(reinterpret_cast(&header)) + sizeof(header_t), header.numChars};
+ }
+
+ [[nodiscard]]
+ const header_t& messageAt(std::uint32_t idx) const MIJIN_NOEXCEPT
+ {
+ return *reinterpret_cast(bytes.data() + idx);
+ }
+
+ [[nodiscard]]
+ std::span messageText(const header_t& header) const MIJIN_NOEXCEPT
+ {
+ return {reinterpret_cast(reinterpret_cast(&header)) + sizeof(header_t), header.numChars};
+ }
+
+ static std::uint32_t messageBytes(std::uint32_t numChars) MIJIN_NOEXCEPT
+ {
+ return static_cast(sizeof(header_t)) + (numChars * static_cast(sizeof(char_t)));
+ }
+};
+
+template
+class BufferSinkRange;
+
+template
+class LockedMessageBuffer
+{
+public:
+ using char_t = TChar;
+ using traits_t = TTraits;
+
+ static constexpr std::size_t BUFFER_SIZE = VBufferSize;
+
+ using range_t = impl::BufferSinkRange;
+private:
+ using buffer_t = MessageBuffer;
+
+ const buffer_t* buffer_ = nullptr;
+ std::uint32_t firstIdx_ = INVALID_BUFFER_INDEX;
+ std::uint32_t lastIdx_ = INVALID_BUFFER_INDEX;
+
+ LockedMessageBuffer(const buffer_t& buffer, std::uint32_t firstIdx, std::uint32_t lastIdx) MIJIN_NOEXCEPT
+ : buffer_(&buffer), firstIdx_(firstIdx), lastIdx_(lastIdx) {
+ buffer_->mutex.lock();
+ }
+public:
+ LockedMessageBuffer() noexcept = default;
+ LockedMessageBuffer(const LockedMessageBuffer&) = delete;
+ LockedMessageBuffer(LockedMessageBuffer&& other) noexcept
+ : buffer_(std::exchange(other.buffer_, nullptr)), firstIdx_(other.firstIdx_), lastIdx_(other.lastIdx_) {}
+
+ ~LockedMessageBuffer() noexcept
+ {
+ reset();
+ }
+
+ LockedMessageBuffer& operator=(const LockedMessageBuffer&) = delete;
+ LockedMessageBuffer& operator=(LockedMessageBuffer&& other) noexcept
+ {
+ if (this != &other)
+ {
+ reset();
+ buffer_ = std::exchange(other.buffer_, nullptr);
+ firstIdx_ = other.firstIdx_;
+ lastIdx_ = other.lastIdx_;
+ }
+ return *this;
+ }
+
+ [[nodiscard]]
+ range_t getMessages() const MIJIN_NOEXCEPT;
+private:
+ void reset()
+ {
+ if (buffer_ != nullptr)
+ {
+ buffer_->mutex.unlock();
+ buffer_ = nullptr;
+ }
+ }
+
+ template
+ friend class mijin::BaseBufferSink;
+};
+
+template
+class BufferSinkIterator
+{
+public:
+ using char_t = TChar;
+ using traits_t = TTraits;
+ using message_t = BaseLogMessage;
+ static constexpr std::size_t BUFFER_SIZE = VBufferSize;
+
+ using difference_type = std::ptrdiff_t;
+ using value_type = const message_t;
+ using pointer = void;
+ using reference = value_type;
+ using iterator_category = std::bidirectional_iterator_tag;
+private:
+ using buffer_t = MessageBuffer;
+ using header_t = BufferedMessageHeader;
+ using string_view_t = message_t::string_view_t;
+
+ const buffer_t* buffer_ = nullptr;
+ std::uint32_t idx_ = INVALID_BUFFER_INDEX;
+ std::uint32_t prevIdx_ = INVALID_BUFFER_INDEX;
+
+ BufferSinkIterator(const buffer_t& buffer, std::uint32_t idx, std::uint32_t prevIdx) MIJIN_NOEXCEPT : buffer_(&buffer), idx_(idx), prevIdx_(prevIdx) {}
+public:
+ BufferSinkIterator() MIJIN_NOEXCEPT = default;
+ BufferSinkIterator(const BufferSinkIterator&) MIJIN_NOEXCEPT = default;
+
+ BufferSinkIterator& operator=(const BufferSinkIterator&) MIJIN_NOEXCEPT = default;
+
+ auto operator<=>(const BufferSinkIterator&) const noexcept = default;
+
+ reference operator*() const MIJIN_NOEXCEPT
+ {
+ MIJIN_ASSERT(idx_ != INVALID_BUFFER_INDEX, "Attempting to dereference an invalid iterator.");
+ const header_t& header = buffer_->messageAt(idx_);
+ const string_view_t text(buffer_->messageText(header));
+ return {
+ .text = text,
+ .channel = header.channel,
+ .level = header.level,
+ .sourceLocation = header.sourceLocation
+ };
+ }
+
+ BufferSinkIterator& operator++() MIJIN_NOEXCEPT
+ {
+ MIJIN_ASSERT(idx_ != INVALID_BUFFER_INDEX, "Attempting to increment an invalid iterator.");
+ prevIdx_ = idx_;
+ idx_ = buffer_->messageAt(idx_).nextIdx;
+ return *this;
+ }
+
+ BufferSinkIterator operator++(int) MIJIN_NOEXCEPT
+ {
+ BufferSinkIterator copy(*this);
+ operator++();
+ return copy;
+ }
+
+ BufferSinkIterator& operator--() MIJIN_NOEXCEPT
+ {
+ MIJIN_ASSERT(prevIdx_ != INVALID_BUFFER_INDEX, "Attempting to decrement an invalid iterator.");
+ idx_ = prevIdx_;
+ prevIdx_ = buffer_->messageAt(idx_).prevIdx;
+ return *this;
+ }
+
+ BufferSinkIterator operator--(int) MIJIN_NOEXCEPT
+ {
+ BufferSinkIterator copy(*this);
+ operator--();
+ return copy;
+ }
+
+ template
+ friend class BufferSinkRange;
+};
+static_assert(std::forward_iterator, BUFFER_SINK_DEFAULT_SIZE>>);
+
+template
+class BufferSinkRange
+{
+public:
+ using char_t = TChar;
+ using traits_t = TTraits;
+ static constexpr std::size_t BUFFER_SIZE = VBufferSize;
+
+ using iterator = BufferSinkIterator;
+ using const_iterator = iterator;
+private:
+ using buffer_t = MessageBuffer;
+
+ const buffer_t* buffer_ = nullptr;
+ std::uint32_t firstIdx_ = INVALID_BUFFER_INDEX;
+ std::uint32_t lastIdx_ = INVALID_BUFFER_INDEX;
+
+ BufferSinkRange(const buffer_t& buffer, std::uint32_t firstIdx, std::uint32_t lastIdx) MIJIN_NOEXCEPT
+ : buffer_(&buffer), firstIdx_(firstIdx), lastIdx_(lastIdx) {
+ }
+public:
+ BufferSinkRange() noexcept = default;
+ BufferSinkRange(const BufferSinkRange&) MIJIN_NOEXCEPT = default;
+ BufferSinkRange& operator=(const BufferSinkRange&) MIJIN_NOEXCEPT = default;
+
+ [[nodiscard]]
+ iterator begin() const MIJIN_NOEXCEPT { return {*buffer_, firstIdx_, INVALID_BUFFER_INDEX}; }
+
+ [[nodiscard]]
+ iterator end() const MIJIN_NOEXCEPT { return {*buffer_, INVALID_BUFFER_INDEX, lastIdx_}; }
+
+ [[nodiscard]]
+ const_iterator cbegin() const MIJIN_NOEXCEPT { return {*buffer_, firstIdx_, INVALID_BUFFER_INDEX}; }
+
+ [[nodiscard]]
+ const_iterator cend() const MIJIN_NOEXCEPT { return {*buffer_, INVALID_BUFFER_INDEX, lastIdx_}; }
+
+ friend class LockedMessageBuffer;
+};
+
+static_assert(std::bidirectional_iterator, 100>>);
+static_assert(std::ranges::bidirectional_range, 100>>);
+
+template
+auto LockedMessageBuffer::getMessages() const MIJIN_NOEXCEPT -> range_t
+{
+ return range_t(*buffer_, firstIdx_, lastIdx_);
+}
+}
+
+using impl::LockedMessageBuffer;
+
+template
+class BaseBufferSink : public BaseLogSink
+{
+public:
+ using base_t = BaseLogSink;
+ using typename base_t::char_t;
+ using typename base_t::traits_t;
+ using typename base_t::message_t;
+
+ static constexpr std::size_t BUFFER_SIZE = VBufferSize;
+
+ using locked_buffer_t = LockedMessageBuffer;
+private:
+ using buffer_t = impl::MessageBuffer;
+ using header_t = impl::BufferedMessageHeader;
+
+ buffer_t buffer_;
+ std::uint32_t firstIdx_ = impl::INVALID_BUFFER_INDEX;
+ std::uint32_t lastIdx_ = impl::INVALID_BUFFER_INDEX;
+public:
+ void handleMessage(const message_t& message) MIJIN_NOEXCEPT override
+ {
+ const std::uint32_t numChars = static_cast(message.text.size());
+ const std::uint32_t totalBytes = buffer_t::messageBytes(numChars);
+
+ std::scoped_lock _(buffer_.mutex); // TODO: use a message queue and try_lock here
+ if (lastIdx_ == impl::INVALID_BUFFER_INDEX)
+ {
+ // no message yet
+ insertMessageAt(0, message);
+ firstIdx_ = 0;
+ return;
+ }
+
+ header_t& lastHeader = buffer_.messageAt(lastIdx_);
+ const std::uint32_t newIdx = lastIdx_ + buffer_t::messageBytes(lastHeader.numChars);
+ if (newIdx + totalBytes < BUFFER_SIZE)
+ {
+ // enough space in the buffer, can append
+ insertMessageAt(newIdx, message);
+ }
+ else
+ {
+ // not enough space, put at front
+ insertMessageAt(0, message);
+ }
+ }
+
+ [[nodiscard]]
+ locked_buffer_t lockBuffer() const MIJIN_NOEXCEPT { return locked_buffer_t(buffer_, firstIdx_, lastIdx_); }
+private:
+ void insertMessageAt(std::uint32_t idx, const message_t& message)
+ {
+ const std::uint32_t numChars = static_cast(message.text.size());
+ freeSpace(idx, idx + buffer_t::messageBytes(numChars));
+
+ if (lastIdx_ != impl::INVALID_BUFFER_INDEX) {
+ buffer_.messageAt(lastIdx_).nextIdx = idx;
+ }
+
+ header_t& newHeader = buffer_.messageAt(idx);
+ ::new (&newHeader) header_t({
+ .channel = message.channel,
+ .level = message.level,
+ .sourceLocation = message.sourceLocation,
+ .nextIdx = impl::INVALID_BUFFER_INDEX,
+ .prevIdx = lastIdx_,
+ .numChars = numChars
+ });
+ lastIdx_ = idx;
+ std::ranges::copy(message.text, buffer_.messageText(newHeader).begin());
+ }
+
+ void freeSpace(std::uint32_t startIdx, std::uint32_t endIdx)
+ {
+ while (firstIdx_ != impl::INVALID_BUFFER_INDEX && firstIdx_ >= startIdx && firstIdx_ < endIdx)
+ {
+ header_t& message = buffer_.messageAt(firstIdx_);
+ firstIdx_ = message.nextIdx;
+ message.~header_t();
+ }
+
+ // cleared everything?
+ if (firstIdx_ == impl::INVALID_BUFFER_INDEX) {
+ lastIdx_ = impl::INVALID_BUFFER_INDEX;
+ }
+ else {
+ buffer_.messageAt(firstIdx_).prevIdx = impl::INVALID_BUFFER_INDEX;
+ }
+ }
+};
+
+#define SINK_COMMON_ARGS(chr_type) std::size_t BUFFER_SIZE
+#define SINK_SET_ARGS(chr_type) BUFFER_SIZE, chr_type
+
+MIJIN_DEFINE_CHAR_VERSIONS_TMPL(BufferSink, SINK_COMMON_ARGS, SINK_SET_ARGS)
+
+#undef SINK_COMMON_ARGS
+#undef SINK_SET_ARGS
+} // namespace mijin
+
+
+#endif // !defined(MIJIN_LOGGING_STDIO_SINK_HPP_INCLUDED)
diff --git a/source/mijin/logging/debug_output_sink.hpp b/source/mijin/logging/debug_output_sink.hpp
index 54c900e..da6d51f 100644
--- a/source/mijin/logging/debug_output_sink.hpp
+++ b/source/mijin/logging/debug_output_sink.hpp
@@ -26,27 +26,28 @@ public:
using typename base_t::allocator_t;
using typename base_t::formatter_ptr_t;
using typename base_t::message_t;
+ using typename base_t::string_t;
public:
explicit BaseDebugOutputSink(formatter_ptr_t formatter, allocator_t allocator = {})
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v)
: base_t(std::move(formatter), std::move(allocator)) {}
- void handleMessageFormatted(const message_t&, const char_t* formatted) MIJIN_NOEXCEPT override
+ void handleMessageFormatted(const message_t&, const string_t& formatted) MIJIN_NOEXCEPT override
{
if constexpr (std::is_same_v)
{
- OutputDebugStringA(formatted);
+ OutputDebugStringA(formatted.c_str());
OutputDebugStringA("\n");
}
else if constexpr (std::is_same_v)
{
- OutputDebugStringW(formatted);
+ OutputDebugStringW(formatted.c_str());
OutputDebugStringW(L"\n");
}
else if constexpr (sizeof(char_t) == sizeof(char))
{
// char8_t etc.
- OutputDebugStringA(std::bit_cast(formatted));
+ OutputDebugStringA(std::bit_cast(formatted.c_str()));
OutputDebugStringA("\n");
}
else
diff --git a/source/mijin/logging/filters.hpp b/source/mijin/logging/filters.hpp
index 0f2b2b3..bc2e9db 100644
--- a/source/mijin/logging/filters.hpp
+++ b/source/mijin/logging/filters.hpp
@@ -8,11 +8,11 @@
namespace mijin
{
-template
-class BaseLevelFilter : public BaseLogFilter
+template>
+class BaseLevelFilter : public BaseLogFilter
{
public:
- using base_t = BaseLogFilter;
+ using base_t = BaseLogFilter;
using typename base_t::char_t;
using typename base_t::message_t;
private:
diff --git a/source/mijin/logging/formatting.hpp b/source/mijin/logging/formatting.hpp
index cae956d..395be21 100644
--- a/source/mijin/logging/formatting.hpp
+++ b/source/mijin/logging/formatting.hpp
@@ -78,19 +78,19 @@ MIJIN_DEFINE_CHAR_VERSIONS_TMPL(SimpleLogFormatter, FORMATTER_COMMON_ARGS, FORMA
template
requires(allocator_type>)
-class BaseFormattingLogSink : public BaseLogSink
+class BaseFormattingLogSink : public BaseLogSink
{
public:
- using base_t = BaseLogSink;
+ using base_t = BaseLogSink;
- using char_t = TChar;
- using traits_t = TTraits;
+ using typename base_t::char_t;
+ using typename base_t::message_t;
+ using typename base_t::traits_t;
using allocator_t = TAllocator;
using formatter_t = BaseLogFormatter;
using formatter_deleter_t = TDeleter;
using formatter_ptr_t = DynamicPointer;
using string_t = formatter_t::string_t;
- using typename base_t::message_t;
private:
formatter_ptr_t mFormatter;
string_t mBuffer;
@@ -100,13 +100,13 @@ public:
: mFormatter(std::move(formatter)), mBuffer(std::move(allocator))
{}
- virtual void handleMessageFormatted(const message_t& message, const char_t* formatted) MIJIN_NOEXCEPT = 0;
+ virtual void handleMessageFormatted(const message_t& message, const string_t& formatted) MIJIN_NOEXCEPT = 0;
void handleMessage(const message_t& message) noexcept override
{
mBuffer.clear();
mFormatter->format(message, mBuffer);
- handleMessageFormatted(message, mBuffer.c_str());
+ handleMessageFormatted(message, mBuffer);
}
};
@@ -162,7 +162,7 @@ void BaseSimpleLogFormatter::format(const LogMessage
{
std::format_to(std::back_inserter(outFormatted), MIJIN_SMART_QUOTE(char_t, "{}"), value);
}
- else if constexpr (is_string_v || is_cstring_v)
+ else if constexpr (is_string_v || is_string_view_v || is_cstring_v)
{
convertStringType(value, outFormatted);
}
diff --git a/source/mijin/logging/logger.hpp b/source/mijin/logging/logger.hpp
index 5de4045..94bd571 100644
--- a/source/mijin/logging/logger.hpp
+++ b/source/mijin/logging/logger.hpp
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
#include
#include "../internal/common.hpp"
#include "../util/annot.hpp"
@@ -78,12 +79,14 @@ struct BaseLogChannel
MIJIN_DEFINE_CHAR_VERSIONS(LogChannel)
-template
+template>
struct BaseLogMessage
{
using char_t = TChar;
+ using traits_t = TTraits;
+ using string_view_t = std::basic_string_view;
- const char_t* text;
+ string_view_t text;
const BaseLogChannel* channel;
const BaseLogLevel* level;
std::source_location sourceLocation;
@@ -91,12 +94,13 @@ struct BaseLogMessage
MIJIN_DEFINE_CHAR_VERSIONS(LogMessage)
-template
+template>
class BaseLogSink
{
public:
using char_t = TChar;
- using message_t = BaseLogMessage;
+ using traits_t = TTraits;
+ using message_t = BaseLogMessage;
virtual ~BaseLogSink() noexcept = default;
@@ -105,12 +109,13 @@ public:
MIJIN_DEFINE_CHAR_VERSIONS(LogSink)
-template
+template>
class BaseLogFilter
{
public:
using char_t = TChar;
- using message_t = BaseLogMessage;
+ using traits_t = TTraits;
+ using message_t = BaseLogMessage;
virtual ~BaseLogFilter() noexcept = default;
@@ -128,12 +133,13 @@ public:
using traits_t = TTraits;
using allocator_t = TAllocator;
- using sink_t = BaseLogSink;
- using filter_t = BaseLogFilter;
+ using sink_t = BaseLogSink;
+ using filter_t = BaseLogFilter;
using level_t = BaseLogLevel;
using channel_t = BaseLogChannel;
- using message_t = BaseLogMessage;
+ using message_t = BaseLogMessage;
using string_t = std::basic_string;
+ using string_view_t = std::basic_string_view;
private:
struct SinkEntry
{
@@ -146,13 +152,10 @@ public:
explicit BaseLogger(TAllocator allocator = {}) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v, TAllocator&&>))
: mSinks(TAllocator(std::move(allocator)))
{}
-
BaseLogger(const BaseLogger&) = default;
-
BaseLogger(BaseLogger&&) = default;
BaseLogger& operator=(const BaseLogger&) = default;
-
BaseLogger& operator=(BaseLogger&&) = default;
void addSink(sink_t& sink)
@@ -167,6 +170,19 @@ public:
mSinks.push_back({&sink, &filter});
}
+ bool removeSink(sink_t& sink)
+ {
+ std::unique_lock _(mMutex);
+ auto it = std::ranges::find_if(mSinks, [&](const SinkEntry& entry) {
+ return entry.sink == &sink;
+ });
+ if (it == mSinks.end()) {
+ return false;
+ }
+ mSinks.erase(it);
+ return true;
+ }
+
void postMessage(const message_t& message) const MIJIN_NOEXCEPT
{
std::unique_lock _(mMutex);
@@ -179,7 +195,7 @@ public:
}
}
- void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation, const char_t* msg) const MIJIN_NOEXCEPT
+ void logUnformatted(const level_t& level, const channel_t& channel, std::source_location sourceLocation, string_view_t msg) const MIJIN_NOEXCEPT
{
postMessage({
.text = msg,
@@ -189,6 +205,11 @@ public:
});
}
+ void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation, std::basic_format_string msg) const MIJIN_NOEXCEPT
+ {
+ logUnformatted(level, channel, sourceLocation, msg.get());
+ }
+
template
void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation,
std::basic_format_string...> fmt, TArgs&& ... args) const
@@ -196,7 +217,7 @@ public:
{
string_t buffer(allocator_t(mSinks.get_allocator()));
std::format_to(std::back_inserter(buffer), fmt, std::forward(args)...);
- log(level, channel, std::move(sourceLocation), buffer.c_str());
+ logUnformatted(level, channel, std::move(sourceLocation), buffer);
}
};
@@ -254,6 +275,9 @@ MIJIN_DEFINE_LOG_LEVEL(ERROR, MIJIN_LOG_LEVEL_VALUE_ERROR)
#define MIJIN_LOG_ALWAYS(level, channel, ...) MIJIN_FUNCNAME_GET_LOGGER().log( \
MIJIN_LOG_LEVEL_OBJECT(level), MIJIN_LOG_CHANNEL_OBJECT(channel), std::source_location::current(), __VA_ARGS__ \
)
+#define MIJIN_LOG_ALWAYS_RAW(level, channel, ...) MIJIN_FUNCNAME_GET_LOGGER().logUnformatted( \
+ MIJIN_LOG_LEVEL_OBJECT(level), MIJIN_LOG_CHANNEL_OBJECT(channel), std::source_location::current(), __VA_ARGS__ \
+)
#define MIJIN_LOG(level, channel, ...) \
if constexpr (MIJIN_LOG_LEVEL_OBJECT(level).value < MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE()) {} \
else MIJIN_LOG_ALWAYS(level, channel, __VA_ARGS__)
@@ -262,6 +286,14 @@ else MIJIN_LOG_ALWAYS(level, channel, __VA_ARGS__)
if constexpr (MIJIN_LOG_LEVEL_OBJECT(level).value < MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE()) {} \
else if (cond) MIJIN_LOG_ALWAYS(level, channel, __VA_ARGS__)
+#define MIJIN_LOG_RAW(level, channel, ...) \
+if constexpr (MIJIN_LOG_LEVEL_OBJECT(level).value < MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE()) {} \
+else MIJIN_LOG_ALWAYS_RAW(level, channel, __VA_ARGS__)
+
+#define MIJIN_LOG_IF_RAW(cond, level, channel, ...) \
+if constexpr (MIJIN_LOG_LEVEL_OBJECT(level).value < MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE()) {} \
+else if (cond) MIJIN_LOG_ALWAYS_RAW(level, channel, __VA_ARGS__)
+
#define MIJIN_SET_CLASS_LOGGER(loggerExpr) \
const auto& MIJIN_FUNCNAME_GET_LOGGER() const noexcept \
{ \
diff --git a/source/mijin/logging/stdio_sink.hpp b/source/mijin/logging/stdio_sink.hpp
index 423806a..b00609d 100644
--- a/source/mijin/logging/stdio_sink.hpp
+++ b/source/mijin/logging/stdio_sink.hpp
@@ -19,6 +19,7 @@ public:
using typename base_t::allocator_t;
using typename base_t::formatter_ptr_t;
using typename base_t::message_t;
+ using typename base_t::string_t;
private:
int mMinStderrLevel = MIJIN_LOG_LEVEL_VALUE_WARNING;
public:
@@ -33,30 +34,30 @@ public:
void setMinStderrLevel(const BaseLogLevel& level) MIJIN_NOEXCEPT { mMinStderrLevel = level.value; }
- void handleMessageFormatted(const message_t& message, const char_t* formatted) MIJIN_NOEXCEPT override
+ void handleMessageFormatted(const message_t& message, const string_t& formatted) MIJIN_NOEXCEPT override
{
FILE* stream = (message.level->value >= mMinStderrLevel) ? stderr : stdout;
if constexpr (std::is_same_v)
{
- std::fputs(formatted, stream);
- std::fputc('\n', stream);
+ (void) std::fputs(formatted.c_str(), stream);
+ (void) std::fputc('\n', stream);
}
else if constexpr (std::is_same_v)
{
- std::fputws(formatted, stream);
- std::fputwc(L'\n', stream);
+ (void) std::fputws(formatted.c_str(), stream);
+ (void) std::fputwc(L'\n', stream);
}
else if constexpr (sizeof(char_t) == sizeof(char))
{
// char8_t etc.
- std::fputs(std::bit_cast(formatted), stream);
- std::fputc('\n', stream);
+ (void) std::fputs(std::bit_cast(formatted.c_str()), stream);
+ (void) std::fputc('\n', stream);
}
else
{
static_assert(always_false_v, "Character type not supported.");
}
- std::fflush(stream);
+ (void) std::fflush(stream);
}
};
diff --git a/source/mijin/logging/stream_sink.hpp b/source/mijin/logging/stream_sink.hpp
index 9dd5523..12f70d3 100644
--- a/source/mijin/logging/stream_sink.hpp
+++ b/source/mijin/logging/stream_sink.hpp
@@ -6,7 +6,6 @@
#include "./formatting.hpp"
#include "../io/stream.hpp"
-#include "../util/traits.hpp"
namespace mijin
{
@@ -20,6 +19,7 @@ public:
using typename base_t::allocator_t;
using typename base_t::formatter_ptr_t;
using typename base_t::message_t;
+ using typename base_t::string_t;
using stream_ptr_t = DynamicPointer;
private:
stream_ptr_t mStream;
@@ -36,12 +36,12 @@ public:
mStream = std::move(stream).release();
}
- void handleMessageFormatted(const message_t& /* message */, const char_t* formatted) MIJIN_NOEXCEPT override
+ void handleMessageFormatted(const message_t& /* message */, const string_t& formatted) MIJIN_NOEXCEPT override
{
if (!mStream) {
return;
}
- (void) mStream->writeSpan(std::basic_string_view(formatted));
+ (void) mStream->writeSpan(formatted);
(void) mStream->write('\n');
mStream->flush();
}