Debugging Techniques and Tools in the Pyrite64 Editor: Log Window and Notification System

The Pyrite64 editor provides a lightweight runtime diagnostics infrastructure centered around a thread-safe logger, a real-time Log Window, and a transient Notification system to diagnose issues without leaving the UI.

The Pyrite64 editor, part of the HailToDodongo/pyrite64 repository, includes built-in debugging techniques and tools designed for runtime issue diagnosis. These features allow developers to monitor execution flow and catch errors through a dedicated Log Window and a non-intrusive Notification system, both implemented using ImGui for seamless integration.

Core Logging Infrastructure

The Logger API in src/utils/logger.h

The foundation of Pyrite64's diagnostics is the Utils::Logger namespace defined in src/utils/logger.h. It exposes severity levels and functions for emitting structured messages:

namespace Utils::Logger
{
    constexpr int LEVEL_INFO  = 0;
    constexpr int LEVEL_WARN  = 1;
    constexpr int LEVEL_ERROR = 2;

    void log(const std::string& msg, int level = LEVEL_INFO);
    void logRaw(const std::string& msg, int level = LEVEL_INFO);
    void clear();
    std::string getLog();
}

Thread-Safe Buffer Implementation in src/utils/logger.cpp

The implementation in src/utils/logger.cpp maintains a thread-safe static std::string buffer that accumulates all log entries. When log() is called, the function prepends severity tags like [INFO], [WARN], or [ERROR] automatically. The getLog() method returns the entire buffer content, enabling the UI to display a complete history without file I/O overhead.

Typical usage inside the editor or engine modules looks like this:

Utils::Logger::log("Project opened: " + projectPath);
Utils::Logger::log("Missing texture asset!", Utils::Logger::LEVEL_ERROR);

Real-Time Log Window Visualization

LogWindow::draw Implementation in src/editor/pages/parts/logWindow.cpp

The LogWindow class in src/editor/pages/parts/logWindow.cpp renders the buffered log using ImGui's InputTextMultiline widget. The draw() method fetches the current log buffer via Utils::Logger::getLog() and displays it in a read-only text field with a monospaced font for alignment:

void Editor::LogWindow::draw()
{
    auto log = Utils::Logger::getLog();
    
    ImGui::PushFont(ImGui::getFontMono());
    ImGui::InputTextMultiline("", log.data(), log.size()+1,
                              ImVec2(ImGui::GetWindowSize().x-18,
                                     ImGui::GetWindowSize().y-44),
                              ImGuiInputTextFlags_ReadOnly);
    // Auto-scroll logic follows...
}

Auto-Scroll and Read-Only Features

To ensure developers always see the latest entries, the implementation tracks buffer length changes. When lastLen != log.length(), the code calculates the maximum scroll value and calls ImGui::SetScrollY() to snap to the bottom. The ImGuiInputTextFlags_ReadOnly flag prevents accidental modification of the diagnostic history while allowing text selection for copying.

Transient Notification System

Notification Structure and Rendering in src/editor/imgui/notification.cpp

For user-facing alerts that don't require persistent log storage, Pyrite64 uses a lightweight notification system defined in src/editor/imgui/notification.cpp. It maintains a static std::vector<Notification> where each entry contains message text, a time-to-live counter (ttl), and a severity type:

struct Notification
{
    std::string text;
    uint32_t    ttl;
    int         type;  // Maps to LEVEL_INFO, LEVEL_WARN, LEVEL_ERROR
};

static std::vector<Notification> notifications{};

The rendering loop positions these as small pop-ups in the top-right corner using ImGui::SetNextWindowPos() and ImGui::Begin() with ImGuiWindowFlags_NoTitleBar and ImGuiWindowFlags_AlwaysAutoResize.

Triggering User-Facing Alerts

Developers emit notifications via the Notify() function, which pushes a new entry into the vector with a default lifespan of 180 frames (approximately 3 seconds at 60 FPS):

void Notify(const std::string& msg, int type = Utils::Logger::LEVEL_INFO, uint32_t ttl = 180)
{
    notifications.push_back({msg, ttl, type});
}

Typical usage appears in UI controllers after significant events:

void AssetManager::onAssetLoaded(const std::string& name)
{
    Utils::Logger::log("Asset loaded: " + name);
    Notify("✅ " + name + " ready", Utils::Logger::LEVEL_INFO, 120);
}

Practical Debugging Workflow

Combining these debugging techniques and tools creates a seamless diagnostic pipeline:

  1. Instrument your code with Utils::Logger::log() calls at key execution points. Files like src/editor/globalActions.cpp use this pattern for project operations.

  2. Monitor in real-time by opening the Log Window from the View menu. The window auto-scrolls to show the latest LEVEL_ERROR or LEVEL_WARN entries as they are emitted by the thread-safe buffer.

  3. Surface critical events using Notify() for user-facing feedback—such as build completion in src/build/projectBuilder.cpp—without requiring the user to check the log buffer.

  4. Clear history via Utils::Logger::clear() when starting a new debugging session to ensure fresh output without restarting the editor.

Summary

  • Utils::Logger provides the backbone for runtime diagnostics with thread-safe buffering and severity levels (LEVEL_INFO, LEVEL_WARN, LEVEL_ERROR).
  • The Log Window (src/editor/pages/parts/logWindow.cpp) renders the buffered log using ImGui with auto-scroll and read-only protection.
  • The Notification System (src/editor/imgui/notification.cpp) displays transient, colour-coded pop-ups via the Notify() function for immediate user feedback.
  • Together, these debugging techniques and tools in the Pyrite64 editor enable efficient runtime issue diagnosis without external debuggers or console windows.

Frequently Asked Questions

How do I view the log output in the Pyrite64 editor?

Open the Log Window from the View menu. This window calls Utils::Logger::getLog() to retrieve the entire buffered history and displays it in a read-only ImGui text field that auto-scrolls to the newest entries as they arrive from the thread-safe buffer.

What is the difference between the Log Window and the Notification System?

The Log Window provides a persistent, scrollable history of all diagnostic messages stored in the thread-safe logger buffer, suitable for detailed analysis. The Notification System displays transient pop-ups that disappear after a set TTL (time-to-live), designed for immediate user alerts without cluttering the log history.

How do I trigger a notification from my own editor code?

Include the notification header and call the Notify() function with your message, severity level, and optional duration in frames:

#include "editor/imgui/notification.h"
Notify("Operation complete", Utils::Logger::LEVEL_INFO, 180);

Can I redirect the logger output to a file instead of the Log Window?

Yes. The Utils::Logger::setOutput() function accepts a custom callback that receives every log string. You can implement a file-writing sink and register it during editor initialization to capture diagnostics to disk while still viewing them in the Log Window.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →