Skip to content

Add the logging system#36

Open
Shakai-Dev wants to merge 5 commits into
Redot-Engine:masterfrom
Shakai-Dev:logging-pr
Open

Add the logging system#36
Shakai-Dev wants to merge 5 commits into
Redot-Engine:masterfrom
Shakai-Dev:logging-pr

Conversation

@Shakai-Dev

@Shakai-Dev Shakai-Dev commented Jun 17, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

Release Notes

  • New Features

    • Added an asynchronous logging system with global verbosity filtering, sink management (add/remove), and lifecycle controls (initialize, flush, shutdown).
    • Included console, file, and rolling-file sinks, plus runtime metrics and overflow/fatal logging behavior.
  • Tests

    • Added a comprehensive logging test suite covering dispatch, flushing, formatting, verbosity overrides, concurrency/overflow, sink removal, and fatal-path handling.
  • Documentation

    • Added documentation describing the logging sink architecture and how the built-in sinks behave.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Shakai-Dev, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 36 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3ca69b39-565a-49ca-922c-a08bb43b8269

📥 Commits

Reviewing files that changed from the base of the PR and between e5bb604 and b87c6f4.

📒 Files selected for processing (3)
  • Engine/cpp/runtime/core/logging/logging.cpp
  • Engine/cpp/runtime/core/logging/logging.cppm
  • Engine/cpp/runtime/core/logging/logging.test.cpp
📝 Walkthrough

Walkthrough

A new core.logging C++20 module adds an async ring-buffer pipeline, sink implementations, lifecycle and fatal APIs, runtime build wiring, module re-export, tests, and sink documentation.

Changes

core.logging Runtime Logging Feature

Layer / File(s) Summary
Module interface, build wiring, and re-export
Engine/cpp/runtime/core/logging/logging.cppm, Engine/cpp/runtime/core/core.cppm, Engine/cpp/runtime/core/CMakeLists.txt, Engine/cpp/runtime/CMakeLists.txt
Defines the exported logging types, sinks, metrics, internal hooks, lifecycle/control APIs, formatting wrappers, and templated log/fatal entry points. Registers and links the logging module target and re-exports it from core.cppm.
Ring-buffer state and enqueue/dequeue
Engine/cpp/runtime/core/logging/logging.cpp
Defines queue state, counters, and worker-thread globals, and implements bounded enqueue/dequeue behavior, drop handling, and the worker dispatch loop.
Lifecycle, control APIs, and fatal path
Engine/cpp/runtime/core/logging/logging.cpp
Implements init(), shutdown(), flush(), verbosity and sink management, internal::enqueue_log, internal::execute_fatal, and get_metrics().
Console, file, and rolling sinks
Engine/cpp/runtime/core/logging/logging.cpp
Implements ConsoleSink, FileSink, and RollingFileSink, including append-mode file output, deferred flushing, and size-based rolling with timestamped filenames.
Behavioral tests and sink documentation
Engine/cpp/runtime/core/logging/logging.test.cpp, Docs/LoggingSinks.md
Adds doctest coverage for lifecycle, dispatch, flush, verbosity, sink management, stress cases, fatal handling, file output, formatting, category overrides, and metrics, plus sink architecture documentation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 I hopped through logs both bright and deep,
While worker threads did leap and keep.
Three sinks sang out in tidy rows,
And fatal bunnies struck their pose.
The queue spun on, the tests all cheered,
A logging moonbeam now appeared.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: introducing the logging system.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
Engine/cpp/runtime/core/logging/logging.test.cpp (1)

247-265: ⚡ Quick win

Make fatal_hook restoration exception-safe.

This test mutates global process state and restores it manually; if control exits unexpectedly before Line 264, later tests inherit the wrong hook.

