Table-ify logs
This commit is contained in:
parent
fbf717db33
commit
75d62a0031
|
@ -18,8 +18,8 @@ static inline void check_for_logcat_items(LogcatThread& logcat_thread, const Fil
|
||||||
LogcatThreadItem* logcat_thread_item;
|
LogcatThreadItem* logcat_thread_item;
|
||||||
|
|
||||||
while ((logcat_thread_item = logcat_thread.atomic_ring_buffer.get())) {
|
while ((logcat_thread_item = logcat_thread.atomic_ring_buffer.get())) {
|
||||||
if (std::holds_alternative<std::string>(*logcat_thread_item)) {
|
if (std::holds_alternative<LogEntry>(*logcat_thread_item)) {
|
||||||
log_raw(std::move(std::get<std::string>(*logcat_thread_item)), false);
|
log(std::move(std::get<LogEntry>(*logcat_thread_item)), false);
|
||||||
} else if (std::holds_alternative<LogcatEntry>(*logcat_thread_item)) {
|
} else if (std::holds_alternative<LogcatEntry>(*logcat_thread_item)) {
|
||||||
logcat_entries.push_back(std::move(std::get<LogcatEntry>(*logcat_thread_item)));
|
logcat_entries.push_back(std::move(std::get<LogcatEntry>(*logcat_thread_item)));
|
||||||
if (matches(filters, logcat_entries.back())) {
|
if (matches(filters, logcat_entries.back())) {
|
||||||
|
|
49
log.cpp
49
log.cpp
|
@ -1,44 +1,39 @@
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <exception>
|
|
||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
std::string log_entries;
|
std::vector<LogEntry> log_entries;
|
||||||
std::vector<size_t> log_entry_line_offsets = {0};
|
|
||||||
|
|
||||||
std::string format_log(std::string entry, time_t time) {
|
LogEntry::LogEntry(time_t time_, std::string message_) : time(time_), message(std::move(message_)) {
|
||||||
size_t newline_pos;
|
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};
|
char time_as_str[128] = {0};
|
||||||
|
strftime(time_as_str, 127 * sizeof(char), "%c", localtime(&entry.time));
|
||||||
strftime(time_as_str, 127 * sizeof(char), "%c", localtime(&time));
|
return std::string(1, '[') + time_as_str + "] " + entry.message;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string format_log(std::string entry) {
|
void print_log(const LogEntry& entry) {
|
||||||
return format_log(std::move(entry), time(nullptr));
|
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) {
|
if (print) {
|
||||||
printf("%s\n", line.c_str());
|
print_log(entry);
|
||||||
}
|
}
|
||||||
if (!log_entries.empty()) {
|
log_entries.push_back(std::move(entry));
|
||||||
log_entries += '\n';
|
|
||||||
log_entry_line_offsets.push_back(log_entries.size());
|
|
||||||
}
|
|
||||||
log_entries += std::move(line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void log(std::string entry, time_t time) {
|
void log(std::string message) {
|
||||||
log_raw(format_log(std::move(entry), time));
|
log({time(nullptr), std::move(message)});
|
||||||
}
|
|
||||||
|
|
||||||
void log(std::string entry) {
|
|
||||||
log_raw(format_log(std::move(entry)));
|
|
||||||
}
|
}
|
||||||
|
|
18
log.h
18
log.h
|
@ -2,13 +2,17 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <exception>
|
|
||||||
|
|
||||||
extern std::string log_entries;
|
struct LogEntry {
|
||||||
extern std::vector<size_t> log_entry_line_offsets;
|
time_t time;
|
||||||
|
std::string message;
|
||||||
|
|
||||||
std::string format_log(std::string entry, time_t time);
|
LogEntry() = default;
|
||||||
std::string format_log(std::string entry);
|
LogEntry(time_t time_, std::string message_);
|
||||||
void log_raw(std::string line, bool print = true);
|
};
|
||||||
void log(std::string entry, time_t time);
|
extern std::vector<LogEntry> 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);
|
void log(std::string entry);
|
||||||
|
|
|
@ -168,8 +168,8 @@ void LogcatThread::_put_if_not_stopped(LogcatThreadItem item) {
|
||||||
|
|
||||||
void LogcatThread::_handle_line(char* buf, size_t length, bool is_stdout) {
|
void LogcatThread::_handle_line(char* buf, size_t length, bool is_stdout) {
|
||||||
if (!is_stdout) {
|
if (!is_stdout) {
|
||||||
std::string log_entry = format_log(std::string("Received from logcat stderr: ") + std::string(buf, length));
|
LogEntry log_entry = {time(nullptr), std::string("Received from logcat stderr: ") + std::string(buf, length)};
|
||||||
printf("%s\n", log_entry.c_str());
|
print_log(log_entry);
|
||||||
this->_put_if_not_stopped(std::move(log_entry));
|
this->_put_if_not_stopped(std::move(log_entry));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -178,8 +178,8 @@ void LogcatThread::_handle_line(char* buf, size_t length, bool is_stdout) {
|
||||||
try {
|
try {
|
||||||
logcat_entry = try_parse_logcat_entry(buf, length, this->_current_buffer);
|
logcat_entry = try_parse_logcat_entry(buf, length, this->_current_buffer);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
std::string log_entry = format_log(std::string("Failed to parse logcat entry: ") + e.what());
|
LogEntry log_entry = {time(nullptr), std::string("Failed to parse logcat entry: ") + e.what()};
|
||||||
printf("%s\n", log_entry.c_str());
|
print_log(log_entry);
|
||||||
this->_put_if_not_stopped(std::move(log_entry));
|
this->_put_if_not_stopped(std::move(log_entry));
|
||||||
}
|
}
|
||||||
if (logcat_entry) {
|
if (logcat_entry) {
|
||||||
|
@ -190,8 +190,8 @@ void LogcatThread::_handle_line(char* buf, size_t length, bool is_stdout) {
|
||||||
try {
|
try {
|
||||||
new_buffer = try_parse_buffer(buf, length);
|
new_buffer = try_parse_buffer(buf, length);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
std::string log_entry = format_log(std::string("Failed to parse buffer line: ") + e.what());
|
LogEntry log_entry = {time(nullptr), std::string("Failed to parse buffer line: ") + e.what()};
|
||||||
printf("%s\n", log_entry.c_str());
|
print_log(log_entry);
|
||||||
this->_put_if_not_stopped(std::move(log_entry));
|
this->_put_if_not_stopped(std::move(log_entry));
|
||||||
}
|
}
|
||||||
if (new_buffer) {
|
if (new_buffer) {
|
||||||
|
@ -199,8 +199,8 @@ void LogcatThread::_handle_line(char* buf, size_t length, bool is_stdout) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string log_entry = format_log(std::string("Cannot parse logcat stdout: ") + std::string(buf, length));
|
LogEntry log_entry = {time(nullptr), std::string("Cannot parse logcat stdout: ") + std::string(buf, length)};
|
||||||
printf("%s\n", log_entry.c_str());
|
print_log(log_entry);
|
||||||
this->_put_if_not_stopped(std::move(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);
|
int ready_fds = epoll_wait(this->_epoll_fd, events, EPOLL_MAX_EVENTS, 1000);
|
||||||
if (ready_fds == -1) {
|
if (ready_fds == -1) {
|
||||||
std::string log_entry = format_log(std::string("epoll_wait(): ") + strerror(errno));
|
LogEntry log_entry = {time(nullptr), std::string("epoll_wait(): ") + strerror(errno)};
|
||||||
printf("%s\n", log_entry.c_str());
|
print_log(log_entry);
|
||||||
this->_put_if_not_stopped(std::move(log_entry));
|
this->_put_if_not_stopped(std::move(log_entry));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -223,11 +223,11 @@ void LogcatThread::_run_epoll_round() {
|
||||||
try {
|
try {
|
||||||
handle_fd(events[i].data.fd, buf, used, &LogcatThread::_handle_line, this, is_stdout);
|
handle_fd(events[i].data.fd, buf, used, &LogcatThread::_handle_line, this, is_stdout);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
std::string log_entry = "Failed to handle std";
|
std::string message = "Failed to handle std";
|
||||||
log_entry += is_stdout ? "out: " : "err: ";
|
message += is_stdout ? "out: " : "err: ";
|
||||||
log_entry += e.what();
|
message += e.what();
|
||||||
log_entry = format_log(std::move(log_entry));
|
LogEntry log_entry = {time(nullptr), std::move(message)};
|
||||||
printf("%s\n", log_entry.c_str());
|
print_log(log_entry);
|
||||||
this->_put_if_not_stopped(std::move(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()) {
|
while (!stoken.stop_requested()) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (this->debug_log_request.test()) {
|
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();
|
this->debug_log_request.clear();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include "arb.h"
|
#include "arb.h"
|
||||||
#include "logcat_entry.h"
|
#include "logcat_entry.h"
|
||||||
|
|
||||||
typedef std::variant<std::string, LogcatEntry> LogcatThreadItem;
|
typedef std::variant<LogEntry, LogcatEntry> LogcatThreadItem;
|
||||||
#define NEWLINE_BUF_SIZE 512 * 1024
|
#define NEWLINE_BUF_SIZE 512 * 1024
|
||||||
|
|
||||||
class LogcatThread {
|
class LogcatThread {
|
||||||
|
|
|
@ -1,36 +1,47 @@
|
||||||
|
#include <ctime>
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
||||||
#include "../log.h"
|
#include "../log.h"
|
||||||
#include "logs.h"
|
#include "logs.h"
|
||||||
|
|
||||||
static inline void logs_scrolling_region(ImFont* monospace_font, bool* autoscrolling) {
|
static inline void render_table(ImFont* monospace_font, bool* autoscrolling) {
|
||||||
ImGui::PushFont(monospace_font);
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
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;
|
ImGuiListClipper clipper;
|
||||||
clipper.Begin(static_cast<int>(log_entry_line_offsets.size()));
|
clipper.Begin(static_cast<int>(log_entries.size()));
|
||||||
while (clipper.Step()) {
|
while (clipper.Step()) {
|
||||||
for (int i_u = clipper.DisplayStart; i_u < clipper.DisplayEnd; i_u++) {
|
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
|
// what'd we do if we log the error about an error failing to show logs
|
||||||
assert(i_u >= 0);
|
assert(i_u >= 0);
|
||||||
size_t i = static_cast<size_t>(i_u);
|
size_t i = static_cast<size_t>(i_u);
|
||||||
|
|
||||||
const char* start_offset = &log_entries[log_entry_line_offsets[i]];
|
const LogEntry* log_entry = &log_entries[i];
|
||||||
const char* end_offset = log_entry_line_offsets.size() > i + 1
|
char time_as_str[128] = {0};
|
||||||
? &log_entries[log_entry_line_offsets[i + 1] - 1]
|
strftime(time_as_str, 127 * sizeof(char), "%c", localtime(&log_entry->time));
|
||||||
: &log_entries[log_entries.size()];
|
|
||||||
ImGui::TextUnformatted(start_offset, end_offset);
|
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();
|
clipper.End();
|
||||||
|
|
||||||
ImGui::PopStyleVar();
|
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
|
||||||
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) {
|
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) {
|
||||||
*autoscrolling = true;
|
*autoscrolling = true;
|
||||||
ImGui::SetScrollHereY(1.0f);
|
ImGui::SetScrollHereY(1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
void logs_window(ImFont* monospace_font, bool* autoscrolling, bool* p_open) {
|
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")) {
|
if (ImGui::Button("Clear")) {
|
||||||
log_entries.clear();
|
log_entries.clear();
|
||||||
log_entry_line_offsets = {0};
|
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("Copy")) {
|
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();
|
ImGui::Separator();
|
||||||
// copied from imgui/imgui_demo.cpp: [SECTION] Example App: Debug Console / ShowExampleAppConsole()
|
// copied from imgui/imgui_demo.cpp: [SECTION] Example App: Debug Console / ShowExampleAppConsole()
|
||||||
// and [SECTION] Example App: Long Text / ShowExampleAppLongText()
|
// and [SECTION] Example App: Long Text / ShowExampleAppLongText()
|
||||||
// and [SECTION] Example App: Debug Log / ShowExampleAppLog()
|
// and [SECTION] Example App: Debug Log / ShowExampleAppLog()
|
||||||
if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar)) {
|
// and Tables/Vertical scrolling, with clipping
|
||||||
logs_scrolling_region(monospace_font, autoscrolling);
|
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();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue