Add logging

This commit is contained in:
blankie 2023-01-05 17:27:14 +07:00
parent f1c2dc7f63
commit 3bf47f3a5c
Signed by: blankie
GPG Key ID: CC15FC822C7F61F5
6 changed files with 162 additions and 39 deletions

View File

@ -12,19 +12,20 @@
# #
#CXX = g++ #CXX = g++
#CXX = clang++ CXX = clang++
EXE = logmeow EXE = logmeow
IMGUI_DIR = imgui IMGUI_DIR = imgui
SOURCES = main.cpp config.cpp event_loop.cpp SOURCES = main.cpp log.cpp config.cpp event_loop.cpp
SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
SOURCES += $(IMGUI_DIR)/misc/cpp/imgui_stdlib.cpp $(IMGUI_DIR)/backends/imgui_impl_sdl.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl3.cpp SOURCES += $(IMGUI_DIR)/misc/cpp/imgui_stdlib.cpp $(IMGUI_DIR)/backends/imgui_impl_sdl.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl3.cpp
OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES)))) OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES))))
UNAME_S := $(shell uname -s) UNAME_S := $(shell uname -s)
LINUX_GL_LIBS = -lGL LINUX_GL_LIBS = -lGL
CXXFLAGS = -std=c++11 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/misc/cpp -I$(IMGUI_DIR)/backends CXXFLAGS = -std=c++14 -I$(IMGUI_DIR) -I$(IMGUI_DIR)/misc/cpp -I$(IMGUI_DIR)/backends
CXXFLAGS += -g -Wall -Wformat # https://t.me/NightShadowsHangout/670691
CXXFLAGS += -g -Werror -Wall -Wextra -Wshadow -Wpedantic -Wno-gnu-anonymous-struct -fno-rtti -fPIC -Wconversion -Wno-unused-parameter -Wimplicit-fallthrough
LIBS = LIBS =
##--------------------------------------------------------------------- ##---------------------------------------------------------------------
@ -85,7 +86,7 @@ endif
$(CXX) $(CXXFLAGS) -c -o $@ $< $(CXX) $(CXXFLAGS) -c -o $@ $<
$(EXE): $(OBJS) $(EXE): $(OBJS)
$(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS) $(CXX) -o $@ $^ $(CXXFLAGS) -fuse-ld=lld $(LIBS)
clean: clean:
rm -f $(EXE) $(OBJS) rm -f $(EXE) $(OBJS)

View File

@ -6,6 +6,7 @@
#include <memory> #include <memory>
#include <system_error> #include <system_error>
#include "log.h"
#include "config.h" #include "config.h"
static FILE* fopen_or_raise(const char* path, const char* mode, bool ignore_enoent) { static FILE* fopen_or_raise(const char* path, const char* mode, bool ignore_enoent) {
@ -13,14 +14,15 @@ static FILE* fopen_or_raise(const char* path, const char* mode, bool ignore_enoe
if (file || (ignore_enoent && errno == ENOENT)) { if (file || (ignore_enoent && errno == ENOENT)) {
return file; return file;
} }
throw std::system_error(errno, std::generic_category(), "fopen()"); throw std::system_error(errno, std::generic_category(), std::string("fopen(") + quote(path) + ')');
} }
static void fclose_and_log(FILE* file) { static void fclose_and_log(FILE* file) {
if (!fclose(file)) { if (!fclose(file)) {
return; return;
} }
// TODO log() when logging exists std::system_error e = std::system_error(errno, std::generic_category(), "fclose()");
log("Failed to close a file", std::move(e));
} }
static bool write(const std::string& ptr, FILE* file) { static bool write(const std::string& ptr, FILE* file) {
@ -52,21 +54,23 @@ std::string get_config_file_path() {
// tries to create a directory with an empty string if it's just "/" but who cares // tries to create a directory with an empty string if it's just "/" but who cares
void create_config_folders_if_necessary() { void create_config_folders_if_necessary() {
std::string path = get_config_folder(); std::string full_path = get_config_folder();
std::string path;
size_t pos = 0; size_t pos = 0;
while (pos != std::string::npos) { while (pos != std::string::npos) {
pos = path.find('/', pos); pos = full_path.find('/', pos);
if (pos != std::string::npos) { if (pos != std::string::npos) {
pos++; pos++;
} }
if (!mkdir(path.substr(0, pos).c_str(), 0600)) { path = full_path.substr(0, pos);
if (!mkdir(path.c_str(), 0600)) {
continue; continue;
} }
if (errno == EEXIST) { if (errno == EEXIST) {
continue; continue;
} }
throw std::system_error(errno, std::generic_category(), "mkdir()"); throw std::system_error(errno, std::generic_category(), std::string("mkdir(") + quote(path) + ')');
} }
} }
@ -87,7 +91,7 @@ static inline Config load_config(FILE* file) {
if (errsv == ENOMEM) { if (errsv == ENOMEM) {
throw std::bad_alloc(); throw std::bad_alloc();
} else if (errsv != 0) { } else if (errsv != 0) {
throw std::system_error(errsv, std::generic_category(), "reading config line"); throw std::system_error(errsv, std::generic_category(), "getline()");
} else { } else {
break; break;
} }
@ -129,13 +133,13 @@ static inline void write_config(FILE* file, const Config& config) {
"# # a comment, only available at the start of a line\n" "# # a comment, only available at the start of a line\n"
"# (an empty line, no whitespace)\n" "# (an empty line, no whitespace)\n"
"# key=value pairs, no spaces around the delimiter, and no unknown keys\n\n", file)) { "# key=value pairs, no spaces around the delimiter, and no unknown keys\n\n", file)) {
throw std::runtime_error("failed to write info comment"); throw std::runtime_error("Failed to write info comment");
} }
if (!write("logcat_command=", file)) { if (!write("logcat_command=", file)) {
throw std::runtime_error("failed to write logcat command key"); throw std::runtime_error("Failed to write logcat command key");
} }
if (!write(config.logcat_command, file)) { if (!write(config.logcat_command, file)) {
throw std::runtime_error("failed to write logcat command value"); throw std::runtime_error("Failed to write logcat command value");
} }
} }
@ -150,5 +154,6 @@ void write_config(const Config& config) {
if (!rename(tmp_config_file_path.c_str(), config_file_path.c_str())) { if (!rename(tmp_config_file_path.c_str(), config_file_path.c_str())) {
return; return;
} }
throw std::system_error(errno, std::generic_category(), "rename()"); throw std::system_error(errno, std::generic_category(),
std::string("rename(") + quote(tmp_config_file_path) + ", " + quote(config_file_path) + ')');
} }