Suggested fix
-    draco::core::logging::internal::fatal_hook = [] { throw FatalTestException{}; };
+    auto previous_fatal_hook = draco::core::logging::internal::fatal_hook;
+    struct FatalHookRestore final
+    {
+        void (**slot)();
+        void (*old_value)();
+        ~FatalHookRestore() { *slot = old_value; }
+    } fatal_hook_restore{&draco::core::logging::internal::fatal_hook, previous_fatal_hook};
+
+    draco::core::logging::internal::fatal_hook = [] { throw FatalTestException{}; };
@@
-    draco::core::logging::internal::fatal_hook = [] { std::abort(); };
     shutdown();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Engine/cpp/runtime/core/logging/logging.test.cpp` around lines 247 - 265, The
fatal_hook is manually restored after the test logic, but if an exception is
thrown before reaching the restoration line at the end, the global state remains
corrupted for subsequent tests. Wrap the fatal_hook assignment at the beginning
of the test in a RAII pattern or scope guard that guarantees the hook is
restored to its original state (std::abort) even if exceptions occur during the
LOG_FATAL call or any other operation within the test scope. Ensure the
restoration happens automatically when exiting the test scope, rather than
relying on manual restoration at a specific line.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Engine/cpp/runtime/core/logging/logging.cpp`:
- Around line 444-451: The timestamp format at line 444 uses second-level
precision which causes filename collisions when multiple log rolls occur within
the same second, causing the std::ios::trunc flag at line 451 to overwrite
previously rotated logs. Add higher precision to the timestamp (such as
milliseconds or microseconds) or implement a counter-based collision avoidance
mechanism in the new_path construction at line 450 to ensure each rolled log
file gets a unique filename even when rolls happen within the same second.
- Around line 195-219: The init() function creates a new worker thread and
assigns it to g_worker_thread without checking if a thread is already running.
To prevent std::terminate errors from repeated init() calls, add a guard before
the thread creation line that checks if g_worker_thread is joinable; if it is,
return early from the init() function to prevent reassigning an active thread.
The check should happen before g_worker_thread is assigned the new
std::thread(worker_loop).
- Around line 162-169: The worker thread loop (where sink->write(msg) is called
around line 166-169), the flush() function (lines 258-260), and the fatal()
function (lines 368-369) all invoke sink I/O operations without synchronization,
creating a data race when using std::ofstream-backed sinks. Add a new mutex
(separate from the existing g_sink_update_mutex which only protects sink list
modifications) to serialize all sink I/O operations. Lock this mutex around the
sink->write() call in the worker thread dequeue loop, around the flush
operations in the flush() function, and around the sink operations in the
fatal() function to ensure thread-safe access to the sink objects across all
three call sites.

In `@Engine/cpp/runtime/core/logging/logging.test.cpp`:
- Around line 288-306: The test has two issues: the ifstream object infile
opened at the file validation section is never closed before attempting to
remove the file at the end of the test, which causes cross-platform failures on
Windows where open files cannot be deleted; additionally, the hardcoded
test_output.log path can cause collisions when tests run in parallel. Fix this
by ensuring the infile stream is closed before file removal (either by wrapping
the file reading operations in a separate scope or explicitly calling
infile.close()) and by replacing the hardcoded test_path string with a unique
temporary file path using std::filesystem mechanisms to generate a unique name
per test run.

In `@Engine/cpp/runtime/core/logging/macros.h`:
- Around line 3-12: The SET_CATEGORY_VERBOSITY macro uses
std::memory_order_relaxed which is declared in the <atomic> header, but the
header file does not include it. Add `#include` <atomic> at the top of the file
near the existing `#include` <format> to ensure the header is self-sufficient and
all dependencies are properly declared, preventing potential compilation issues
for callers that may not have included <atomic> before using these macros.

---

Nitpick comments:
In `@Engine/cpp/runtime/core/logging/logging.test.cpp`:
- Around line 247-265: The fatal_hook is manually restored after the test logic,
but if an exception is thrown before reaching the restoration line at the end,
the global state remains corrupted for subsequent tests. Wrap the fatal_hook
assignment at the beginning of the test in a RAII pattern or scope guard that
guarantees the hook is restored to its original state (std::abort) even if
exceptions occur during the LOG_FATAL call or any other operation within the
test scope. Ensure the restoration happens automatically when exiting the test
scope, rather than relying on manual restoration at a specific line.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2530ce9c-7b86-4ae8-8d00-4f2021e58a00

📥 Commits

Reviewing files that changed from the base of the PR and between 4b1ebc5 and 7f2885b.

📒 Files selected for processing (8)
  • Engine/cpp/runtime/CMakeLists.txt
  • Engine/cpp/runtime/core/CMakeLists.txt
  • Engine/cpp/runtime/core/core.cppm
  • Engine/cpp/runtime/core/io/image_loader.cpp
  • Engine/cpp/runtime/core/logging/logging.cpp
  • Engine/cpp/runtime/core/logging/logging.cppm
  • Engine/cpp/runtime/core/logging/logging.test.cpp
  • Engine/cpp/runtime/core/logging/macros.h

Comment thread Engine/cpp/runtime/core/logging/logging.cpp
Comment thread Engine/cpp/runtime/core/logging/logging.cpp
Comment thread Engine/cpp/runtime/core/logging/logging.cpp Outdated
Comment thread Engine/cpp/runtime/core/logging/logging.test.cpp
Comment thread Engine/cpp/runtime/core/logging/macros.h Outdated
Shakai-Dev and others added 2 commits June 17, 2026 21:40
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

@JoltedJon JoltedJon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@OldDev78 Please check out my comments on my review

Comment thread Engine/cpp/runtime/core/logging/macros.h Outdated
Comment thread Engine/cpp/runtime/core/logging/logging.cppm
Comment thread Engine/cpp/runtime/core/logging/logging.cppm
Comment thread Engine/cpp/runtime/core/logging/logging.cppm
Comment thread Engine/cpp/runtime/core/logging/logging.cppm Outdated
Comment thread Engine/cpp/runtime/core/logging/logging.cppm Outdated
Comment thread Engine/cpp/runtime/core/io/image_loader.cpp
Comment thread Engine/cpp/runtime/core/logging/logging.test.cpp Outdated

@OldDev78 OldDev78 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too much dependencies on headers and macros leaked through the .cppm. Most headers are for sure moveable to the .cpp, as is more of the implementation. Use pimpl if needed for hiding STL dependencies.
Use variadic templates instead of macros.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Engine/cpp/runtime/core/logging/logging.cpp (1)

198-226: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Concurrent init() calls can trigger std::terminate.

The joinable() guard at line 200 is not thread-safe. If two threads call init() simultaneously, both may pass the guard before either assigns to g_worker_thread. When the second thread assigns a new std::thread to g_worker_thread, the destructor of the first thread (which is joinable) is invoked, causing std::terminate.

If concurrent init() is not a supported usage pattern, document this clearly. Otherwise, protect with a mutex or use std::call_once.

