add Logger::shared()

This commit is contained in:
falsycat 2022-06-06 09:43:38 +09:00
parent e839457bfd
commit 6d2e6a71a1
6 changed files with 228 additions and 135 deletions

View File

@ -63,7 +63,7 @@ target_sources(nf7
common/lambda.hh
common/lock.hh
common/logger.hh
common/logger_pool.hh
common/logger_ref.hh
common/luajit_queue.hh
common/memento.hh
common/native_file.hh

View File

@ -1,5 +1,6 @@
#pragma once
#include <memory>
#include <string>
#include <string_view>
@ -22,7 +23,11 @@ class Logger : public File::Interface {
Logger() = default;
// thread-safe
virtual void Write(Item&&) noexcept = 0;
// The parameter is to avoid problems with multi-inheritance and nothing more than.
virtual std::shared_ptr<Logger> self(Logger* = nullptr) noexcept = 0;
};
struct Logger::Item final {
public:

View File

@ -1,79 +0,0 @@
#pragma once
#include <cassert>
#include <mutex>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "nf7.hh"
#include "common/logger.hh"
namespace nf7 {
class LoggerPool : public Logger {
public:
static constexpr size_t kMaxPool = 1024;
static constexpr auto kDefaultLoggerName = "_logger";
LoggerPool(File& owner,
size_t search_min_dist = 0,
std::string_view name = kDefaultLoggerName) noexcept :
owner_(&owner), search_min_dist_(search_min_dist), logger_name_(name) {
}
~LoggerPool() noexcept {
assert(items_.empty());
}
LoggerPool(const LoggerPool&) = delete;
LoggerPool(LoggerPool&&) = delete;
LoggerPool& operator=(const LoggerPool&) = delete;
LoggerPool& operator=(LoggerPool&&) = delete;
void Write(Logger::Item&& item) noexcept override {
if (items_.size() >= kMaxPool) return;
items_.push_back(std::move(item));
}
void Flush() noexcept
try {
if (items_.empty()) return;
auto& logger = owner_->
ancestorOrThrow(search_min_dist_).
ResolveUpwardOrThrow(kDefaultLoggerName).
interfaceOrThrow<nf7::Logger>();
for (auto& item : items_) {
item.file = owner_->id();
logger.Write(std::move(item));
}
items_.clear();
} catch (File::NotFoundException&) {
} catch (File::NotImplementedException&) {
}
private:
File* const owner_;
size_t search_min_dist_;
std::string logger_name_;
std::vector<Item> items_;
};
class LoggerSyncPool : private LoggerPool {
public:
using LoggerPool::LoggerPool;
void Write(Logger::Item&& item) noexcept override {
std::unique_lock<std::mutex> _(mtx_);
LoggerPool::Write(std::move(item));
}
using LoggerPool::Flush;
private:
std::mutex mtx_;
};
} // namespace nf7

62
common/logger_ref.hh Normal file
View File

@ -0,0 +1,62 @@
#pragma once
#include <cassert>
#include <memory>
#include <string_view>
#include "nf7.hh"
#include "common/logger.hh"
namespace nf7 {
class LoggerRef final {
public:
LoggerRef() noexcept {
}
LoggerRef(const LoggerRef&) = default;
LoggerRef(LoggerRef&&) = default;
LoggerRef& operator=(const LoggerRef&) = default;
LoggerRef& operator=(LoggerRef&&) = default;
void SetUp(nf7::File& f, std::string_view name = "_logger") {
try {
id_ = f.id();
logger_ = f.ResolveUpwardOrThrow(name).
interfaceOrThrow<nf7::Logger>().self();
} catch (Exception&) {
id_ = 0;
}
}
void TearDown() {
id_ = 0;
logger_ = nullptr;
}
// thread-safe
void Trace(std::string_view msg) noexcept {
Write({nf7::Logger::kTrace, msg});
}
void Info(std::string_view msg) noexcept {
Write({nf7::Logger::kInfo, msg});
}
void Warn(std::string_view msg) noexcept {
Write({nf7::Logger::kWarn, msg});
}
void Error(std::string_view msg) noexcept {
Write({nf7::Logger::kError, msg});
}
void Write(nf7::Logger::Item&& item) noexcept {
if (!id_ || !logger_) return;
item.file = id_;
logger_->Write(std::move(item));
}
private:
File::Id id_;
std::shared_ptr<nf7::Logger> logger_;
};
} // namespace nf7

31
common/yas_std_atomic.hh Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include <atomic>
#include <yas/serialize.hpp>
namespace yas::detail {
template <size_t F, typename T>
struct serializer<
type_prop::not_a_fundamental,
ser_case::use_internal_serializer,
F,
std::atomic<T>> {
public:
template <typename Archive>
static Archive& save(Archive& ar, const std::atomic<T>& v) {
ar(v.load());
return ar;
}
template <typename Archive>
static Archive& load(Archive& ar, std::atomic<T>& v) {
T temp;
ar(temp);
v.store(temp);
return ar;
}
};
} // namespace yas::detail

View File

@ -1,6 +1,8 @@
#include <atomic>
#include <cinttypes>
#include <deque>
#include <memory>
#include <mutex>
#include <typeinfo>
#include <utility>
#include <iostream>
@ -11,8 +13,8 @@
#include "common/generic_type_info.hh"
#include "common/gui_window.hh"
#include "common/logger.hh"
#include "common/logger_pool.hh"
#include "common/ptr_selector.hh"
#include "common/yas_std_atomic.hh"
using namespace std::literals;
@ -21,9 +23,8 @@ using namespace std::literals;
namespace nf7 {
namespace {
class Logger final : public File,
public nf7::DirItem,
public nf7::Logger {
class Logger final : public nf7::File,
public nf7::DirItem {
public:
static inline const GenericTypeInfo<Logger> kType = {"System/Logger", {"DirItem"}};
@ -46,93 +47,78 @@ class Logger final : public File,
return st.str();
}
};
struct Param final {
public:
Param(uint32_t mr, bool p, bool f) : max_rows(mr), propagate(p), freeze(f) {
}
std::atomic<uint32_t> max_rows;
std::atomic<bool> propagate;
std::atomic<bool> freeze;
};
class ItemStore;
Logger(Env& env, uint32_t max_rows = 1024, bool propagate = false, bool freeze = false) noexcept :
File(kType, env), DirItem(DirItem::kMenu),
propagation_pool_(*this, 1), win_(*this, "LogView System/Logger"),
max_rows_(max_rows), propagate_(propagate), freeze_(freeze) {
param_(std::make_shared<Param>(max_rows, propagate, freeze)),
win_(*this, "LogView") {
win_.shown() = true;
}
Logger(Env& env, Deserializer& ar) : Logger(env) {
ar(win_, max_rows_, propagate_, freeze_);
ar(win_, param_->max_rows, param_->propagate, param_->freeze);
if (max_rows_ == 0) {
if (param_->max_rows == 0) {
throw DeserializeException("max_rows must be 1 or more");
}
}
void Serialize(Serializer& ar) const noexcept override {
ar(win_, max_rows_, propagate_, freeze_);
ar(win_, param_->max_rows, param_->propagate, param_->freeze);
}
std::unique_ptr<File> Clone(Env& env) const noexcept override {
return std::make_unique<Logger>(env, max_rows_, propagate_, freeze_);
return std::make_unique<Logger>(
env, param_->max_rows, param_->propagate, param_->freeze);
}
void Handle(const Event& ev) noexcept override;
void Update() noexcept override;
void UpdateMenu() noexcept override;
void UpdateRowMenu(const Row&) noexcept;
void Write(Item&& item) noexcept override {
if (freeze_) return;
if (rows_.size() >= max_rows_) rows_.pop_front();
if (propagate_) {
propagation_pool_.Write(Item(item));
}
Row row = {
.file = item.file,
.srcloc = item.srcloc,
.level = GetLevelString(item.level),
.msg = std::move(item.msg),
.path = GetPathString(item.file),
.location = GetLocationString(item.srcloc),
};
rows_.push_back(std::move(row));
updated_ = true;
}
File::Interface* interface(const std::type_info& t) noexcept override {
return InterfaceSelector<nf7::DirItem, nf7::Logger>(t).Select(this);
return InterfaceSelector<nf7::DirItem, nf7::Logger>(t).
Select(this, store_.get());
}
private:
std::deque<Row> rows_;
std::shared_ptr<Param> param_;
std::shared_ptr<ItemStore> store_;
std::deque<Row> rows_;
const char* popup_ = nullptr;
LoggerPool propagation_pool_;
bool updated_ = false;
// persistent params
gui::Window win_;
uint32_t max_rows_;
bool propagate_;
bool freeze_;
void DropExceededRows() noexcept {
if (rows_.size() <= max_rows_) return;
rows_.erase(rows_.begin(), rows_.end()-max_rows_);
if (rows_.size() <= param_->max_rows) return;
rows_.erase(rows_.begin(), rows_.end()-param_->max_rows);
}
std::string GetPathString(File::Id id) const noexcept
try {
return env().GetFile(id).abspath().Stringify();
} catch (File::NotFoundException&) {
} catch (ExpiredException&) {
return "[EXPIRED]";
}
static const char* GetLevelString(Level lv) noexcept {
static const char* GetLevelString(nf7::Logger::Level lv) noexcept {
switch (lv) {
case kTrace:
case nf7::Logger::kTrace:
return "TRAC";
case kInfo:
case nf7::Logger::kInfo:
return "INFO";
case kWarn:
case nf7::Logger::kWarn:
return "WARN";
case kError:
case nf7::Logger::kError:
return "ERRR";
default:
assert(false);
@ -144,6 +130,86 @@ class Logger final : public File,
}
};
class Logger::ItemStore final : public nf7::Context,
public nf7::Logger,
public std::enable_shared_from_this<ItemStore> {
public:
ItemStore() = delete;
ItemStore(File& owner, const std::shared_ptr<Param>& param) noexcept :
Context(owner.env(), owner.id()), param_(param) {
}
ItemStore(const ItemStore&) = delete;
ItemStore(ItemStore&&) = delete;
ItemStore& operator=(const ItemStore&) = delete;
ItemStore& operator=(ItemStore&&) = delete;
void Write(nf7::Logger::Item&& item) noexcept override {
if (param_->freeze) return;
if (param_->propagate) {
// TODO propagation
}
std::unique_lock<std::mutex> k(mtx_);
if (items_.size() >= param_->max_rows) items_.pop_front();
items_.push_back(std::move(item));
}
bool MoveItemsTo(auto& owner) noexcept {
std::unique_lock<std::mutex> k(mtx_);
if (items_.empty()) return false;
auto& rows = owner.rows_;
auto itr = items_.begin();
if (rows.size()+items_.size() > param_->max_rows) {
// max_rows may be changed
if (items_.size() > param_->max_rows) {
itr += static_cast<intmax_t>(param_->max_rows - items_.size());
}
const auto keep =
static_cast<intmax_t>(param_->max_rows) - std::distance(itr, items_.end());
rows.erase(rows.begin(), rows.end()-keep);
}
for (; itr < items_.end(); ++itr) {
Row row = {
.file = itr->file,
.srcloc = itr->srcloc,
.level = GetLevelString(itr->level),
.msg = std::move(itr->msg),
.path = owner.GetPathString(itr->file),
.location = GetLocationString(itr->srcloc),
};
rows.push_back(std::move(row));
}
items_.clear();
return true;
}
std::string GetDescription() const noexcept override {
return "System/Logger shared instance";
}
std::shared_ptr<nf7::Logger> self(nf7::Logger*) noexcept override {
return shared_from_this();
}
private:
std::mutex mtx_;
std::deque<nf7::Logger::Item> items_;
std::shared_ptr<Param> param_;
};
void Logger::Handle(const Event& ev) noexcept {
switch (ev.type) {
case Event::kAdd:
store_ = std::make_shared<ItemStore>(*this, param_);
return;
case Event::kRemove:
store_ = nullptr;
return;
default:
return;
}
}
void Logger::Update() noexcept {
if (const auto name = std::exchange(popup_, nullptr)) {
ImGui::OpenPopup(name);
@ -157,19 +223,27 @@ void Logger::Update() noexcept {
ImGui::Spacing();
static const uint32_t kMinRows = 1, kMaxRows = 1024*1024;
if (ImGui::DragScalar("max rows", ImGuiDataType_U32, &max_rows_, 1, &kMinRows, &kMaxRows)) {
uint32_t max_rows = param_->max_rows;
if (ImGui::DragScalar("max rows", ImGuiDataType_U32, &max_rows, 1, &kMinRows, &kMaxRows)) {
param_->max_rows = max_rows;
DropExceededRows();
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("the oldest row is dropped when exceed");
}
ImGui::Checkbox("propagate", &propagate_);
bool propagate = param_->propagate;
if (ImGui::Checkbox("propagate", &propagate)) {
param_->propagate = propagate;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("after handling, passes the msg to outer logger if exists");
}
ImGui::Checkbox("freeze", &freeze_);
bool freeze = param_->freeze;
if (ImGui::Checkbox("freeze", &freeze)) {
param_->freeze = freeze;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("stop handling except propagation");
}
@ -190,8 +264,8 @@ void Logger::Update() noexcept {
ImGuiTableFlags_SizingStretchProp |
ImGuiTableFlags_ScrollY;
if (ImGui::BeginTable("logs", 4, kTableFlags, ImGui::GetContentRegionAvail(), 0)) {
const bool autoscroll =
std::exchange(updated_, false) && ImGui::GetScrollY() == ImGui::GetScrollMaxY();
const bool updated = store_->MoveItemsTo(*this);
const bool autoscroll = updated && ImGui::GetScrollY() == ImGui::GetScrollMaxY();
ImGui::TableSetupColumn("level");
ImGui::TableSetupColumn("msg");