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*.
This commit is contained in:
parent
7d4c782b0f
commit
0e1964ba25
376
source/mijin/logging/buffer_sink.hpp
Normal file
376
source/mijin/logging/buffer_sink.hpp
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !defined(MIJIN_LOGGING_BUFFER_SINK_HPP_INCLUDED)
|
||||||
|
#define MIJIN_LOGGING_BUFFER_SINK_HPP_INCLUDED 1
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <mutex>
|
||||||
|
#include <span>
|
||||||
|
#include "./logger.hpp"
|
||||||
|
|
||||||
|
namespace mijin
|
||||||
|
{
|
||||||
|
inline constexpr std::size_t BUFFER_SINK_DEFAULT_SIZE = 10 * 1024 * 1024; // default 10 MiB buffer
|
||||||
|
|
||||||
|
template<std::size_t VBufferSize = BUFFER_SINK_DEFAULT_SIZE, typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>>
|
||||||
|
class BaseBufferSink;
|
||||||
|
|
||||||
|
namespace impl
|
||||||
|
{
|
||||||
|
static constexpr std::uint32_t INVALID_BUFFER_INDEX = std::numeric_limits<std::uint32_t>::max();
|
||||||
|
|
||||||
|
template<typename TChar>
|
||||||
|
struct BufferedMessageHeader
|
||||||
|
{
|
||||||
|
const BaseLogChannel<TChar>* channel;
|
||||||
|
const BaseLogLevel<TChar>* 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<typename TChar, typename TTraits, std::size_t VBufferSize>
|
||||||
|
struct MessageBuffer
|
||||||
|
{
|
||||||
|
using char_t = TChar;
|
||||||
|
using traits_t = TTraits;
|
||||||
|
using message_t = BaseLogMessage<char_t, traits_t>;
|
||||||
|
using header_t = BufferedMessageHeader<TChar>;
|
||||||
|
static constexpr std::size_t BUFFER_SIZE = VBufferSize;
|
||||||
|
|
||||||
|
std::array<std::uint8_t, BUFFER_SIZE> bytes;
|
||||||
|
mutable std::mutex mutex;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
header_t& messageAt(std::uint32_t idx) MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
return *reinterpret_cast<header_t*>(bytes.data() + idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::span<char_t> messageText(header_t& header) MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
return {reinterpret_cast<char_t*>(reinterpret_cast<std::uint8_t*>(&header)) + sizeof(header_t), header.numChars};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const header_t& messageAt(std::uint32_t idx) const MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
return *reinterpret_cast<const header_t*>(bytes.data() + idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::span<const char_t> messageText(const header_t& header) const MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
return {reinterpret_cast<const char_t*>(reinterpret_cast<const std::uint8_t*>(&header)) + sizeof(header_t), header.numChars};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::uint32_t messageBytes(std::uint32_t numChars) MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
return static_cast<std::uint32_t>(sizeof(header_t)) + (numChars * static_cast<std::uint32_t>(sizeof(char_t)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename TChar, typename TTraits, std::size_t VBufferSize>
|
||||||
|
class BufferSinkRange;
|
||||||
|
|
||||||
|
template<typename TChar, typename TTraits, std::size_t VBufferSize>
|
||||||
|
class LockedMessageBuffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using char_t = TChar;
|
||||||
|
using traits_t = TTraits;
|
||||||
|
|
||||||
|
static constexpr std::size_t BUFFER_SIZE = VBufferSize;
|
||||||
|
|
||||||
|
using range_t = impl::BufferSinkRange<char_t, traits_t, BUFFER_SIZE>;
|
||||||
|
private:
|
||||||
|
using buffer_t = MessageBuffer<char_t, traits_t, BUFFER_SIZE>;
|
||||||
|
|
||||||
|
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<std::size_t, typename, typename>
|
||||||
|
friend class mijin::BaseBufferSink;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename TChar, typename TTraits, std::size_t VBufferSize>
|
||||||
|
class BufferSinkIterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using char_t = TChar;
|
||||||
|
using traits_t = TTraits;
|
||||||
|
using message_t = BaseLogMessage<char_t, traits_t>;
|
||||||
|
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<char_t, traits_t, BUFFER_SIZE>;
|
||||||
|
using header_t = BufferedMessageHeader<char_t>;
|
||||||
|
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<typename, typename, std::size_t>
|
||||||
|
friend class BufferSinkRange;
|
||||||
|
};
|
||||||
|
static_assert(std::forward_iterator<BufferSinkIterator<char, std::char_traits<char>, BUFFER_SINK_DEFAULT_SIZE>>);
|
||||||
|
|
||||||
|
template<typename TChar, typename TTraits, std::size_t VBufferSize>
|
||||||
|
class BufferSinkRange
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using char_t = TChar;
|
||||||
|
using traits_t = TTraits;
|
||||||
|
static constexpr std::size_t BUFFER_SIZE = VBufferSize;
|
||||||
|
|
||||||
|
using iterator = BufferSinkIterator<char_t, traits_t, BUFFER_SIZE>;
|
||||||
|
using const_iterator = iterator;
|
||||||
|
private:
|
||||||
|
using buffer_t = MessageBuffer<char_t, traits_t, BUFFER_SIZE>;
|
||||||
|
|
||||||
|
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<char_t, traits_t, BUFFER_SIZE>;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(std::bidirectional_iterator<BufferSinkIterator<char, std::char_traits<char>, 100>>);
|
||||||
|
static_assert(std::ranges::bidirectional_range<BufferSinkRange<char, std::char_traits<char>, 100>>);
|
||||||
|
|
||||||
|
template<typename TChar, typename TTraits, std::size_t VBufferSize>
|
||||||
|
auto LockedMessageBuffer<TChar, TTraits, VBufferSize>::getMessages() const MIJIN_NOEXCEPT -> range_t
|
||||||
|
{
|
||||||
|
return range_t(*buffer_, firstIdx_, lastIdx_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using impl::LockedMessageBuffer;
|
||||||
|
|
||||||
|
template<std::size_t VBufferSize, typename TChar, typename TTraits>
|
||||||
|
class BaseBufferSink : public BaseLogSink<TChar, TTraits>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using base_t = BaseLogSink<TChar>;
|
||||||
|
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<char_t, traits_t, BUFFER_SIZE>;
|
||||||
|
private:
|
||||||
|
using buffer_t = impl::MessageBuffer<char_t, traits_t, BUFFER_SIZE>;
|
||||||
|
using header_t = impl::BufferedMessageHeader<char_t>;
|
||||||
|
|
||||||
|
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<std::uint32_t>(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<std::uint32_t>(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)
|
||||||
@ -26,27 +26,28 @@ public:
|
|||||||
using typename base_t::allocator_t;
|
using typename base_t::allocator_t;
|
||||||
using typename base_t::formatter_ptr_t;
|
using typename base_t::formatter_ptr_t;
|
||||||
using typename base_t::message_t;
|
using typename base_t::message_t;
|
||||||
|
using typename base_t::string_t;
|
||||||
public:
|
public:
|
||||||
explicit BaseDebugOutputSink(formatter_ptr_t formatter, allocator_t allocator = {})
|
explicit BaseDebugOutputSink(formatter_ptr_t formatter, allocator_t allocator = {})
|
||||||
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<allocator_t>)
|
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<allocator_t>)
|
||||||
: base_t(std::move(formatter), std::move(allocator)) {}
|
: 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<char_t, char>)
|
if constexpr (std::is_same_v<char_t, char>)
|
||||||
{
|
{
|
||||||
OutputDebugStringA(formatted);
|
OutputDebugStringA(formatted.c_str());
|
||||||
OutputDebugStringA("\n");
|
OutputDebugStringA("\n");
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<char_t, wchar_t>)
|
else if constexpr (std::is_same_v<char_t, wchar_t>)
|
||||||
{
|
{
|
||||||
OutputDebugStringW(formatted);
|
OutputDebugStringW(formatted.c_str());
|
||||||
OutputDebugStringW(L"\n");
|
OutputDebugStringW(L"\n");
|
||||||
}
|
}
|
||||||
else if constexpr (sizeof(char_t) == sizeof(char))
|
else if constexpr (sizeof(char_t) == sizeof(char))
|
||||||
{
|
{
|
||||||
// char8_t etc.
|
// char8_t etc.
|
||||||
OutputDebugStringA(std::bit_cast<const char*>(formatted));
|
OutputDebugStringA(std::bit_cast<const char*>(formatted.c_str()));
|
||||||
OutputDebugStringA("\n");
|
OutputDebugStringA("\n");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@ -8,11 +8,11 @@
|
|||||||
|
|
||||||
namespace mijin
|
namespace mijin
|
||||||
{
|
{
|
||||||
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
|
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>>
|
||||||
class BaseLevelFilter : public BaseLogFilter<TChar>
|
class BaseLevelFilter : public BaseLogFilter<TChar, TTraits>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using base_t = BaseLogFilter<TChar>;
|
using base_t = BaseLogFilter<TChar, TTraits>;
|
||||||
using typename base_t::char_t;
|
using typename base_t::char_t;
|
||||||
using typename base_t::message_t;
|
using typename base_t::message_t;
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -78,19 +78,19 @@ MIJIN_DEFINE_CHAR_VERSIONS_TMPL(SimpleLogFormatter, FORMATTER_COMMON_ARGS, FORMA
|
|||||||
|
|
||||||
template<MIJIN_FORMATTING_SINK_TMPL_ARGS_INIT>
|
template<MIJIN_FORMATTING_SINK_TMPL_ARGS_INIT>
|
||||||
requires(allocator_type<TAllocator<TChar>>)
|
requires(allocator_type<TAllocator<TChar>>)
|
||||||
class BaseFormattingLogSink : public BaseLogSink<TChar>
|
class BaseFormattingLogSink : public BaseLogSink<TChar, TTraits>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using base_t = BaseLogSink<TChar>;
|
using base_t = BaseLogSink<TChar, TTraits>;
|
||||||
|
|
||||||
using char_t = TChar;
|
using typename base_t::char_t;
|
||||||
using traits_t = TTraits;
|
using typename base_t::message_t;
|
||||||
|
using typename base_t::traits_t;
|
||||||
using allocator_t = TAllocator<TChar>;
|
using allocator_t = TAllocator<TChar>;
|
||||||
using formatter_t = BaseLogFormatter<char_t, traits_t, allocator_t>;
|
using formatter_t = BaseLogFormatter<char_t, traits_t, allocator_t>;
|
||||||
using formatter_deleter_t = TDeleter;
|
using formatter_deleter_t = TDeleter;
|
||||||
using formatter_ptr_t = DynamicPointer<formatter_t, formatter_deleter_t>;
|
using formatter_ptr_t = DynamicPointer<formatter_t, formatter_deleter_t>;
|
||||||
using string_t = formatter_t::string_t;
|
using string_t = formatter_t::string_t;
|
||||||
using typename base_t::message_t;
|
|
||||||
private:
|
private:
|
||||||
formatter_ptr_t mFormatter;
|
formatter_ptr_t mFormatter;
|
||||||
string_t mBuffer;
|
string_t mBuffer;
|
||||||
@ -100,13 +100,13 @@ public:
|
|||||||
: mFormatter(std::move(formatter)), mBuffer(std::move(allocator))
|
: 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
|
void handleMessage(const message_t& message) noexcept override
|
||||||
{
|
{
|
||||||
mBuffer.clear();
|
mBuffer.clear();
|
||||||
mFormatter->format(message, mBuffer);
|
mFormatter->format(message, mBuffer);
|
||||||
handleMessageFormatted(message, mBuffer.c_str());
|
handleMessageFormatted(message, mBuffer);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -162,7 +162,7 @@ void BaseSimpleLogFormatter<TChar, TTraits, TAllocator>::format(const LogMessage
|
|||||||
{
|
{
|
||||||
std::format_to(std::back_inserter(outFormatted), MIJIN_SMART_QUOTE(char_t, "{}"), value);
|
std::format_to(std::back_inserter(outFormatted), MIJIN_SMART_QUOTE(char_t, "{}"), value);
|
||||||
}
|
}
|
||||||
else if constexpr (is_string_v<type_t> || is_cstring_v<type_t>)
|
else if constexpr (is_string_v<type_t> || is_string_view_v<type_t> || is_cstring_v<type_t>)
|
||||||
{
|
{
|
||||||
convertStringType(value, outFormatted);
|
convertStringType(value, outFormatted);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <source_location>
|
#include <source_location>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "../internal/common.hpp"
|
#include "../internal/common.hpp"
|
||||||
#include "../util/annot.hpp"
|
#include "../util/annot.hpp"
|
||||||
@ -78,12 +79,14 @@ struct BaseLogChannel
|
|||||||
|
|
||||||
MIJIN_DEFINE_CHAR_VERSIONS(LogChannel)
|
MIJIN_DEFINE_CHAR_VERSIONS(LogChannel)
|
||||||
|
|
||||||
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
|
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>>
|
||||||
struct BaseLogMessage
|
struct BaseLogMessage
|
||||||
{
|
{
|
||||||
using char_t = TChar;
|
using char_t = TChar;
|
||||||
|
using traits_t = TTraits;
|
||||||
|
using string_view_t = std::basic_string_view<char_t, traits_t>;
|
||||||
|
|
||||||
const char_t* text;
|
string_view_t text;
|
||||||
const BaseLogChannel<char_t>* channel;
|
const BaseLogChannel<char_t>* channel;
|
||||||
const BaseLogLevel<char_t>* level;
|
const BaseLogLevel<char_t>* level;
|
||||||
std::source_location sourceLocation;
|
std::source_location sourceLocation;
|
||||||
@ -91,12 +94,13 @@ struct BaseLogMessage
|
|||||||
|
|
||||||
MIJIN_DEFINE_CHAR_VERSIONS(LogMessage)
|
MIJIN_DEFINE_CHAR_VERSIONS(LogMessage)
|
||||||
|
|
||||||
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
|
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>>
|
||||||
class BaseLogSink
|
class BaseLogSink
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using char_t = TChar;
|
using char_t = TChar;
|
||||||
using message_t = BaseLogMessage<char_t>;
|
using traits_t = TTraits;
|
||||||
|
using message_t = BaseLogMessage<char_t, traits_t>;
|
||||||
|
|
||||||
virtual ~BaseLogSink() noexcept = default;
|
virtual ~BaseLogSink() noexcept = default;
|
||||||
|
|
||||||
@ -105,12 +109,13 @@ public:
|
|||||||
|
|
||||||
MIJIN_DEFINE_CHAR_VERSIONS(LogSink)
|
MIJIN_DEFINE_CHAR_VERSIONS(LogSink)
|
||||||
|
|
||||||
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
|
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>>
|
||||||
class BaseLogFilter
|
class BaseLogFilter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using char_t = TChar;
|
using char_t = TChar;
|
||||||
using message_t = BaseLogMessage<char_t>;
|
using traits_t = TTraits;
|
||||||
|
using message_t = BaseLogMessage<char_t, traits_t>;
|
||||||
|
|
||||||
virtual ~BaseLogFilter() noexcept = default;
|
virtual ~BaseLogFilter() noexcept = default;
|
||||||
|
|
||||||
@ -128,12 +133,13 @@ public:
|
|||||||
using traits_t = TTraits;
|
using traits_t = TTraits;
|
||||||
using allocator_t = TAllocator<char_t>;
|
using allocator_t = TAllocator<char_t>;
|
||||||
|
|
||||||
using sink_t = BaseLogSink<char_t>;
|
using sink_t = BaseLogSink<char_t, traits_t>;
|
||||||
using filter_t = BaseLogFilter<char_t>;
|
using filter_t = BaseLogFilter<char_t, traits_t>;
|
||||||
using level_t = BaseLogLevel<char_t>;
|
using level_t = BaseLogLevel<char_t>;
|
||||||
using channel_t = BaseLogChannel<char_t>;
|
using channel_t = BaseLogChannel<char_t>;
|
||||||
using message_t = BaseLogMessage<char_t>;
|
using message_t = BaseLogMessage<char_t, traits_t>;
|
||||||
using string_t = std::basic_string<char_t, traits_t, allocator_t>;
|
using string_t = std::basic_string<char_t, traits_t, allocator_t>;
|
||||||
|
using string_view_t = std::basic_string_view<char_t, traits_t>;
|
||||||
private:
|
private:
|
||||||
struct SinkEntry
|
struct SinkEntry
|
||||||
{
|
{
|
||||||
@ -146,13 +152,10 @@ public:
|
|||||||
explicit BaseLogger(TAllocator<void> allocator = {}) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<TAllocator<SinkEntry>, TAllocator<void>&&>))
|
explicit BaseLogger(TAllocator<void> allocator = {}) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<TAllocator<SinkEntry>, TAllocator<void>&&>))
|
||||||
: mSinks(TAllocator<SinkEntry>(std::move(allocator)))
|
: mSinks(TAllocator<SinkEntry>(std::move(allocator)))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
BaseLogger(const BaseLogger&) = default;
|
BaseLogger(const BaseLogger&) = default;
|
||||||
|
|
||||||
BaseLogger(BaseLogger&&) = default;
|
BaseLogger(BaseLogger&&) = default;
|
||||||
|
|
||||||
BaseLogger& operator=(const BaseLogger&) = default;
|
BaseLogger& operator=(const BaseLogger&) = default;
|
||||||
|
|
||||||
BaseLogger& operator=(BaseLogger&&) = default;
|
BaseLogger& operator=(BaseLogger&&) = default;
|
||||||
|
|
||||||
void addSink(sink_t& sink)
|
void addSink(sink_t& sink)
|
||||||
@ -167,6 +170,19 @@ public:
|
|||||||
mSinks.push_back({&sink, &filter});
|
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
|
void postMessage(const message_t& message) const MIJIN_NOEXCEPT
|
||||||
{
|
{
|
||||||
std::unique_lock _(mMutex);
|
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({
|
postMessage({
|
||||||
.text = msg,
|
.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<char_t> msg) const MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
logUnformatted(level, channel, sourceLocation, msg.get());
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... TArgs>
|
template<typename... TArgs>
|
||||||
void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation,
|
void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation,
|
||||||
std::basic_format_string<char_t, std::type_identity_t<TArgs>...> fmt, TArgs&& ... args) const
|
std::basic_format_string<char_t, std::type_identity_t<TArgs>...> fmt, TArgs&& ... args) const
|
||||||
@ -196,7 +217,7 @@ public:
|
|||||||
{
|
{
|
||||||
string_t buffer(allocator_t(mSinks.get_allocator()));
|
string_t buffer(allocator_t(mSinks.get_allocator()));
|
||||||
std::format_to(std::back_inserter(buffer), fmt, std::forward<TArgs>(args)...);
|
std::format_to(std::back_inserter(buffer), fmt, std::forward<TArgs>(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( \
|
#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__ \
|
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, ...) \
|
#define MIJIN_LOG(level, channel, ...) \
|
||||||
if constexpr (MIJIN_LOG_LEVEL_OBJECT(level).value < MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE()) {} \
|
if constexpr (MIJIN_LOG_LEVEL_OBJECT(level).value < MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE()) {} \
|
||||||
else MIJIN_LOG_ALWAYS(level, channel, __VA_ARGS__)
|
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()) {} \
|
if constexpr (MIJIN_LOG_LEVEL_OBJECT(level).value < MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE()) {} \
|
||||||
else if (cond) MIJIN_LOG_ALWAYS(level, channel, __VA_ARGS__)
|
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) \
|
#define MIJIN_SET_CLASS_LOGGER(loggerExpr) \
|
||||||
const auto& MIJIN_FUNCNAME_GET_LOGGER() const noexcept \
|
const auto& MIJIN_FUNCNAME_GET_LOGGER() const noexcept \
|
||||||
{ \
|
{ \
|
||||||
|
|||||||
@ -19,6 +19,7 @@ public:
|
|||||||
using typename base_t::allocator_t;
|
using typename base_t::allocator_t;
|
||||||
using typename base_t::formatter_ptr_t;
|
using typename base_t::formatter_ptr_t;
|
||||||
using typename base_t::message_t;
|
using typename base_t::message_t;
|
||||||
|
using typename base_t::string_t;
|
||||||
private:
|
private:
|
||||||
int mMinStderrLevel = MIJIN_LOG_LEVEL_VALUE_WARNING;
|
int mMinStderrLevel = MIJIN_LOG_LEVEL_VALUE_WARNING;
|
||||||
public:
|
public:
|
||||||
@ -33,30 +34,30 @@ public:
|
|||||||
|
|
||||||
void setMinStderrLevel(const BaseLogLevel<char_t>& level) MIJIN_NOEXCEPT { mMinStderrLevel = level.value; }
|
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
|
void handleMessageFormatted(const message_t& message, const string_t& formatted) MIJIN_NOEXCEPT override
|
||||||
{
|
{
|
||||||
FILE* stream = (message.level->value >= mMinStderrLevel) ? stderr : stdout;
|
FILE* stream = (message.level->value >= mMinStderrLevel) ? stderr : stdout;
|
||||||
if constexpr (std::is_same_v<char_t, char>)
|
if constexpr (std::is_same_v<char_t, char>)
|
||||||
{
|
{
|
||||||
std::fputs(formatted, stream);
|
(void) std::fputs(formatted.c_str(), stream);
|
||||||
std::fputc('\n', stream);
|
(void) std::fputc('\n', stream);
|
||||||
}
|
}
|
||||||
else if constexpr (std::is_same_v<char_t, wchar_t>)
|
else if constexpr (std::is_same_v<char_t, wchar_t>)
|
||||||
{
|
{
|
||||||
std::fputws(formatted, stream);
|
(void) std::fputws(formatted.c_str(), stream);
|
||||||
std::fputwc(L'\n', stream);
|
(void) std::fputwc(L'\n', stream);
|
||||||
}
|
}
|
||||||
else if constexpr (sizeof(char_t) == sizeof(char))
|
else if constexpr (sizeof(char_t) == sizeof(char))
|
||||||
{
|
{
|
||||||
// char8_t etc.
|
// char8_t etc.
|
||||||
std::fputs(std::bit_cast<const char*>(formatted), stream);
|
(void) std::fputs(std::bit_cast<const char*>(formatted.c_str()), stream);
|
||||||
std::fputc('\n', stream);
|
(void) std::fputc('\n', stream);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
static_assert(always_false_v<char_t>, "Character type not supported.");
|
static_assert(always_false_v<char_t>, "Character type not supported.");
|
||||||
}
|
}
|
||||||
std::fflush(stream);
|
(void) std::fflush(stream);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
#include "./formatting.hpp"
|
#include "./formatting.hpp"
|
||||||
#include "../io/stream.hpp"
|
#include "../io/stream.hpp"
|
||||||
#include "../util/traits.hpp"
|
|
||||||
|
|
||||||
namespace mijin
|
namespace mijin
|
||||||
{
|
{
|
||||||
@ -20,6 +19,7 @@ public:
|
|||||||
using typename base_t::allocator_t;
|
using typename base_t::allocator_t;
|
||||||
using typename base_t::formatter_ptr_t;
|
using typename base_t::formatter_ptr_t;
|
||||||
using typename base_t::message_t;
|
using typename base_t::message_t;
|
||||||
|
using typename base_t::string_t;
|
||||||
using stream_ptr_t = DynamicPointer<Stream>;
|
using stream_ptr_t = DynamicPointer<Stream>;
|
||||||
private:
|
private:
|
||||||
stream_ptr_t mStream;
|
stream_ptr_t mStream;
|
||||||
@ -36,12 +36,12 @@ public:
|
|||||||
mStream = std::move(stream).release();
|
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) {
|
if (!mStream) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
(void) mStream->writeSpan(std::basic_string_view(formatted));
|
(void) mStream->writeSpan(formatted);
|
||||||
(void) mStream->write('\n');
|
(void) mStream->write('\n');
|
||||||
mStream->flush();
|
mStream->flush();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user