🛡️ Suggested fix using std::call_once
+static std::once_flag g_init_flag;
+
+static void init_impl()
+{
+    g_start_time = std::chrono::steady_clock::now();
+    // ... rest of initialization
+    g_worker_thread = std::thread(worker_loop);
+}
+
 void init()
 {
-    if (g_worker_thread.joinable()) { 
-        return; 
-    }
-    // ... initialization code ...
-    g_worker_thread = std::thread(worker_loop);
+    std::call_once(g_init_flag, init_impl);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Engine/cpp/runtime/core/logging/logging.cpp` around lines 198 - 226, The
joinable() check in the init() function is not thread-safe and can result in
multiple threads passing the guard and attempting to initialize g_worker_thread
simultaneously, causing std::terminate when the second thread's assignment
destroys the first thread object. Replace the joinable() guard with a static
std::once_flag and wrap the entire initialization logic (from g_start_time
assignment through g_worker_thread assignment) inside a std::call_once block to
ensure thread-safe one-time initialization of the logging system regardless of
concurrent init() calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Engine/cpp/runtime/core/logging/logging.cpp`:
- Around line 362-363: In the execute_fatal function where strncpy is called to
copy text into msg.text, the length parameter should be limited to not exceed
the actual size of the source string. Replace the strncpy call to use
std::min(text.size(), MaxLogMessageLength - 1) as the length parameter instead
of just MaxLogMessageLength - 1. This ensures we never attempt to read past the
bounds of the text string, matching the fix applied to enqueue_log.
- Around line 344-345: The std::strncpy call in the logging message construction
reads from text.data() which is a std::string_view and is not guaranteed to be
null-terminated, risking out-of-bounds reads past the view's actual size.
Replace the strncpy call with memcpy, passing the explicit size from text.size()
and calculating the length to copy as the minimum of text.size() and
(MaxLogMessageLength - 1) to ensure you only copy the actual contents of the
string_view without reading past its boundaries, then explicitly set the null
terminator at the end as currently done.

In `@Engine/cpp/runtime/core/logging/logging.cppm`:
- Around line 98-103: The fatal hook declarations fatal_hook and execute_fatal
are currently exported as part of the public module API within the export
namespace, which unintentionally exposes them for runtime override and weakens
fail-fast guarantees. Move the internal namespace block containing fatal_hook
and execute_fatal declarations out of the export namespace section so they are
no longer part of the exported module surface, or alternatively gate these
declarations behind a test-only API macro to restrict their visibility to
testing code only.
- Around line 4-7: Add explicit includes for symbols that are directly used in
the logging module interface but currently missing from the header section.
Include the `<concepts>` header to support the use of `std::constructible_from`
and include the `<utility>` header to support the use of `std::forward`
throughout the module. These symbols are used in the interface definitions but
relying on transitive includes from other headers like `<format>` is
non-portable and can cause compilation failures across different toolchain
combinations.

---

Outside diff comments:
In `@Engine/cpp/runtime/core/logging/logging.cpp`:
- Around line 198-226: The joinable() check in the init() function is not
thread-safe and can result in multiple threads passing the guard and attempting
to initialize g_worker_thread simultaneously, causing std::terminate when the
second thread's assignment destroys the first thread object. Replace the
joinable() guard with a static std::once_flag and wrap the entire initialization
logic (from g_start_time assignment through g_worker_thread assignment) inside a
std::call_once block to ensure thread-safe one-time initialization of the
logging system regardless of concurrent init() calls.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b101be71-74b6-41d5-9c3b-54ff8a905856

📥 Commits

Reviewing files that changed from the base of the PR and between f8bc400 and 860e2ad.

📒 Files selected for processing (4)
  • Docs/LoggingSinks.md
  • Engine/cpp/runtime/core/logging/logging.cpp
  • Engine/cpp/runtime/core/logging/logging.cppm
  • Engine/cpp/runtime/core/logging/logging.test.cpp
✅ Files skipped from review due to trivial changes (1)
  • Docs/LoggingSinks.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • Engine/cpp/runtime/core/logging/logging.test.cpp

Comment thread Engine/cpp/runtime/core/logging/logging.cpp Outdated
Comment thread Engine/cpp/runtime/core/logging/logging.cpp Outdated
Comment thread Engine/cpp/runtime/core/logging/logging.cppm
Comment thread Engine/cpp/runtime/core/logging/logging.cppm
@Shakai-Dev Shakai-Dev force-pushed the logging-pr branch 2 times, most recently from d9b90b6 to a0c7815 Compare June 22, 2026 14:12
Comment thread Engine/cpp/runtime/core/logging/logging.cppm
Comment thread Engine/cpp/runtime/core/logging/logging.cppm
Comment thread Engine/cpp/runtime/core/logging/logging.cppm
Comment thread Engine/cpp/runtime/core/logging/logging.cppm
Comment thread Engine/cpp/runtime/core/logging/logging.cpp Outdated
Comment thread Engine/cpp/runtime/core/logging/logging.cpp
Comment thread Engine/cpp/runtime/core/logging/logging.cpp Outdated
Comment thread Engine/cpp/runtime/core/logging/logging.cpp
Comment thread Engine/cpp/runtime/core/logging/logging.cpp

@OldDev78 OldDev78 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just address @mcdubhghlas comments, and it can be merged.
Optionally, consider adding constexpr flags exports here or in platform to allow ruling out portions of code in the implementation. We actually could use those in more places too.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Engine/cpp/runtime/core/logging/logging.cpp`:
- Around line 19-20: The logging timestamp formatting code is missing the
required standard header for time types and formatting. Update the include list
in logging.cpp so the section that uses std::tm, localtime_s/localtime_r, and
std::put_time also includes <ctime>, alongside the existing source_location and
system_error headers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7acfb87f-6aa4-4faa-a268-288e7c1b0a7c

📥 Commits

Reviewing files that changed from the base of the PR and between 860e2ad and e5bb604.

📒 Files selected for processing (4)
  • Docs/LoggingSinks.md
  • Engine/cpp/runtime/core/logging/logging.cpp
  • Engine/cpp/runtime/core/logging/logging.cppm
  • Engine/cpp/runtime/core/logging/logging.test.cpp
✅ Files skipped from review due to trivial changes (1)
  • Docs/LoggingSinks.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • Engine/cpp/runtime/core/logging/logging.cppm
  • Engine/cpp/runtime/core/logging/logging.test.cpp

Comment thread Engine/cpp/runtime/core/logging/logging.cpp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants