add System/Logger

This commit is contained in:
falsycat 2022-05-24 23:59:18 +09:00
parent c0e6332ba8
commit 3d44fbb9d3
6 changed files with 440 additions and 1 deletions

View File

@ -44,11 +44,14 @@ target_sources(nf7
common/dir.hh
common/gui_window.hh
common/logger.hh
common/logger_pool.hh
common/queue.hh
common/type_info.hh
common/yas.hh
file/system_dir.cc
file/system_logger.cc
)
target_link_libraries(nf7
PRIVATE

49
common/file_ref.hh Normal file
View File

@ -0,0 +1,49 @@
#pragma once
#include <optional>
#include <utility>
#include "nf7.hh"
namespace nf7 {
class FileRef final {
public:
FileRef() = delete;
FileRef(File& owner) noexcept : owner_(&owner) {
}
FileRef(File& owner, File::Path&& p, File::Id id = 0) noexcept :
owner_(&owner), path_(std::move(p)), id_(id) {
}
FileRef(File& owner, File::Id id) noexcept : owner_(&owner), id_(id) {
}
FileRef(const FileRef&) = default;
FileRef(FileRef&&) = default;
FileRef& operator=(const FileRef&) = default;
FileRef& operator=(FileRef&&) = default;
File& operator*() const {
try {
return owner_->env().GetFile(id_);
} catch (ExpiredException&) {
if (!path_) throw;
}
auto& ret = owner_->Resolve(*path_);
const_cast<File::Id&>(id_) = ret.id();
return ret;
}
const File::Path& path() const noexcept {
assert(path_);
return *path_;
}
private:
File* file_;
std::optional<File::Path> path_;
File::Id id_ = 0;
};
} // namespace nf7

44
common/logger.hh Normal file
View File

@ -0,0 +1,44 @@
#pragma once
#include <string>
#include <string_view>
#include <source_location.hh>
#include "nf7.hh"
namespace nf7 {
class Logger : public File::Interface {
public:
enum Level {
kTrace,
kInfo,
kWarn,
kError,
};
struct Item;
Logger() = default;
virtual void Write(Item&&) noexcept = 0;
};
struct Logger::Item final {
public:
Item(Level lv, std::string_view m, File::Id f = 0, std::source_location s = std::source_location::current()) noexcept :
level(lv), msg(m), file(f), srcloc(s) {
}
Item(const Item&) = default;
Item(Item&&) = default;
Item& operator=(const Item&) = default;
Item& operator=(Item&&) = default;
Level level;
std::string msg;
File::Id file;
std::source_location srcloc;
};
} // namespace nf7

79
common/logger_pool.hh Normal file
View File

@ -0,0 +1,79 @@
#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).
ifaceOrThrow<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

261
file/system_logger.cc Normal file
View File

@ -0,0 +1,261 @@
#include <cinttypes>
#include <deque>
#include <memory>
#include <typeinfo>
#include <utility>
#include <iostream>
#include "nf7.hh"
#include "common/dir.hh"
#include "common/gui_window.hh"
#include "common/logger.hh"
#include "common/logger_pool.hh"
#include "common/ptr_selector.hh"
#include "common/type_info.hh"
using namespace std::literals;
namespace nf7 {
namespace {
class Logger final : public File, public nf7::DirItem, public nf7::Logger {
public:
static inline const GenericTypeInfo<Logger> kType = {"System", "Logger", {"DirItem"}};
struct Row final {
public:
File::Id file;
std::source_location srcloc;
const char* level;
std::string msg;
std::string path;
std::string location;
std::string Stringify() const noexcept {
std::stringstream st;
st << level << "\n";
st << " " << msg << "\n";
st << " from " << path << "\n";
st << " at " << location;
return st.str();
}
};
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) {
win_.shown() = true;
}
Logger(Env& env, Deserializer& ar) : Logger(env) {
ar(win_, max_rows_, propagate_, freeze_);
if (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_);
}
std::unique_ptr<File> Clone(Env& env) const noexcept override {
return std::make_unique<Logger>(env, max_rows_, propagate_, freeze_);
}
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* iface(const std::type_info& t) noexcept override {
return InterfaceSelector<nf7::DirItem, nf7::Logger>(t).Select(this);
}
private:
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_);
}
std::string GetPathString(File::Id id) const noexcept
try {
return env().GetFile(id).abspath().Stringify();
} catch (File::NotFoundException&) {
return "[EXPIRED]";
}
static const char* GetLevelString(Level lv) noexcept {
switch (lv) {
case kTrace:
return "TRAC";
case kInfo:
return "INFO";
case kWarn:
return "WARN";
case kError:
return "ERRR";
default:
assert(false);
}
}
static std::string GetLocationString(const std::source_location loc) noexcept {
return loc.file_name()+":"s+loc.function_name()+":"s+std::to_string(loc.line());
}
};
void Logger::Update() noexcept {
if (const auto name = std::exchange(popup_, nullptr)) {
ImGui::OpenPopup(name);
}
// config popup
if (ImGui::BeginPopup("ConfigPopup")) {
ImGui::TextUnformatted("System/Logger Config");
ImGui::Spacing();
static const uint32_t kMinRows = 1, kMaxRows = 1024*1024;
if (ImGui::DragScalar("max rows", ImGuiDataType_U32, &max_rows_, 1, &kMinRows, &kMaxRows)) {
DropExceededRows();
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("the oldest row is dropped when exceed");
}
ImGui::Checkbox("propagate", &propagate_);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("after handling, passes the msg to outer logger if exists");
}
ImGui::Checkbox("freeze", &freeze_);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("stop handling except propagation");
}
ImGui::EndPopup();
}
// LogView
if (win_.Begin()) {
constexpr auto kTableFlags =
ImGuiTableFlags_Resizable |
ImGuiTableFlags_Hideable |
ImGuiTableFlags_RowBg |
ImGuiTableFlags_Borders |
ImGuiTableFlags_ContextMenuInBody |
ImGuiTableFlags_SizingStretchProp |
ImGuiTableFlags_ScrollY;
if (ImGui::BeginTable("logs", 4, kTableFlags, ImGui::GetContentRegionAvail(), 0)) {
const bool autoscroll =
std::exchange(updated_, false) && ImGui::GetScrollY() == ImGui::GetScrollMaxY();
ImGui::TableSetupColumn("level");
ImGui::TableSetupColumn("msg");
ImGui::TableSetupColumn("path");
ImGui::TableSetupColumn("location");
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableHeadersRow();
for (const auto& row : rows_) {
ImGui::TableNextRow();
ImGui::PushID(&row);
if (autoscroll && &row == &rows_.back()) {
ImGui::SetScrollHereY();
}
// level column
if (ImGui::TableSetColumnIndex(0)) {
constexpr auto kFlags =
ImGuiSelectableFlags_SpanAllColumns |
ImGuiSelectableFlags_AllowItemOverlap;
ImGui::Selectable(row.level, false, kFlags);
if (ImGui::BeginPopupContextItem()) {
UpdateRowMenu(row);
ImGui::EndPopup();
}
}
// msg column
if (ImGui::TableNextColumn()) {
ImGui::TextUnformatted(row.msg.c_str());
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(row.msg.c_str());
}
}
// path column
if (ImGui::TableNextColumn()) {
ImGui::TextUnformatted(row.path.c_str());
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(row.path.c_str());
}
}
// location column
if (ImGui::TableNextColumn()) {
ImGui::TextUnformatted(row.location.c_str());
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::Text("file: %s", row.srcloc.file_name());
ImGui::Text("func: %s", row.srcloc.function_name());
ImGui::Text("line: %zu", static_cast<size_t>(row.srcloc.line()));
ImGui::Text("col : %zu", static_cast<size_t>(row.srcloc.column()));
ImGui::EndTooltip();
}
}
ImGui::PopID();
}
ImGui::EndTable();
}
}
win_.End();
}
void Logger::UpdateMenu() noexcept {
ImGui::MenuItem("shown", nullptr, &win_.shown());
if (ImGui::MenuItem("config")) {
popup_ = "ConfigPopup";
}
}
void Logger::UpdateRowMenu(const Row& row) noexcept {
if (ImGui::MenuItem("copy as text")) {
ImGui::SetClipboardText(row.Stringify().c_str());
}
}
}
} // namespace nf7

View File

@ -43,7 +43,10 @@ int main(void) {
ar("Dir"s);
ar(std::map<std::string, L> {
{ "home"s, Write(ar, "Dir"s, std::map<std::string, L> {}, WINDOW_(false)) },
{ "_logger"s,
Write(ar, "Logger"s, WINDOW_(true), 1024, false, false) },
{ "home"s,
Write(ar, "Dir"s, std::map<std::string, L> {}, WINDOW_(false)) },
}, WINDOW_(true));
const auto buf = os.get_shared_buffer();