Skip to content

Commit bd2a4ce

Browse files
Gin890Mysticial
authored andcommitted
create global UI-free logger, FileLogger, to replace the old FileWindowLogger. (PokemonAutomation#1063)
Separate UI code with file logger code by replacing UI-connected FileWindowLogger with a UI-free FileLogger. Co-authored-by: Alexander Yee <a-yee@u.northwestern.edu>
1 parent 134fe69 commit bd2a4ce

11 files changed

Lines changed: 123 additions & 133 deletions

File tree

Common/Cpp/Logging/FileLogger.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ struct FileLoggerConfig{
3232
};
3333

3434

35-
// A Qt-free file logger that:
35+
// A file logger that:
3636
// 1. Writes log messages to a file asynchronously via a background thread
3737
// 2. Supports log rotation when the file exceeds a configured size
3838
// 3. Notifies registered listeners when a log message is written (for UI integration)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/* Global Logger
2+
*
3+
* From: https://github.com/PokemonAutomation/
4+
*
5+
*/
6+
7+
#include "Common/Cpp/Concurrency/Backends/ThreadPool_Default.h"
8+
#include "GlobalLogger.h"
9+
10+
namespace PokemonAutomation{
11+
12+
13+
// We should define this function at Main.cpp of each executable
14+
// that uses the Logging/ library.
15+
//
16+
// This function is required by Common/Cpp/Logging/GlobalLogger.h:global_logger_raw() to initialize
17+
// the global file logger.
18+
// This function is called the first time `global_logger_raw()` is called to initialize the static
19+
// local global file logger object.
20+
//
21+
// The advantage of such design is that:
22+
// - This forces each executable's main.cpp to define how the logger is configured.
23+
// - This ensures thread-safety on the global logger object. The static local `FileLogger` defined
24+
// in `global_logger_raw()` is guaranteed by C++ to be thread-safe when constructed.
25+
FileLoggerConfig make_global_config();
26+
27+
28+
Logger& global_logger_raw(){
29+
// Call a function `make_global_config()` that is not defined in
30+
// this Logging/ library! To use this global logger, you must
31+
// define `make_global_config()` in the Main.cpp.
32+
static ThreadPool_Default thread_pool(nullptr, 1);
33+
static FileLogger logger(thread_pool, make_global_config());
34+
return logger;
35+
}
36+
37+
38+
}

Common/Cpp/Logging/GlobalLogger.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/* Global Logger
2+
*
3+
* From: https://github.com/PokemonAutomation/
4+
*
5+
* Provides a global file logger instance for application-wide logging.
6+
* This is a Qt-free logger that can be used before Qt is initialized.
7+
*/
8+
9+
#ifndef PokemonAutomation_Logging_GlobalLogger_H
10+
#define PokemonAutomation_Logging_GlobalLogger_H
11+
12+
#include "FileLogger.h"
13+
14+
namespace PokemonAutomation{
15+
16+
17+
// Return a global raw `FileLogger`. `FileLogger` is defined in FileLogger.h.
18+
// "raw" here means the logger does not add any timestamp or tags to the incoming log lines.
19+
//
20+
// To initialize the global logger, implement `FileLoggerConfig make_global_config()` at
21+
// Main.cpp to return the config for the global logger. If this function is not implemented,
22+
// the program will not compile.
23+
Logger& global_logger_raw();
24+
25+
26+
}
27+
#endif

SerialPrograms/Source/CommandLine/CommandLine_Main.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,20 @@ using namespace PokemonAutomation;
1818
using namespace PokemonAutomation::NintendoSwitch;
1919

2020
namespace PokemonAutomation{
21-
bool USE_QT_UI = false;
21+
22+
bool USE_QT_UI = false;
23+
24+
// This function is required by Common/Cpp/Logging/GlobalLogger.h:global_logger_raw() to initialize
25+
// the global file logger.
26+
// This function is called the first time `global_logger_raw()` is called to initialize the static
27+
// local global file logger object.
28+
FileLoggerConfig make_global_config(){
29+
return FileLoggerConfig{
30+
.file_path = "./SerialProgramsCommandLine.log"
31+
};
32+
}
33+
34+
2235
}
2336

2437
int main(int argc, char* argv[]){

SerialPrograms/Source/CommonFramework/Logging/FileWindowLogger.cpp

Lines changed: 8 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -7,92 +7,25 @@
77
#include <QCoreApplication>
88
#include <QMenuBar>
99
#include <QDir>
10+
#include "Common/Cpp/Logging/GlobalLogger.h"
1011
#include "CommonFramework/Globals.h"
1112
#include "CommonFramework/GlobalSettingsPanel.h"
1213
#include "CommonFramework/Windows/DpiScaler.h"
1314
#include "CommonFramework/Windows/WindowTracker.h"
1415
#include "CommonFramework/Windows/MainWindow.h"
1516
#include "CommonFramework/Options/ResolutionOption.h"
16-
#include "CommonFramework/Tools/GlobalThreadPools.h"
1717
#include "FileWindowLogger.h"
1818

19-
#include <iostream>
20-
using std::cout;
21-
using std::endl;
22-
2319
namespace PokemonAutomation{
2420

2521

26-
Logger& global_logger_raw(){
27-
auto get_log_filepath = [&](){
28-
QString application_name(QCoreApplication::applicationName());
29-
if (application_name.size() == 0){
30-
application_name = "SerialPrograms";
31-
}
32-
return USER_FILE_PATH() + (application_name + ".log").toStdString();
33-
};
34-
35-
static FileWindowLogger logger(get_log_filepath(), LOG_HISTORY_LINES);
36-
return logger;
37-
}
38-
FileWindowLogger::~FileWindowLogger(){
39-
stop();
40-
m_file_logger.remove_listener(*this);
41-
}
42-
void FileWindowLogger::stop(){
43-
m_file_logger.stop();
44-
}
45-
46-
47-
FileWindowLogger::FileWindowLogger(const std::string& path, size_t max_queue_size)
48-
: m_file_logger(
49-
GlobalThreadPools::unlimited_normal(),
50-
FileLoggerConfig{
51-
.file_path = path,
52-
.max_queue_size = max_queue_size,
53-
.max_file_size_bytes = 50 * 1024 * 1024, // 50MB
54-
.last_log_max_lines = max_queue_size,
55-
}
56-
)
57-
{
58-
m_file_logger.add_listener(*this);
59-
}
60-
61-
void FileWindowLogger::operator+=(FileWindowLoggerWindow& widget){
62-
std::lock_guard<Mutex> lg(m_window_lock);
63-
m_windows.insert(&widget);
64-
}
65-
66-
void FileWindowLogger::operator-=(FileWindowLoggerWindow& widget){
67-
std::lock_guard<Mutex> lg(m_window_lock);
68-
m_windows.erase(&widget);
69-
}
70-
71-
void FileWindowLogger::log(const std::string& msg, Color color){
72-
m_file_logger.log(msg, color);
73-
}
74-
75-
void FileWindowLogger::log(std::string&& msg, Color color){
76-
m_file_logger.log(std::move(msg), color);
77-
}
78-
79-
std::vector<std::string> FileWindowLogger::get_last() const{
80-
return m_file_logger.get_last();
81-
}
82-
83-
void FileWindowLogger::on_log(const std::string& msg, Color color){
22+
void FileWindowLoggerWindow::on_log(const std::string& msg, Color color){
8423
// This is called from FileLogger's background thread.
8524
// Format the message for Qt display and send to all windows.
86-
std::lock_guard<Mutex> lg(m_window_lock);
87-
if (!m_windows.empty()){
88-
QString str = to_window_str(msg, color);
89-
for (FileWindowLoggerWindow* window : m_windows){
90-
window->log(str);
91-
}
92-
}
25+
emit signal_log(to_window_str(msg, color));
9326
}
9427

95-
QString FileWindowLogger::to_window_str(const std::string& msg, Color color){
28+
QString FileWindowLoggerWindow::to_window_str(const std::string& msg, Color color){
9629
// Convert message to HTML for display in QTextEdit.
9730
// Replace spaces with &nbsp; and newlines with <br>.
9831
std::string str;
@@ -118,10 +51,11 @@ QString FileWindowLogger::to_window_str(const std::string& msg, Color color){
11851
}
11952

12053

121-
FileWindowLoggerWindow::FileWindowLoggerWindow(FileWindowLogger& logger, QWidget* parent)
54+
FileWindowLoggerWindow::FileWindowLoggerWindow(QWidget* parent)
12255
: QMainWindow(parent)
123-
, m_logger(logger)
56+
, m_logger(dynamic_cast<FileLogger&>(global_logger_raw()))
12457
{
58+
m_logger.add_listener(*this);
12559
if (objectName().isEmpty()){
12660
setObjectName(QString::fromUtf8("TextWindow"));
12761
}
@@ -156,7 +90,6 @@ FileWindowLoggerWindow::FileWindowLoggerWindow(FileWindowLogger& logger, QWidget
15690
GlobalSettings::instance().LOG_WINDOW_SIZE->X_POS.add_listener(*this);
15791
GlobalSettings::instance().LOG_WINDOW_SIZE->Y_POS.add_listener(*this);
15892

159-
m_logger += *this;
16093
log("================================================================================");
16194
log("<b>Window Startup...</b>");
16295
log("Current path: " + QDir::currentPath());
@@ -168,7 +101,7 @@ FileWindowLoggerWindow::FileWindowLoggerWindow(FileWindowLogger& logger, QWidget
168101

169102
FileWindowLoggerWindow::~FileWindowLoggerWindow(){
170103
remove_window(*this);
171-
m_logger -= *this;
104+
m_logger.remove_listener(*this);
172105
GlobalSettings::instance().LOG_WINDOW_SIZE->WIDTH.remove_listener(*this);
173106
GlobalSettings::instance().LOG_WINDOW_SIZE->HEIGHT.remove_listener(*this);
174107
GlobalSettings::instance().LOG_WINDOW_SIZE->X_POS.remove_listener(*this);

SerialPrograms/Source/CommonFramework/Logging/FileWindowLogger.h

Lines changed: 11 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -9,71 +9,32 @@
99
#ifndef PokemonAutomation_Logging_FileWindowLogger_H
1010
#define PokemonAutomation_Logging_FileWindowLogger_H
1111

12-
#include <set>
1312
#include <QTextEdit>
1413
#include <QMainWindow>
15-
#include "Common/Cpp/Concurrency/Mutex.h"
1614
#include "Common/Cpp/Logging/FileLogger.h"
1715
#include "Common/Cpp/Options/ConfigOption.h"
1816

1917
namespace PokemonAutomation{
2018

21-
class FileWindowLoggerWindow;
22-
23-
24-
// A logger that writes to a file (via FileLogger) and can also display
25-
// log messages in Qt GUI windows (FileWindowLoggerWindow).
26-
//
27-
// This class acts as a thin Qt wrapper around the Qt-free FileLogger,
28-
// adding the ability to manage multiple Qt windows that display log output.
29-
class FileWindowLogger : public Logger, private FileLogger::Listener{
30-
public:
31-
// Construct a FileWindowLogger that writes to the given file path.
32-
// The max_queue_size parameter controls how many log messages can be
33-
// queued before the log() call blocks.
34-
FileWindowLogger(const std::string& path, size_t max_queue_size);
35-
36-
~FileWindowLogger();
37-
void stop();
38-
39-
// Add/remove Qt windows that will display log messages.
40-
void operator+=(FileWindowLoggerWindow& widget);
41-
void operator-=(FileWindowLoggerWindow& widget);
42-
43-
// Logger interface implementation - forwards to FileLogger.
44-
virtual void log(const std::string& msg, Color color = Color()) override;
45-
virtual void log(std::string&& msg, Color color = Color()) override;
46-
virtual std::vector<std::string> get_last() const override;
47-
48-
private:
49-
// FileLogger::Listener implementation - called when a message is logged.
50-
// Formats the message for Qt display and sends to all registered windows.
51-
virtual void on_log(const std::string& msg, Color color) override;
52-
53-
// Convert a log message to HTML for display in QTextEdit.
54-
static QString to_window_str(const std::string& msg, Color color);
55-
56-
private:
57-
FileLogger m_file_logger;
58-
59-
Mutex m_window_lock;
60-
std::set<FileWindowLoggerWindow*> m_windows;
61-
};
62-
6319

6420
// A Qt window that displays log output from a FileWindowLogger.
6521
// Uses Qt signals/slots for thread-safe updates from the logger's background thread.
66-
class FileWindowLoggerWindow : public QMainWindow, public ConfigOption::Listener{
22+
class FileWindowLoggerWindow : public QMainWindow, public ConfigOption::Listener, public FileLogger::Listener{
6723
Q_OBJECT
6824

6925
public:
70-
FileWindowLoggerWindow(FileWindowLogger& logger, QWidget* parent = nullptr);
26+
FileWindowLoggerWindow(QWidget* parent = nullptr);
7127
virtual ~FileWindowLoggerWindow();
7228

7329
// Called by FileWindowLogger to display a log message.
74-
// Thread-safe: emits a signal that is handled on the UI thread.
30+
7531
void log(QString msg);
7632

33+
// Callback function registered to the global logger.
34+
// The global logger's background thread call it to display a log to the window.
35+
// Thread-safe: emits a signal that is handled on the UI thread.
36+
void on_log(const std::string& msg, Color color) override;
37+
7738
virtual void resizeEvent(QResizeEvent* event) override;
7839
virtual void moveEvent(QMoveEvent* event) override;
7940

@@ -83,7 +44,9 @@ class FileWindowLoggerWindow : public QMainWindow, public ConfigOption::Listener
8344
private:
8445
virtual void on_config_value_changed(void* object) override;
8546

86-
FileWindowLogger& m_logger;
47+
static QString to_window_str(const std::string& msg, Color color);
48+
49+
FileLogger& m_logger;
8750
QMenuBar* m_menubar;
8851
QTextEdit* m_text;
8952
bool m_pending_resize = false;

SerialPrograms/Source/CommonFramework/Logging/Logger.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <iostream>
88
#include "Common/Cpp/Logging/TaggedLogger.h"
9+
#include "Common/Cpp/Logging/GlobalLogger.h"
910
#include "Logger.h"
1011

1112
namespace PokemonAutomation{

SerialPrograms/Source/CommonFramework/Logging/Logger.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,11 @@
1010
#define PokemonAutomation_Logging_Logger_H
1111

1212
#include "Common/Cpp/Logging/AbstractLogger.h"
13+
#include "Common/Cpp/Logging/GlobalLogger.h"
1314

1415
namespace PokemonAutomation{
1516

1617

17-
// The base logger for the application. Use this to build other loggers.
18-
// Its implementation is defined in FileWindowLogger.cpp, writing each input
19-
// log into a log file named "<USER_FILE_PATH()>/<ApplicationName>.log".
20-
// It prints each input log string as is with no tag or timestamp.
21-
Logger& global_logger_raw();
22-
2318
// This logger wraps around `global_logger_raw()` to print each log with a
2419
// timestamp and a default tag "Global". Use this logger directly in the
2520
// application codebase.

SerialPrograms/Source/CommonFramework/Main.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#include "Integrations/DppIntegration/DppClient.h"
3030
#include "Logging/Logger.h"
3131
#include "Logging/OutputRedirector.h"
32+
#include "Common/Cpp/Logging/FileLogger.h"
33+
#include "Common/Cpp/Logging/GlobalLogger.h"
3234
#include "Logging/FileWindowLogger.h"
3335
//#include "Tools/StatsDatabase.h"
3436
//#include "Windows/DpiScaler.h"
@@ -64,6 +66,22 @@ void set_working_directory(){
6466
}
6567
}
6668

69+
namespace PokemonAutomation{
70+
71+
// This function is required by Common/Cpp/Logging/GlobalLogger.h:global_logger_raw() to initialize
72+
// the global file logger.
73+
// This function is called the first time `global_logger_raw()` is called to initialize the static
74+
// local global file logger object.
75+
// Note: in order to make sure `USER_FILE_PATH()` and `QCoreApplication::applicationName()` work
76+
// correctly you need to define `QApplication` before `make_global_config()` is called.
77+
FileLoggerConfig make_global_config(){
78+
return FileLoggerConfig{
79+
.file_path = USER_FILE_PATH() + QCoreApplication::applicationName().toStdString() + ".log",
80+
};
81+
}
82+
83+
}
84+
6785

6886

6987
int run_program(int argc, char *argv[]){
@@ -239,7 +257,7 @@ int main(int argc, char *argv[]){
239257
SystemSleepController::instance().stop();
240258
global_periodic_runner().stop();
241259
global_watchdog().stop();
242-
static_cast<FileWindowLogger&>(global_logger_raw()).stop();
260+
dynamic_cast<FileLogger&>(global_logger_raw()).stop();
243261

244262
//
245263
// Workaround Qt 6.9 thread-adoption bug on Windows.

SerialPrograms/Source/CommonFramework/Windows/MainWindow.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ MainWindow::MainWindow(QWidget* parent)
219219
);
220220
}
221221
{
222-
m_output_window.reset(new FileWindowLoggerWindow((FileWindowLogger&)global_logger_raw()));
222+
m_output_window.reset(new FileWindowLoggerWindow);
223223
QPushButton* output = new QPushButton("Output Window", support_box);
224224
buttons->addWidget(output);
225225
connect(

0 commit comments

Comments
 (0)