-
Notifications
You must be signed in to change notification settings - Fork 116
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
feat: Add Unity 6 enhanced console log service with improved reliability #47
Conversation
…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]>
📝 WalkthroughWalkthroughThis 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
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
Possibly related PRs
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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:
- Add blank lines around the table (line 8)
- 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, 10Also 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
📒 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
toIConsoleLogsService
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.
public const int ObservedRuntimeWarning = 8405504; // 0x804200 (bits: 23, 18, 9) | ||
public const int ObservedRuntimeError = 8405248; // 0x804100 (bits: 23, 18, 8) |
There was a problem hiding this comment.
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.
["message"] = actualMessage, | ||
["type"] = entryType, | ||
["timestamp"] = DateTime.Now.AddSeconds(-(currentCount - i)).ToString("yyyy-MM-dd HH:mm:ss.fff") | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
["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.
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); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
@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 |
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.
✏️ Learnings added
|
There was a problem hiding this 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?
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 |
Summary
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
Editor/Services/ConsoleLogsServiceUnity6.cs
ConsoleWindowUtility
andLogEntries
APIs via reflectioncallstackTextStartUTF16
fieldConsoleWindowUtility.consoleWindowChanged
event to detect when logs are cleared, ensuring our internal log storage stays synchronized with what's actually displayed in Unity's console window2. Log Type Classification Fix
Editor/Services/LogEntryModeFlags.cs
0x41000
(266240)0x42800
(272384)0x40044
(262212)0x804200
(8405504)0x804100
(8405248)3. Configuration System
Editor/UnityBridge/McpUnitySettings.cs
ConsoleLogServiceType
enum with two options:EventBased
(default, safe) - Original implementationUnity6Enhanced
(experimental) - New implementation using internal APIs4. Documentation
Editor/Services/Unity6InternalAPIReference.md
Testing
Tested with various log types:
Important Notes
🤖 Generated with Claude Code
Created with Palmier
Summary by CodeRabbit
New Features
Documentation
Chores