diff --git a/event_loop.cpp b/event_loop.cpp index a8d0eea..c899cca 100644 --- a/event_loop.cpp +++ b/event_loop.cpp @@ -18,8 +18,8 @@ static inline void check_for_logcat_items(LogcatThread& logcat_thread, const Fil LogcatThreadItem* logcat_thread_item; while ((logcat_thread_item = logcat_thread.atomic_ring_buffer.get())) { - if (std::holds_alternative(*logcat_thread_item)) { - log_raw(std::move(std::get(*logcat_thread_item)), false); + if (std::holds_alternative(*logcat_thread_item)) { + log(std::move(std::get(*logcat_thread_item)), false); } else if (std::holds_alternative(*logcat_thread_item)) { logcat_entries.push_back(std::move(std::get(*logcat_thread_item))); if (matches(filters, logcat_entries.back())) { diff --git a/log.cpp b/log.cpp index 565c6b7..60e9d74 100644 --- a/log.cpp +++ b/log.cpp @@ -1,44 +1,39 @@ #include #include #include -#include #include "log.h" -std::string log_entries; -std::vector log_entry_line_offsets = {0}; +std::vector log_entries; -std::string format_log(std::string entry, time_t time) { - size_t newline_pos; +LogEntry::LogEntry(time_t time_, std::string message_) : time(time_), message(std::move(message_)) { + size_t pos; + + while ((pos = this->message.find('\n')) != std::string::npos) { + this->message.replace(pos, 1, 1, ' '); + } +} + + +std::string format_log(const LogEntry& entry) { char time_as_str[128] = {0}; - - strftime(time_as_str, 127 * sizeof(char), "%c", localtime(&time)); - while ((newline_pos = entry.find('\n')) != std::string::npos) { - entry.replace(newline_pos, 1, 1, ' '); - } - - return std::string(1, '[') + time_as_str + "] " + std::move(entry); + strftime(time_as_str, 127 * sizeof(char), "%c", localtime(&entry.time)); + return std::string(1, '[') + time_as_str + "] " + entry.message; } -std::string format_log(std::string entry) { - return format_log(std::move(entry), time(nullptr)); +void print_log(const LogEntry& entry) { + char time_as_str[128] = {0}; + strftime(time_as_str, 127 * sizeof(char), "%c", localtime(&entry.time)); + fprintf(stderr, "[%s] %s\n", time_as_str, entry.message.c_str()); } -void log_raw(std::string line, bool print) { +void log(LogEntry entry, bool print) { if (print) { - printf("%s\n", line.c_str()); + print_log(entry); } - if (!log_entries.empty()) { - log_entries += '\n'; - log_entry_line_offsets.push_back(log_entries.size()); - } - log_entries += std::move(line); + log_entries.push_back(std::move(entry)); } -void log(std::string entry, time_t time) { - log_raw(format_log(std::move(entry), time)); -} - -void log(std::string entry) { - log_raw(format_log(std::move(entry))); +void log(std::string message) { + log({time(nullptr), std::move(message)}); } diff --git a/log.h b/log.h index bb8086a..18e0c0f 100644 --- a/log.h +++ b/log.h @@ -2,13 +2,17 @@ #include #include -#include -extern std::string log_entries; -extern std::vector log_entry_line_offsets; +struct LogEntry { + time_t time; + std::string message; -std::string format_log(std::string entry, time_t time); -std::string format_log(std::string entry); -void log_raw(std::string line, bool print = true); -void log(std::string entry, time_t time); + LogEntry() = default; + LogEntry(time_t time_, std::string message_); +}; +extern std::vector log_entries; + +std::string format_log(const LogEntry& entry); +void print_log(const LogEntry& entry); +void log(LogEntry entry, bool print = true); void log(std::string entry); diff --git a/logcat_thread.cpp b/logcat_thread.cpp index 67a9f5e..07d033e 100644 --- a/logcat_thread.cpp +++ b/logcat_thread.cpp @@ -168,8 +168,8 @@ void LogcatThread::_put_if_not_stopped(LogcatThreadItem item) { void LogcatThread::_handle_line(char* buf, size_t length, bool is_stdout) { if (!is_stdout) { - std::string log_entry = format_log(std::string("Received from logcat stderr: ") + std::string(buf, length)); - printf("%s\n", log_entry.c_str()); + LogEntry log_entry = {time(nullptr), std::string("Received from logcat stderr: ") + std::string(buf, length)}; + print_log(log_entry); this->_put_if_not_stopped(std::move(log_entry)); return; } @@ -178,8 +178,8 @@ void LogcatThread::_handle_line(char* buf, size_t length, bool is_stdout) { try { logcat_entry = try_parse_logcat_entry(buf, length, this->_current_buffer); } catch (const std::exception& e) { - std::string log_entry = format_log(std::string("Failed to parse logcat entry: ") + e.what()); - printf("%s\n", log_entry.c_str()); + LogEntry log_entry = {time(nullptr), std::string("Failed to parse logcat entry: ") + e.what()}; + print_log(log_entry); this->_put_if_not_stopped(std::move(log_entry)); } if (logcat_entry) { @@ -190,8 +190,8 @@ void LogcatThread::_handle_line(char* buf, size_t length, bool is_stdout) { try { new_buffer = try_parse_buffer(buf, length); } catch (const std::exception& e) { - std::string log_entry = format_log(std::string("Failed to parse buffer line: ") + e.what()); - printf("%s\n", log_entry.c_str()); + LogEntry log_entry = {time(nullptr), std::string("Failed to parse buffer line: ") + e.what()}; + print_log(log_entry); this->_put_if_not_stopped(std::move(log_entry)); } if (new_buffer) { @@ -199,8 +199,8 @@ void LogcatThread::_handle_line(char* buf, size_t length, bool is_stdout) { return; } - std::string log_entry = format_log(std::string("Cannot parse logcat stdout: ") + std::string(buf, length)); - printf("%s\n", log_entry.c_str()); + LogEntry log_entry = {time(nullptr), std::string("Cannot parse logcat stdout: ") + std::string(buf, length)}; + print_log(log_entry); this->_put_if_not_stopped(std::move(log_entry)); } @@ -209,8 +209,8 @@ void LogcatThread::_run_epoll_round() { int ready_fds = epoll_wait(this->_epoll_fd, events, EPOLL_MAX_EVENTS, 1000); if (ready_fds == -1) { - std::string log_entry = format_log(std::string("epoll_wait(): ") + strerror(errno)); - printf("%s\n", log_entry.c_str()); + LogEntry log_entry = {time(nullptr), std::string("epoll_wait(): ") + strerror(errno)}; + print_log(log_entry); this->_put_if_not_stopped(std::move(log_entry)); return; } @@ -223,11 +223,11 @@ void LogcatThread::_run_epoll_round() { try { handle_fd(events[i].data.fd, buf, used, &LogcatThread::_handle_line, this, is_stdout); } catch (const std::exception& e) { - std::string log_entry = "Failed to handle std"; - log_entry += is_stdout ? "out: " : "err: "; - log_entry += e.what(); - log_entry = format_log(std::move(log_entry)); - printf("%s\n", log_entry.c_str()); + std::string message = "Failed to handle std"; + message += is_stdout ? "out: " : "err: "; + message += e.what(); + LogEntry log_entry = {time(nullptr), std::move(message)}; + print_log(log_entry); this->_put_if_not_stopped(std::move(log_entry)); } } @@ -237,7 +237,8 @@ void LogcatThread::_run(std::stop_token stoken) { while (!stoken.stop_requested()) { #ifndef NDEBUG if (this->debug_log_request.test()) { - this->_put_if_not_stopped(format_log("A log entry from the logcat thread :D")); + LogEntry log_entry = {time(nullptr), "A log entry from the logcat thread :D"}; + this->_put_if_not_stopped(std::move(log_entry)); this->debug_log_request.clear(); } #endif diff --git a/logcat_thread.h b/logcat_thread.h index 25fc3ad..98069a9 100644 --- a/logcat_thread.h +++ b/logcat_thread.h @@ -5,7 +5,7 @@ #include "arb.h" #include "logcat_entry.h" -typedef std::variant LogcatThreadItem; +typedef std::variant LogcatThreadItem; #define NEWLINE_BUF_SIZE 512 * 1024 class LogcatThread { diff --git a/windows/logs.cpp b/windows/logs.cpp index db18aae..f77a76b 100644 --- a/windows/logs.cpp +++ b/windows/logs.cpp @@ -1,36 +1,47 @@ +#include #include #include "../log.h" #include "logs.h" -static inline void logs_scrolling_region(ImFont* monospace_font, bool* autoscrolling) { - ImGui::PushFont(monospace_font); +static inline void render_table(ImFont* monospace_font, bool* autoscrolling) { ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableSetupColumn("Time", ImGuiTableColumnFlags_None); + ImGui::TableSetupColumn("Message", ImGuiTableColumnFlags_None); + ImGui::TableHeadersRow(); + + ImGui::PushFont(monospace_font); + ImGuiListClipper clipper; - clipper.Begin(static_cast(log_entry_line_offsets.size())); + clipper.Begin(static_cast(log_entries.size())); while (clipper.Step()) { for (int i_u = clipper.DisplayStart; i_u < clipper.DisplayEnd; i_u++) { // what'd we do if we log the error about an error failing to show logs assert(i_u >= 0); size_t i = static_cast(i_u); - const char* start_offset = &log_entries[log_entry_line_offsets[i]]; - const char* end_offset = log_entry_line_offsets.size() > i + 1 - ? &log_entries[log_entry_line_offsets[i + 1] - 1] - : &log_entries[log_entries.size()]; - ImGui::TextUnformatted(start_offset, end_offset); + const LogEntry* log_entry = &log_entries[i]; + char time_as_str[128] = {0}; + strftime(time_as_str, 127 * sizeof(char), "%c", localtime(&log_entry->time)); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); ImGui::TextUnformatted(time_as_str); + ImGui::TableSetColumnIndex(1); ImGui::TextUnformatted(log_entry->message.data(), &log_entry->message.data()[log_entry->message.size()]); } } clipper.End(); - ImGui::PopStyleVar(); ImGui::PopFont(); + ImGui::PopStyleVar(); if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) { *autoscrolling = true; ImGui::SetScrollHereY(1.0f); } + + ImGui::EndTable(); } void logs_window(ImFont* monospace_font, bool* autoscrolling, bool* p_open) { @@ -41,20 +52,27 @@ void logs_window(ImFont* monospace_font, bool* autoscrolling, bool* p_open) { if (ImGui::Button("Clear")) { log_entries.clear(); - log_entry_line_offsets = {0}; } ImGui::SameLine(); if (ImGui::Button("Copy")) { - ImGui::SetClipboardText(log_entries.c_str()); + std::string text; + for (const LogEntry& entry : log_entries) { + if (!text.empty()) { + text += '\n'; + } + text += format_log(entry); + } + ImGui::SetClipboardText(text.c_str()); } ImGui::Separator(); // copied from imgui/imgui_demo.cpp: [SECTION] Example App: Debug Console / ShowExampleAppConsole() // and [SECTION] Example App: Long Text / ShowExampleAppLongText() // and [SECTION] Example App: Debug Log / ShowExampleAppLog() - if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar)) { - logs_scrolling_region(monospace_font, autoscrolling); + // and Tables/Vertical scrolling, with clipping + const constexpr ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_ScrollX | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable; + if (ImGui::BeginTable("logs", 2, flags)) { + render_table(monospace_font, autoscrolling); } - ImGui::EndChild(); ImGui::End(); }