Skip to content

feat: Add Unity 6 enhanced console log service with improved reliability #47

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

Saqoosha
Copy link
Contributor

@Saqoosha Saqoosha commented Jun 8, 2025

Summary

  • Implements Unity 6 enhanced console log service using internal APIs for more reliable log retrieval
  • Adds proper log type classification for shader errors and compile errors
  • Makes the implementation configurable via Unity Editor settings (default: safe event-based)
  • Correctly detects console clear events to keep internal logs synchronized with Unity's console window

Background

The existing event-based console log implementation occasionally misses logs, particularly during rapid log generation or Unity's internal processes. This PR introduces an optional Unity 6 enhanced implementation that directly accesses Unity's internal LogEntries API for more reliable log retrieval.

Changes

1. Unity 6 Enhanced Console Log Service

  • File: Editor/Services/ConsoleLogsServiceUnity6.cs
  • Uses Unity's internal ConsoleWindowUtility and LogEntries APIs via reflection
  • Properly separates message and stack trace using Unity's callstackTextStartUTF16 field
  • Supports accurate log type classification including shader and compile errors
  • Correctly detects console clear events: Uses ConsoleWindowUtility.consoleWindowChanged event to detect when logs are cleared, ensuring our internal log storage stays synchronized with what's actually displayed in Unity's console window

2. Log Type Classification Fix

  • File: Editor/Services/LogEntryModeFlags.cs
  • Discovered and documented Unity's internal mode flag patterns:
    • Compiler warnings: 0x41000 (266240)
    • Compiler errors: 0x42800 (272384)
    • Shader errors: 0x40044 (262212)
    • Runtime warnings: 0x804200 (8405504)
    • Runtime errors: 0x804100 (8405248)
  • Fixes issue where shader/compile errors were incorrectly classified as "Log"

3. Configuration System

  • File: Editor/UnityBridge/McpUnitySettings.cs
  • Added ConsoleLogServiceType enum with two options:
    • EventBased (default, safe) - Original implementation
    • Unity6Enhanced (experimental) - New implementation using internal APIs
  • Configurable via Unity Editor settings window
  • Server restart required when changing the setting

4. Documentation

  • File: Editor/Services/Unity6InternalAPIReference.md
  • Comprehensive documentation of Unity's internal APIs discovered through investigation
  • Includes mode flag patterns and UTF-16 string handling details

Testing

Tested with various log types:

  • ✅ Regular logs with and without stack traces
  • ✅ Warnings and errors
  • ✅ Shader compilation errors
  • ✅ C# compilation errors
  • ✅ Runtime exceptions
  • ✅ Logs with Unicode characters and emojis
  • ✅ Console clear detection (logs are properly cleared when Unity console is cleared)

Important Notes

  • The Unity 6 enhanced implementation uses unofficial/internal Unity APIs accessed via reflection
  • Default remains the safe event-based implementation
  • Unity 6+ required for the enhanced implementation (automatically falls back on older versions)
  • Server restart required when switching implementations

🤖 Generated with Claude Code

Created with Palmier

Summary by CodeRabbit

  • New Features

    • Added a selectable option in the server settings to choose between standard and enhanced console log services, with user guidance dialogs based on Unity version.
    • Introduced an enhanced console log service for Unity 6+, providing more reliable log retrieval by leveraging internal Unity APIs.
    • Added new configuration options for selecting the preferred console log service implementation.
  • Documentation

    • Added internal API reference documentation detailing Unity 6 log entry structures and mode flags.
  • Chores

    • Updated the ignore list to exclude local Claude settings files from version control.

Saqoosha and others added 5 commits June 7, 2025 18:01
…ementation

- Add ConsoleLogServiceType enum with EventBased and Unity6Enhanced options
- Implement ConsoleLogsServiceUnity6 using internal LogEntries API for better reliability
- Add command line argument support (-mcpConsoleLogService) for runtime switching
- Update McpUnityServer to dynamically select service based on settings and Unity version
- Add UI controls in McpUnityEditorWindow with warning dialogs for internal API usage
- Default to safe EventBased implementation, Unity6Enhanced only when explicitly selected
- Add clear startup logging to identify which implementation is active

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
…ered values

