Compare commits

...

3 Commits

Author SHA1 Message Date
Patrick Wuttke
02e99bbc82 Made logger thread-safe and added filters. 2025-07-14 17:16:24 +02:00
Patrick Wuttke
ad627b7c70 Added NotNullable::release() function. 2025-07-14 17:16:09 +02:00
Patrick Wuttke
4a9a60c7f5 Replaced default deleter used in DynamicPointer so it fits better with other functions. 2025-07-14 17:15:25 +02:00
5 changed files with 153 additions and 7 deletions

View File

@ -0,0 +1,52 @@
#pragma once
#if !defined(MIJIN_LOGGING_FILTERS_HPP_INCLUDED)
#define MIJIN_LOGGING_FILTERS_HPP_INCLUDED 1
#include "./logger.hpp"
namespace mijin
{
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
class BaseLevelFilter : public BaseLogFilter<TChar>
{
public:
using base_t = BaseLogFilter<TChar>;
using typename base_t::char_t;
using typename base_t::message_t;
private:
int mMinLevel = 0;
int mMaxLevel = 0;
public:
explicit BaseLevelFilter(int minLevel, int maxLevel = std::numeric_limits<int>::max()) MIJIN_NOEXCEPT
: mMinLevel(minLevel), mMaxLevel(maxLevel) {}
explicit BaseLevelFilter(const BaseLogLevel<char_t>& minLevel, const BaseLogLevel<char_t>& maxLevel = {nullptr, std::numeric_limits<int>::max()}) MIJIN_NOEXCEPT
: mMinLevel(minLevel.value), mMaxLevel(maxLevel.value) {}
[[nodiscard]]
int getMinLevel() const MIJIN_NOEXCEPT { return mMinLevel; }
[[nodiscard]]
int getMaxLevel() const MIJIN_NOEXCEPT { return mMaxLevel; }
void setMinLevel(int level) MIJIN_NOEXCEPT { mMinLevel = level; }
void setMinLevel(const BaseLogLevel<char_t>& level) MIJIN_NOEXCEPT { mMinLevel = level.value; }
void setMaxLevel(int level) MIJIN_NOEXCEPT { mMaxLevel = level; }
void setMaxLevel(const BaseLogLevel<char_t>& level) MIJIN_NOEXCEPT { mMaxLevel = level.value; }
bool shouldShow(const message_t& message) MIJIN_NOEXCEPT override
{
return message.level->value >= mMinLevel && message.level->value <= mMaxLevel;
}
};
MIJIN_DEFINE_CHAR_VERSIONS(LevelFilter)
} // namespace mijin
#endif // !defined(MIJIN_LOGGING_STDIO_SINK_HPP_INCLUDED)

View File

@ -6,6 +6,7 @@
#include <cstdint> #include <cstdint>
#include <format> #include <format>
#include <mutex>
#include <source_location> #include <source_location>
#include <string> #include <string>
#include <vector> #include <vector>
@ -104,6 +105,20 @@ public:
MIJIN_DEFINE_CHAR_VERSIONS(LogSink) MIJIN_DEFINE_CHAR_VERSIONS(LogSink)
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
class BaseLogFilter
{
public:
using char_t = TChar;
using message_t = BaseLogMessage<char_t>;
virtual ~BaseLogFilter() noexcept = default;
virtual bool shouldShow(const message_t& message) MIJIN_NOEXCEPT = 0;
};
MIJIN_DEFINE_CHAR_VERSIONS(LogFilter)
#define LOGGER_COMMON_ARGS(chr_type) template<typename T> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR #define LOGGER_COMMON_ARGS(chr_type) template<typename T> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>, LOGGER_COMMON_ARGS(TChar)> template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>, LOGGER_COMMON_ARGS(TChar)>
class BaseLogger class BaseLogger
@ -114,15 +129,22 @@ public:
using allocator_t = TAllocator<char_t>; using allocator_t = TAllocator<char_t>;
using sink_t = BaseLogSink<char_t>; using sink_t = BaseLogSink<char_t>;
using filter_t = BaseLogFilter<char_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>;
using string_t = std::basic_string<char_t, traits_t, allocator_t>; using string_t = std::basic_string<char_t, traits_t, allocator_t>;
private: private:
std::vector<sink_t*, TAllocator<sink_t*>> mSinks; struct SinkEntry
{
sink_t* sink;
filter_t* filter;
};
std::vector<SinkEntry, TAllocator<SinkEntry>> mSinks;
mutable std::mutex mMutex;
public: public:
explicit BaseLogger(TAllocator<sink_t*> allocator = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TAllocator<sink_t*>>) explicit BaseLogger(TAllocator<void> allocator = {}) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<TAllocator<SinkEntry>, TAllocator<void>&&>))
: mSinks(std::move(allocator)) : mSinks(TAllocator<SinkEntry>(std::move(allocator)))
{} {}
BaseLogger(const BaseLogger&) = default; BaseLogger(const BaseLogger&) = default;
@ -135,14 +157,25 @@ public:
void addSink(sink_t& sink) void addSink(sink_t& sink)
{ {
mSinks.push_back(&sink); std::unique_lock _(mMutex);
mSinks.push_back({&sink, nullptr});
}
void addSink(sink_t& sink, filter_t& filter)
{
std::unique_lock _(mMutex);
mSinks.push_back({&sink, &filter});
} }
void postMessage(const message_t& message) const MIJIN_NOEXCEPT void postMessage(const message_t& message) const MIJIN_NOEXCEPT
{ {
for (sink_t* sink: mSinks) std::unique_lock _(mMutex);
for (const SinkEntry& entry : mSinks)
{ {
sink->handleMessage(message); if (entry.filter != nullptr && !entry.filter->shouldShow(message)) {
continue;
}
entry.sink->handleMessage(message);
} }
} }

View File

@ -0,0 +1,58 @@
#pragma once
#if !defined(MIJIN_LOGGING_STREAM_SINK_HPP_INCLUDED)
#define MIJIN_LOGGING_STREAM_SINK_HPP_INCLUDED 1
#include "./formatting.hpp"
#include "../io/stream.hpp"
#include "../util/traits.hpp"
namespace mijin
{
template<MIJIN_FORMATTING_SINK_TMPL_ARGS_INIT>
requires(allocator_type<TAllocator<TChar>>)
class BaseStreamSink : public BaseFormattingLogSink<MIJIN_FORMATTING_SINK_TMP_ARG_NAMES>
{
public:
using base_t = BaseFormattingLogSink<MIJIN_FORMATTING_SINK_TMP_ARG_NAMES>;
using typename base_t::char_t;
using typename base_t::allocator_t;
using typename base_t::formatter_ptr_t;
using typename base_t::message_t;
using stream_ptr_t = DynamicPointer<Stream>;
private:
stream_ptr_t mStream;
int mMinStderrLevel = MIJIN_LOG_LEVEL_VALUE_WARNING;
public:
explicit BaseStreamSink(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)) {}
explicit BaseStreamSink(not_null_t<stream_ptr_t> stream, 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)), mStream(std::move(stream)) {}
void setStream(not_null_t<stream_ptr_t> stream) {
mStream = std::move(stream).release();
}
void handleMessageFormatted(const message_t& /* message */, const char_t* formatted) MIJIN_NOEXCEPT override
{
if (!mStream) {
return;
}
(void) mStream->writeSpan(std::basic_string_view(formatted));
(void) mStream->write('\n');
mStream->flush();
}
};
#define SINK_SET_ARGS(chr_type) chr_type, std::char_traits<chr_type>, TAllocator, TDeleter
MIJIN_DEFINE_CHAR_VERSIONS_TMPL(StreamSink, MIJIN_FORMATTING_SINK_COMMON_ARGS, SINK_SET_ARGS)
#undef SINK_SET_ARGS
} // namespace mijin
#endif // !defined(MIJIN_LOGGING_STREAM_SINK_HPP_INCLUDED)

View File

@ -17,7 +17,7 @@ namespace mijin
{ {
MIJIN_DEFINE_FLAG(Owning); MIJIN_DEFINE_FLAG(Owning);
template<typename T, deleter_type<T> TDeleter = std::default_delete<T>> template<typename T, deleter_type<T> TDeleter = AllocatorDeleter<MIJIN_DEFAULT_ALLOCATOR<T>>>
class DynamicPointer class DynamicPointer
{ {
public: public:

View File

@ -160,6 +160,9 @@ public:
[[nodiscard]] [[nodiscard]]
constexpr const T& get() const MIJIN_NOEXCEPT { return base_; } constexpr const T& get() const MIJIN_NOEXCEPT { return base_; }
[[nodiscard]]
constexpr T release() && MIJIN_NOEXCEPT { return std::exchange(base_, nullptr); }
template<nullable_type TOther> template<nullable_type TOther>
friend class NotNullable; friend class NotNullable;
}; };