--- nono/host/logger.cpp 2026/04/29 17:05:51 1.1 +++ nono/host/logger.cpp 2026/04/29 17:06:02 1.1.1.3 @@ -8,6 +8,40 @@ // ログ // +// Logger はモニタを持つため Object から派生しているが、(Object のもう一つの +// 機能である) ログ出力は出来ない。これは自明。 + +// また、ログと comdriver_stdio (特にデバッガの出力) が標準出力への出力を +// 共有するため標準出力側のみここで担当している。 +// 概念としての動作はこんな感じ。 +// +// 送信側スレッド : ログスレッド +// +// ログ/標準出力 : +// | +// +------->| queue |------+ +// | Yes +// : (ログ?)-------------+ +// | | +// : | No +------------------+ +// | | ログモニタに格納 | +// : | +------------------+ +// | | +// : | v +// | (ログを標準出力に出力?) +// : | | | +// | | Yes No | +// : +<---------+ | +// | | +// : v | +// +----------------+ | +// : | 標準出力に出力 | | +// +----------------+ | +// : | | +// +-----------+----------+ +// : | +// END + #include "logger.h" #include "monitor.h" #include "mythread.h" @@ -18,8 +52,10 @@ Logger::Logger() : inherited(OBJ_LOGGER) { + ClearAlias(); + monitor = gMonitorManager->Regist(ID_MONITOR_LOG, this); - monitor->func = ToMonitorCallback(&Logger::MonitorUpdate); + monitor->SetCallback(&Logger::MonitorScreen); monitor->SetSize(80, 40); // 初期サイズは適当 } @@ -40,21 +76,14 @@ Logger::SetConverter(std::string (*conv) bool Logger::StartThread() { - auto func = [this]() { - PTHREAD_SETNAME("Logger"); - std::lock_guard lock_sub(this->thread_starter); - this->ThreadRun(); - }; - if (ResizeCol(monitor->GetCol()) == false) { return false; } - // スレッド起動 + // スレッド起動。 std::lock_guard lock(thread_starter); - try { - thread.reset(new std::thread(func)); + thread.reset(new std::thread(&Logger::OnStart, this)); } catch (...) { } if ((bool)thread == false) { warnx("Failed to initialize thread at %s", __method__); @@ -63,11 +92,30 @@ Logger::StartThread() return true; } +// 開始されたスレッドでのエントリポイント。 +void +Logger::OnStart() +{ + // このスレッドにスレッド名を設定。 + static const char name[] = "Logger"; + PTHREAD_SETNAME(name); + + auto thman = gMainApp.GetThreadManager(); + thman->RegistThread(this, name); + + // 実行開始。 + std::lock_guard lock_sub(this->thread_starter); + ThreadRun(); +} + // スレッドを終了させる void Logger::TerminateThread() { if ((bool)thread) { + auto thman = gMainApp.GetThreadManager(); + thman->UnregistThread(this); + Terminate(); if (thread->joinable()) { @@ -87,13 +135,31 @@ Logger::Terminate() cv.notify_one(); } +// 標準出力にも出力するかを設定。 +void +Logger::UseStdout(bool val) +{ + use_stdout = val; +} + // ログ書き込み。 // 他スレッドから呼ばれる。 void -Logger::Write(const char *str) +Logger::WriteLog(const char *str) +{ + std::lock_guard lock(queue_mtx); + queue.emplace_back(LogMsg::Type::Log, str); + request |= REQ_QUEUE; + cv.notify_one(); +} + +// 標準出力への出力。 +// COMDriverStdio から呼ばれる。 +void +Logger::WriteStdout(const char *str) { std::lock_guard lock(queue_mtx); - queue.emplace_back(str); + queue.emplace_back(LogMsg::Type::Stdout, str); request |= REQ_QUEUE; cv.notify_one(); } @@ -104,7 +170,7 @@ Logger::ThreadRun() SetThreadAffinityHint(AffinityClass::Light); for (;;) { - std::string utf8str; + LogMsg msg; // 何か起きるまで待つ。 { @@ -120,7 +186,7 @@ Logger::ThreadRun() request = 0; continue; } else { - utf8str = queue.front(); + msg = queue.front(); queue.pop_front(); if (queue.empty()) { request = 0; @@ -128,19 +194,32 @@ Logger::ThreadRun() } } - if (__predict_true(utf8str.empty() == false)) { - DoLog(utf8str); + if (msg.type == LogMsg::Type::Log) { + DoLog(msg.text); + } else { + DoStdout(msg.text); } } } +// 標準出力へ出力。 +void +Logger::DoStdout(const std::string& text) +{ + fputs(text.c_str(), stdout); + fflush(stdout); +} + // 着信処理。 void Logger::DoLog(const std::string& utf8str) { - // 必要なら標準出力にも出力 if (use_stdout) { + // 標準出力にも出力。 + // 入力のログ文字列に終端の改行はなく、 + // puts() は stdout に改行を付けて出す。fflush() は念のためくらい。 puts(utf8str.c_str()); + fflush(stdout); } // コンバータがなければここで終了 (CLI)。 @@ -272,7 +351,7 @@ Logger::ResizeCol(int newcol) // userdata = 1 なら1行古いほうに遡った状態、となる。 // screen.userdata + screen の行数が maxlines を超えてはいけない。 void -Logger::MonitorUpdate(Monitor *, TextScreen& screen) +Logger::MonitorScreen(Monitor *, TextScreen& screen) { assertmsg(screen.userdata + screen.GetRow() <= maxlines, "userdata=%" PRIu64 " row=%d maxlines=%d",