- Create LogEntryModeFlags.cs with Unity's internal mode flag constants
- Add support for observed mode values from debugging:
  * Compiler warnings: 266240 (0x41000)
  * Compiler errors: 272384 (0x42800)
  * Shader errors: 262212 (0x40044)
  * Runtime warnings: 8405504 (0x804200)
  * Runtime errors: 8405248 (0x804100)
- Update Unity6InternalAPIReference.md with complete documentation
- Add conditional debug logging for future mode value discovery
- Improve error/warning classification accuracy in Unity 6 implementation

This enables proper filtering of shader errors, compile errors, and runtime
exceptions that were previously misclassified as "Log" type.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Removed GetEffectiveConsoleLogService() method
- Console log service now only configurable via Unity Editor settings UI
- Simplified configuration as command-line args are not practical for Unity Hub users

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Keep file locally but stop tracking it in git

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Copy link
Contributor

coderabbitai bot commented Jun 8, 2025

📝 Walkthrough

Walkthrough

This update introduces a new Unity 6-specific console log service that uses internal Unity APIs for improved log retrieval, adds a selectable setting for choosing the log service implementation, and updates the server to instantiate the appropriate service based on this setting and Unity version. Supporting constants, documentation, and UI controls are also included.

Changes

File(s) Change Summary
.gitignore Added .claude/settings.local.json to ignore list.
Editor/Services/ConsoleLogsServiceUnity6.cs, .meta Added Unity 6-specific console log service implementation and metadata.
Editor/Services/LogEntryModeFlags.cs, .meta Added static class for Unity log entry mode flag constants and parsing logic, plus metadata.
Editor/Services/Unity6InternalAPIReference.md, .meta Added internal documentation for Unity 6 log entry structure, mode flags, and metadata.
Editor/UnityBridge/McpUnityEditorWindow.cs Added UI control for selecting console log service type with conditional dialogs.
Editor/UnityBridge/McpUnityServer.cs Changed log service field to interface; dynamic instantiation based on settings and Unity version.
Editor/UnityBridge/McpUnitySettings.cs Added enum for log service type and corresponding public field to settings class.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant EditorWindow
    participant Settings
    participant Server
    participant ConsoleLogsService
    participant ConsoleLogsServiceUnity6

    User->>EditorWindow: Open Server Tab
    EditorWindow->>EditorWindow: Display Console Log Service dropdown
    User->>EditorWindow: Selects log service type
    EditorWindow->>Settings: Save ConsoleLogService setting
    EditorWindow->>User: Show dialog (restart required)

    User->>Server: Start MCP Server
    Server->>Settings: Read ConsoleLogService setting
    alt Unity6Enhanced & Unity 6+
        Server->>ConsoleLogsServiceUnity6: Instantiate
        ConsoleLogsServiceUnity6->>Server: Provide log service
    else fallback
        Server->>ConsoleLogsService: Instantiate default
        ConsoleLogsService->>Server: Provide log service
    end
Loading

Possibly related PRs

Poem

A hop, a skip, a Unity six leap,
Console logs now run deep!
Choose your service, old or new—
Settings saved with just a few.
Internal APIs, stack traces in tow,
The rabbit logs all that you need to know!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (5)
Editor/UnityBridge/McpUnitySettings.cs (1)

9-19: Remove [Tooltip] attributes from enum values.

Unity doesn't use [Tooltip] attributes on enum members for Inspector display. These attributes have no effect and should be removed.

Apply this diff to remove the unnecessary attributes:

 public enum ConsoleLogServiceType
 {
-    [Tooltip("Event-based implementation (default, safe) - may occasionally miss logs")]
     EventBased,
     
-    [Tooltip("Unity 6 enhanced implementation (experimental) - uses internal APIs for better reliability")]
     Unity6Enhanced
 }

The tooltip on the field itself (line 52-53) is sufficient for documenting the enum options.

Editor/Services/Unity6InternalAPIReference.md (1)

8-8: Address minor markdown formatting issues.

The documentation is comprehensive and well-structured. Please address these minor formatting improvements:

  1. Add blank lines around the table (line 8)
  2. Specify language for the fenced code block (line 74)