View File

@ -1,31 +1,57 @@
#include <imgui.h> #include <imgui.h>
#include <imgui_stdlib.h> #include <imgui_stdlib.h>
#include "log.h"
#include "config.h" #include "config.h"
static inline void write_config_and_update_structures(const Config& config) { static inline void write_config_and_update_structures(const Config& config) {
try { try {
write_config(config); write_config(config);
} catch (const std::exception& e) { } catch (const std::exception& e) {
// TODO log() when logging exists log("Failed to write config", e);
throw; return;
} }
} }
static inline void settings_window(Config& config, float* config_write_timer, bool* show_settings_window) { static inline void settings_window(Config& config, float* config_write_timer, bool* show_settings_window) {
if (!ImGui::Begin("Settings", show_settings_window)) { if (!ImGui::Begin("Settings", show_settings_window)) {
ImGui::End(); ImGui::End();
return; return;
} }
// TODO actually have process control // TODO actually have process control
ImGui::Text("Logcat command is only modifiable when logcat is stopped"); ImGui::Text("Logcat command only takes effect when logcat is not running");
if (ImGui::InputTextWithHint("Logcat command", "adb logcat -Dv 'threadtime UTC epoch usec'", &config.logcat_command)) { if (ImGui::InputTextWithHint("Logcat command", "adb logcat -Dv 'threadtime UTC epoch usec'", &config.logcat_command)) {
*config_write_timer = *config_write_timer > 0.0f ? *config_write_timer : 5.0f; *config_write_timer = *config_write_timer > 0.0f ? *config_write_timer : 5.0f;
} }
ImGui::End(); ImGui::End();
} }
static inline void main_window(Config& active_config, bool* show_settings_window, bool* exit_requested_rev) { static inline void logs_window(bool* show_logs_window) {
if (!ImGui::Begin("LogMeow Logs", show_logs_window)) {
ImGui::End();
return;
}
if (ImGui::Button("Clear")) {
log_entries.clear();
}
ImGui::SameLine();
if (ImGui::Button("Copy")) {
ImGui::SetClipboardText(log_entries.c_str());
}
// copied from imgui/imgui_demo.cpp: [SECTION] Example App: Debug Console / ShowExampleAppConsole()
ImGui::Separator();
if (ImGui::BeginChild("ScrollingRegion", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar)) {
ImGui::TextUnformatted(log_entries.data(), &log_entries[log_entries.size()]);
}
ImGui::EndChild();
ImGui::End();
}
static inline void main_window(Config& active_config, bool* show_settings_window, bool* show_logs_window, bool* exit_requested_rev) {
if (!ImGui::Begin("LogMeow", exit_requested_rev)) { if (!ImGui::Begin("LogMeow", exit_requested_rev)) {
ImGui::End(); ImGui::End();
return; return;
@ -33,6 +59,31 @@ static inline void main_window(Config& active_config, bool* show_settings_window
if (ImGui::Button("Settings")) { if (ImGui::Button("Settings")) {
*show_settings_window = true; *show_settings_window = true;
} }
ImGui::SameLine();
if (ImGui::Button("Logs")) {
*show_logs_window = true;
}
ImGui::End();
}
static inline void debug_window() {
static bool show_demo_window = false;
static size_t add_log_entry_presses = 1;
if (show_demo_window) {
ImGui::ShowDemoWindow(&show_demo_window);
}
if (!ImGui::Begin("LogMeow Debug")) {
ImGui::End();
return;
}
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::Checkbox("Show Dear ImGui Demo Window", &show_demo_window);
ImGui::Separator();
if (ImGui::Button("Add Log Entry")) {
std::exception e;
log(std::string("Add log entry button pressed ") + std::to_string(add_log_entry_presses++) + " time(s)", e);
}
ImGui::End(); ImGui::End();
} }
@ -53,25 +104,13 @@ static inline void exit_modal_if_necessary(bool* run_event_loop) {
ImGui::EndPopup(); ImGui::EndPopup();
} }
static inline void debug_window() {
static bool show_demo_window = false;
if (show_demo_window) {
ImGui::ShowDemoWindow(&show_demo_window);
}
if (!ImGui::Begin("LogMeow Debug")) {
ImGui::End();
return;
}
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::Checkbox("Show Dear ImGui Demo Window", &show_demo_window);
ImGui::End();
}
void event_loop(Config& config, float* config_write_timer, bool* run_event_loop) { void event_loop(Config& config, float* config_write_timer, bool* run_event_loop) {
static bool show_settings_window = false; static bool show_settings_window = false;
static bool show_logs_window = false;
static bool exit_requested_rev = true; static bool exit_requested_rev = true;
debug_window();
if (show_settings_window) { if (show_settings_window) {
settings_window(config, config_write_timer, &show_settings_window); settings_window(config, config_write_timer, &show_settings_window);
} }
@ -82,11 +121,14 @@ void event_loop(Config& config, float* config_write_timer, bool* run_event_loop)
} }
} }
if (show_logs_window) {
logs_window(&show_logs_window);
}
if (!exit_requested_rev) { if (!exit_requested_rev) {
ImGui::OpenPopup("Exit?"); ImGui::OpenPopup("Exit?");
exit_requested_rev = true; exit_requested_rev = true;
} }
exit_modal_if_necessary(run_event_loop); exit_modal_if_necessary(run_event_loop);
main_window(config, &show_settings_window, &exit_requested_rev); main_window(config, &show_settings_window, &show_logs_window, &exit_requested_rev);
debug_window();
} }

63
log.cpp Normal file
View File

@ -0,0 +1,63 @@
#include <ctime>
#include <string>
#include <vector>
#include <iomanip>
#include <sstream>
#include <iostream>
#include <exception>
#include <system_error>
#include "log.h"
static time_t current_time() {
static bool no_time_warned = false;
struct timespec tp;
if (!clock_gettime(CLOCK_REALTIME, &tp)) {
return tp.tv_sec;
}
if (no_time_warned) {
return 0;
}
no_time_warned = true;
std::system_error e = std::system_error(errno, std::generic_category(), "clock_gettime()");
log("Failed to get current time", std::move(e));
return 0;
}
std::string log_entries;
void log(std::string entry, time_t time) {
size_t last_newline_pos = 0, newline_pos;
std::string line;
char time_as_str[128] = {0};
strftime(time_as_str, 127 * sizeof(char), "%c", localtime(&time));
do {
newline_pos = entry.find('\n', last_newline_pos);
line = '[';
line += time_as_str;
line += "] ";
line += entry.substr(last_newline_pos, newline_pos);
printf("%s\n", line.c_str());
if (!log_entries.empty()) {
log_entries += '\n';
}
log_entries += std::move(line);
last_newline_pos = newline_pos + 1;
} while (last_newline_pos);
}
void log(std::string action, const std::exception& e) {
log(action + ": " + e.what(), current_time());
}
std::string quote(const std::string& str) {
std::stringstream ss;
ss << std::quoted(str);
return ss.str();
}

10
log.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <string>
#include <vector>
#include <exception>
extern std::string log_entries;
void log(std::string action, const std::exception& e);
std::string quote(const std::string& str);

View File

@ -1,4 +1,5 @@
#include <cstdio> #include <cstdio>
#include <locale.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <imgui.h> #include <imgui.h>
@ -14,8 +15,9 @@
#include "config.h" #include "config.h"
#include "event_loop.h" #include "event_loop.h"
int main(int, char**) int main(int, char**) {
{ setlocale(LC_ALL, "");
Config config; Config config;
try { try {
config = load_config(); config = load_config();