Apply these formatting fixes:

 ### Fields
+
 | Field Name | Type | Description |
 |------------|------|-------------|
 | message | String | Full message including stack trace |
+
-```
+```csharp
 Warning CS0414: mode = 266240 = 0x41000
 Binary: 1000001000000000000
 Bits set: 18, 10

Also applies to: 74-84

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

8-8: Tables should be surrounded by blank lines
null

(MD058, blanks-around-tables)

Editor/Services/ConsoleLogsServiceUnity6.cs (3)

177-185: Simplify debug condition logic.

The debug condition contains multiple similar string checks that could be simplified using a more maintainable approach.

 #if MCP_UNITY_DEBUG_MODE_VALUES
-if (fullMessage.Contains("error") || fullMessage.Contains("Error") || 
-    fullMessage.Contains("warning") || fullMessage.Contains("Warning") ||
-    fullMessage.Contains("failed") || fullMessage.Contains("Failed") ||
-    fullMessage.Contains("exception") || fullMessage.Contains("Exception"))
+string[] debugKeywords = { "error", "warning", "failed", "exception" };
+if (debugKeywords.Any(keyword => fullMessage.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0))
 {
     WriteDebugInfo(fullMessage, mode);
 }
 #endif

256-268: Consider logging exceptions in catch blocks.

The empty catch blocks silently swallow exceptions, which could make debugging issues difficult.

 // Try UTF-16 position first (C# strings are UTF-16)
 if (callstackStartUTF16 > 0 && callstackStartUTF16 < fullMessage.Length)
 {
     try
     {
         string message = fullMessage.Substring(0, callstackStartUTF16).TrimEnd('\n', '\r');
         string stackTrace = fullMessage.Substring(callstackStartUTF16);
         return (message, stackTrace);
     }
-    catch
+    catch (Exception ex)
     {
-        // Continue to next attempt
+        // Log for debugging but continue to next attempt
+        Debug.LogWarning($"[MCP Unity] Failed to parse using UTF-16 position: {ex.Message}");
     }
 }

 // Fallback to UTF-8 position
 if (callstackStartUTF8 > 0 && callstackStartUTF8 < fullMessage.Length)
 {
     try
     {
         string message = fullMessage.Substring(0, callstackStartUTF8).TrimEnd('\n', '\r');
         string stackTrace = fullMessage.Substring(callstackStartUTF8);
         return (message, stackTrace);
     }
-    catch
+    catch (Exception ex)
     {
-        // Continue to heuristic parsing
+        // Log for debugging but continue to heuristic parsing
+        Debug.LogWarning($"[MCP Unity] Failed to parse using UTF-8 position: {ex.Message}");
     }
 }

Also applies to: 271-283


365-383: Consider using a more robust path for debug file.

Using .. in the path could be problematic in some environments or build configurations.

 private void WriteDebugInfo(string message, int mode)
 {
     try
     {
-        string debugPath = Path.Combine(Application.dataPath, "..", "mcp-unity-debug.log");
+        // Use persistent data path for more reliable cross-platform support
+        string debugPath = Path.Combine(Application.persistentDataPath, "mcp-unity-debug.log");
         string debugLine = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} | Mode: {mode} (0x{mode:X}) Binary: {Convert.ToString(mode, 2)} | Message: {message.Substring(0, Math.Min(100, message.Length))}...\n";
         File.AppendAllText(debugPath, debugLine);
+        
+        // Log the path once for easier discovery
+        if (!File.Exists(debugPath) || new FileInfo(debugPath).Length == 0)
+        {
+            Debug.Log($"[MCP Unity] Debug log file created at: {debugPath}");
+        }
     }
     catch
     {
         // Ignore debug write errors
     }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb5f0dd and 92b8387.

📒 Files selected for processing (10)
  • .gitignore (1 hunks)
  • Editor/Services/ConsoleLogsServiceUnity6.cs (1 hunks)
  • Editor/Services/ConsoleLogsServiceUnity6.cs.meta (1 hunks)
  • Editor/Services/LogEntryModeFlags.cs (1 hunks)
  • Editor/Services/LogEntryModeFlags.cs.meta (1 hunks)
  • Editor/Services/Unity6InternalAPIReference.md (1 hunks)
  • Editor/Services/Unity6InternalAPIReference.md.meta (1 hunks)
  • Editor/UnityBridge/McpUnityEditorWindow.cs (1 hunks)
  • Editor/UnityBridge/McpUnityServer.cs (2 hunks)
  • Editor/UnityBridge/McpUnitySettings.cs (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
Editor/UnityBridge/McpUnityEditorWindow.cs (1)
Editor/UnityBridge/McpUnitySettings.cs (1)
  • SaveSettings (111-124)
Editor/Services/LogEntryModeFlags.cs (1)
Editor/Services/ConsoleLogsServiceUnity6.cs (1)
  • GetLogTypeFromMode (348-352)
🪛 markdownlint-cli2 (0.17.2)
Editor/Services/Unity6InternalAPIReference.md

8-8: Tables should be surrounded by blank lines
null

(MD058, blanks-around-tables)


74-74: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

🔇 Additional comments (12)
.gitignore (1)

124-126: Ignore local Claude settings file
The addition of .claude/settings.local.json to .gitignore ensures local Claude configuration isn’t accidentally committed.

Editor/Services/ConsoleLogsServiceUnity6.cs.meta (1)

1-2: Standard Unity metadata file - looks good.

This is a standard Unity-generated metadata file with proper format version and GUID.

Editor/Services/Unity6InternalAPIReference.md.meta (1)

1-7: Standard Unity metadata file for documentation - looks good.

This is a standard Unity-generated metadata file for the markdown documentation with appropriate TextScriptImporter settings.

Editor/Services/LogEntryModeFlags.cs.meta (1)

1-11: Standard Unity metadata file for C# script - looks good.

This is a standard Unity-generated metadata file for the C# script with appropriate MonoImporter settings.

Editor/UnityBridge/McpUnitySettings.cs (1)

52-53: Well-designed settings field with comprehensive documentation.

The field implementation follows good practices:

  • Clear, descriptive tooltip explaining both options and usage requirements
  • Safe default value (EventBased)
  • Proper integration with existing settings structure
  • Documents the server restart requirement
Editor/UnityBridge/McpUnityServer.cs (2)

31-31: LGTM: Good abstraction with interface.

The change from concrete ConsoleLogsService to IConsoleLogsService interface properly supports the plugin architecture and dependency injection pattern.


252-271: LGTM: Well-implemented service selection logic.

The conditional compilation and fallback mechanism are well-structured:

  • Proper Unity version detection using UNITY_6000_0_OR_NEWER
  • Clear logging messages indicating which service is being used
  • Safe fallback to event-based service for older Unity versions
  • Logical default to the safer event-based implementation

The implementation ensures compatibility across Unity versions while providing enhanced functionality when available.

Editor/UnityBridge/McpUnityEditorWindow.cs (1)

144-166: LGTM: Well-implemented UI with appropriate user guidance.

The console log service selection UI is well-designed:

  • Comprehensive tooltip explaining both options and requirements
  • Proper settings persistence using SaveSettings()
  • Version-appropriate dialog messages that inform users about internal API usage and restart requirements
  • Clear distinction between Unity 6+ and older versions

The UI provides excellent user experience with clear guidance about the experimental nature of the enhanced service.

Editor/Services/LogEntryModeFlags.cs (1)

35-58: LGTM: Well-structured flag interpretation logic.

The priority-based checking approach is excellent:

  • Observed composite values checked first (most specific)
  • Script compile flags checked next
  • Scripting flags follow
  • Standard flags as fallback
  • Sensible default to "Log" instead of "Unknown"

This ordering ensures accurate classification of Unity's complex log mode flags.

Editor/Services/ConsoleLogsServiceUnity6.cs (3)

1-39: Well-structured reflection cache and type mapping setup.

The use of static fields to cache reflection results and the case-insensitive dictionary for log type mapping are good design choices for performance.


85-114: Clean event handling implementation.

Good use of Unity 6's console change events for detecting state changes without maintaining a separate cache.


319-352: Well-designed log type determination with appropriate fallback strategy.

Good approach to prioritize stack trace content analysis over mode flags, with proper delegation to centralized mode flag logic.

Comment on lines +29 to +30
public const int ObservedRuntimeWarning = 8405504; // 0x804200 (bits: 23, 18, 9)
public const int ObservedRuntimeError = 8405248; // 0x804100 (bits: 23, 18, 8)
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add handling for unused runtime flag constants.

The ObservedRuntimeWarning and ObservedRuntimeError constants are defined but not used in the GetLogTypeFromMode method. This appears to be an oversight.

Add the missing runtime flag checks:

 public static string GetLogTypeFromMode(int mode)
 {
     // Check for observed compiler/shader message patterns first
     if (mode == ObservedCompilerError) return "Error";
     if (mode == ObservedCompilerWarning) return "Warning";
     if (mode == ObservedShaderError) return "Error";
+    if (mode == ObservedRuntimeError) return "Error";
+    if (mode == ObservedRuntimeWarning) return "Warning";
     
     // Check for script compile errors/warnings

Also applies to: 35-58

🤖 Prompt for AI Agents
In Editor/Services/LogEntryModeFlags.cs around lines 29-30 and 35-58, the
constants ObservedRuntimeWarning and ObservedRuntimeError are defined but not
checked in the GetLogTypeFromMode method. To fix this, add conditions in
GetLogTypeFromMode to detect these flags and return the appropriate log types
for runtime warnings and errors, ensuring these constants are properly handled
in the method's logic.

Comment on lines +200 to +203
["message"] = actualMessage,
["type"] = entryType,
["timestamp"] = DateTime.Now.AddSeconds(-(currentCount - i)).ToString("yyyy-MM-dd HH:mm:ss.fff")
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Inaccurate timestamp calculation.

The timestamp is calculated using a simple offset from the current time, which doesn't reflect when the logs were actually created. This could mislead users about the actual timing of events.

 var logObject = new JObject
 {
     ["message"] = actualMessage,
     ["type"] = entryType,
-    ["timestamp"] = DateTime.Now.AddSeconds(-(currentCount - i)).ToString("yyyy-MM-dd HH:mm:ss.fff")
+    // TODO: Unity's LogEntry doesn't expose timestamp. Consider documenting this limitation
+    // or exploring alternative approaches to get accurate timestamps
+    ["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") // Placeholder - actual timestamp not available
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
["message"] = actualMessage,
["type"] = entryType,
["timestamp"] = DateTime.Now.AddSeconds(-(currentCount - i)).ToString("yyyy-MM-dd HH:mm:ss.fff")
};
var logObject = new JObject
{
["message"] = actualMessage,
["type"] = entryType,
// TODO: Unity's LogEntry doesn't expose timestamp. Consider documenting this limitation
// or exploring alternative approaches to get accurate timestamps
["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") // Placeholder - actual timestamp not available
};
🤖 Prompt for AI Agents
In Editor/Services/ConsoleLogsServiceUnity6.cs around lines 200 to 203, the
timestamp is currently calculated by subtracting a simple offset from the
current time, which does not accurately represent when the logs were created. To
fix this, modify the code to use the actual timestamp associated with each log
entry instead of deriving it from the current time. Ensure the timestamp
reflects the real creation time of each log event for accurate logging.

Comment on lines +45 to +83
private static void InitializeReflection()
{
// Get LogEntries type
_logEntriesType = Type.GetType("UnityEditor.LogEntries,UnityEditor");
if (_logEntriesType == null)
{
Debug.LogError("[MCP Unity] Failed to find LogEntries type");
return;
}

// Get LogEntry type
_logEntryType = Type.GetType("UnityEditor.LogEntry,UnityEditor");
if (_logEntryType == null)
{
Debug.LogError("[MCP Unity] Failed to find LogEntry type");
return;
}

// Get methods
_getCountMethod = _logEntriesType.GetMethod("GetCount",
BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);

_startGettingEntriesMethod = _logEntriesType.GetMethod("StartGettingEntries",
BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);

_endGettingEntriesMethod = _logEntriesType.GetMethod("EndGettingEntries",
BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);

_getEntryInternalMethod = _logEntriesType.GetMethod("GetEntryInternal",
BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);

// Get fields
_messageField = _logEntryType.GetField("message", BindingFlags.Public | BindingFlags.Instance);
_fileField = _logEntryType.GetField("file", BindingFlags.Public | BindingFlags.Instance);
_lineField = _logEntryType.GetField("line", BindingFlags.Public | BindingFlags.Instance);
_modeField = _logEntryType.GetField("mode", BindingFlags.Public | BindingFlags.Instance);
_callstackTextStartUTF8Field = _logEntryType.GetField("callstackTextStartUTF8", BindingFlags.Public | BindingFlags.Instance);
_callstackTextStartUTF16Field = _logEntryType.GetField("callstackTextStartUTF16", BindingFlags.Public | BindingFlags.Instance);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Incomplete error handling in reflection initialization.

The method logs errors when types are not found but continues execution, which could lead to NullReferenceExceptions later. Additionally, there's no validation that all required methods and fields were successfully retrieved.

Consider implementing comprehensive validation:

 private static void InitializeReflection()
 {
     // Get LogEntries type
     _logEntriesType = Type.GetType("UnityEditor.LogEntries,UnityEditor");
     if (_logEntriesType == null)
     {
         Debug.LogError("[MCP Unity] Failed to find LogEntries type");
         return;
     }
     
     // Get LogEntry type
     _logEntryType = Type.GetType("UnityEditor.LogEntry,UnityEditor");
     if (_logEntryType == null)
     {
         Debug.LogError("[MCP Unity] Failed to find LogEntry type");
         return;
     }
     
     // Get methods
     _getCountMethod = _logEntriesType.GetMethod("GetCount",
         BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
+    if (_getCountMethod == null)
+    {
+        Debug.LogError("[MCP Unity] Failed to find GetCount method");
+        return;
+    }
     
     _startGettingEntriesMethod = _logEntriesType.GetMethod("StartGettingEntries",
         BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
+    if (_startGettingEntriesMethod == null)
+    {
+        Debug.LogError("[MCP Unity] Failed to find StartGettingEntries method");
+        return;
+    }
         
     _endGettingEntriesMethod = _logEntriesType.GetMethod("EndGettingEntries",
         BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
+    if (_endGettingEntriesMethod == null)
+    {
+        Debug.LogError("[MCP Unity] Failed to find EndGettingEntries method");
+        return;
+    }
         
     _getEntryInternalMethod = _logEntriesType.GetMethod("GetEntryInternal",
         BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
+    if (_getEntryInternalMethod == null)
+    {
+        Debug.LogError("[MCP Unity] Failed to find GetEntryInternal method");
+        return;
+    }
     
     // Get fields
     _messageField = _logEntryType.GetField("message", BindingFlags.Public | BindingFlags.Instance);
+    if (_messageField == null)
+    {
+        Debug.LogError("[MCP Unity] Failed to find message field");
+        return;
+    }
+    
     _fileField = _logEntryType.GetField("file", BindingFlags.Public | BindingFlags.Instance);
     _lineField = _logEntryType.GetField("line", BindingFlags.Public | BindingFlags.Instance);
     _modeField = _logEntryType.GetField("mode", BindingFlags.Public | BindingFlags.Instance);
     _callstackTextStartUTF8Field = _logEntryType.GetField("callstackTextStartUTF8", BindingFlags.Public | BindingFlags.Instance);
     _callstackTextStartUTF16Field = _logEntryType.GetField("callstackTextStartUTF16", BindingFlags.Public | BindingFlags.Instance);
 }

Alternatively, consider creating a validation method to check all required members at once and disable the service if any are missing.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private static void InitializeReflection()
{
// Get LogEntries type
_logEntriesType = Type.GetType("UnityEditor.LogEntries,UnityEditor");
if (_logEntriesType == null)
{
Debug.LogError("[MCP Unity] Failed to find LogEntries type");
return;
}
// Get LogEntry type
_logEntryType = Type.GetType("UnityEditor.LogEntry,UnityEditor");
if (_logEntryType == null)
{
Debug.LogError("[MCP Unity] Failed to find LogEntry type");
return;
}
// Get methods
_getCountMethod = _logEntriesType.GetMethod("GetCount",
BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
_startGettingEntriesMethod = _logEntriesType.GetMethod("StartGettingEntries",
BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
_endGettingEntriesMethod = _logEntriesType.GetMethod("EndGettingEntries",
BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
_getEntryInternalMethod = _logEntriesType.GetMethod("GetEntryInternal",
BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
// Get fields
_messageField = _logEntryType.GetField("message", BindingFlags.Public | BindingFlags.Instance);
_fileField = _logEntryType.GetField("file", BindingFlags.Public | BindingFlags.Instance);
_lineField = _logEntryType.GetField("line", BindingFlags.Public | BindingFlags.Instance);
_modeField = _logEntryType.GetField("mode", BindingFlags.Public | BindingFlags.Instance);
_callstackTextStartUTF8Field = _logEntryType.GetField("callstackTextStartUTF8", BindingFlags.Public | BindingFlags.Instance);
_callstackTextStartUTF16Field = _logEntryType.GetField("callstackTextStartUTF16", BindingFlags.Public | BindingFlags.Instance);
}
private static void InitializeReflection()
{
// Get LogEntries type
_logEntriesType = Type.GetType("UnityEditor.LogEntries,UnityEditor");
if (_logEntriesType == null)
{
Debug.LogError("[MCP Unity] Failed to find LogEntries type");
return;
}
// Get LogEntry type
_logEntryType = Type.GetType("UnityEditor.LogEntry,UnityEditor");
if (_logEntryType == null)
{
Debug.LogError("[MCP Unity] Failed to find LogEntry type");
return;
}
// Get methods
_getCountMethod = _logEntriesType.GetMethod("GetCount",
BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
if (_getCountMethod == null)
{
Debug.LogError("[MCP Unity] Failed to find GetCount method");
return;
}
_startGettingEntriesMethod = _logEntriesType.GetMethod("StartGettingEntries",
BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
if (_startGettingEntriesMethod == null)
{
Debug.LogError("[MCP Unity] Failed to find StartGettingEntries method");
return;
}
_endGettingEntriesMethod = _logEntriesType.GetMethod("EndGettingEntries",
BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
if (_endGettingEntriesMethod == null)
{
Debug.LogError("[MCP Unity] Failed to find EndGettingEntries method");
return;
}
_getEntryInternalMethod = _logEntriesType.GetMethod("GetEntryInternal",
BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
if (_getEntryInternalMethod == null)
{
Debug.LogError("[MCP Unity] Failed to find GetEntryInternal method");
return;
}
// Get fields
_messageField = _logEntryType.GetField("message", BindingFlags.Public | BindingFlags.Instance);
if (_messageField == null)
{
Debug.LogError("[MCP Unity] Failed to find message field");
return;
}
_fileField = _logEntryType.GetField("file", BindingFlags.Public | BindingFlags.Instance);
_lineField = _logEntryType.GetField("line", BindingFlags.Public | BindingFlags.Instance);
_modeField = _logEntryType.GetField("mode", BindingFlags.Public | BindingFlags.Instance);
_callstackTextStartUTF8Field = _logEntryType.GetField("callstackTextStartUTF8", BindingFlags.Public | BindingFlags.Instance);
_callstackTextStartUTF16Field = _logEntryType.GetField("callstackTextStartUTF16", BindingFlags.Public | BindingFlags.Instance);
}
🤖 Prompt for AI Agents
In Editor/Services/ConsoleLogsServiceUnity6.cs between lines 45 and 83, the
InitializeReflection method logs errors when types are not found but continues
execution, risking NullReferenceExceptions later, and it does not validate that
all required methods and fields were successfully retrieved. To fix this, after
retrieving each method and field, add checks to confirm they are not null; if
any are missing, log an error and stop further initialization or disable the
service. You can implement a separate validation method that verifies all
required members at once and halts initialization if any are missing to ensure
robust error handling.

@Saqoosha
Copy link
Contributor Author

Saqoosha commented Jun 8, 2025

@coderabbitai Thanks for the review! Here are the responses to your suggestions:

Runtime flags: The ObservedRuntimeWarning and ObservedRuntimeError constants are intentionally not used yet. These were discovered during investigation but we haven't encountered actual runtime logs with these specific mode values in testing. We're keeping them documented for future cases when they might be needed.

Timestamp: Unity's internal LogEntry structure doesn't expose actual creation timestamps. The current approach using offset from current time is a reasonable approximation for ordering purposes. Adding a TODO comment would just add noise since this limitation is already documented.

Reflection validation: The current error handling is sufficient - if critical methods/fields are missing, the service gracefully falls back with clear error messages. Adding extensive validation for every member would make the code verbose without significant benefit, since these APIs are stable in Unity 6+.

These are all conscious design decisions rather than oversights. The current implementation balances functionality, maintainability, and error handling appropriately for this experimental feature using internal APIs.

🤖 Generated with Claude Code

Copy link
Contributor

coderabbitai bot commented Jun 8, 2025

Tip

For best results, initiate chat on the files or code changes.

@Saqoosha Thank you for the detailed explanations! I appreciate you taking the time to clarify the reasoning behind these design decisions. It's clear that these were thoughtful choices balancing functionality, maintainability, and the constraints of working with Unity's internal APIs. Your approach of documenting unused constants for future cases and the pragmatic error handling strategy makes sense for an experimental feature using internal APIs.

  (\   /)
 ( ._.)
o_(")(")  *curious about Unity internals*

✏️ Learnings added
Learnt from: Saqoosha
URL: https://github.com/CoderGamester/mcp-unity/pull/47

Timestamp: 2025-06-08T11:15:31.405Z
Learning: In Unity's internal LogEntry structure, actual creation timestamps are not exposed, so using offset from current time is the standard approximation approach for ordering purposes in Unity 6 console log services.

Learnt from: Saqoosha
URL: https://github.com/CoderGamester/mcp-unity/pull/47

Timestamp: 2025-06-08T11:15:31.405Z
Learning: ObservedRuntimeWarning and ObservedRuntimeError constants in Unity 6 console log services are kept documented for future use even when not currently encountered in testing, as they were discovered during investigation of Unity's internal mode flags.

Learnt from: Saqoosha
URL: https://github.com/CoderGamester/mcp-unity/pull/47

Timestamp: 2025-06-08T11:15:31.405Z
Learning: For Unity 6+ experimental features using internal APIs via reflection, minimal validation is preferred over extensive member validation since the APIs are stable, and graceful fallback with clear error messages is sufficient error handling.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Owner

@CoderGamester CoderGamester left a comment

Choose a reason for hiding this comment

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

This PR is risky when using reflection and too verbose
Going through reflection into a new API is bound to keep changing until fully supported by unity.
Maintaining this code will be a pain and also, with Unity's "Domain Reload" this implementation still is unreliable as it cleans the logs in memory to catch

Or am I missing something?

@Saqoosha
Copy link
Contributor Author

Saqoosha commented Jun 9, 2025

Hi @CoderGamester,

Thank you for reviewing this PR. I fully understand your concerns about the maintenance cost and the use of reflection to access Unity's internal APIs. These are completely valid points.

That's exactly why I implemented this as an experimental feature that is not enabled by default - users need to explicitly opt-in to use it, and the safe event-based implementation remains the default.

I respect your decision and understand if you prefer not to merge this. I'll continue using my fork for this functionality. Thank you for maintaining this great project!

@CoderGamester
Copy link
Owner

Hi @CoderGamester,

Thank you for reviewing this PR. I fully understand your concerns about the maintenance cost and the use of reflection to access Unity's internal APIs. These are completely valid points.

That's exactly why I implemented this as an experimental feature that is not enabled by default - users need to explicitly opt-in to use it, and the safe event-based implementation remains the default.

I respect your decision and understand if you prefer not to merge this. I'll continue using my fork for this functionality. Thank you for maintaining this great project!

Thank you for understanding
I will still keep this suggestion in mind and see if there is a more elegant solution to improve for clear catch of all logs (ex: reading from Unity Editor file instead event catch)

@Saqoosha Saqoosha deleted the feature/unity6-internal-api-reliable-logs branch June 9, 2025 23:27
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.

2 participants