using System; using System.Reflection; using System.Collections.Generic; using UnityEngine; using Object = UnityEngine.Object; namespace LLM.Editor.Analysis { /// /// Provides access to the Unity Editor's console log entries using reflection. /// public class ConsoleLogProvider : IContextProvider { private class ConsoleLogEntry { public string type; public string message; } // Reflection-based access to internal Unity log types private static Type _logEntriesType; private static MethodInfo _getCountMethod; private static MethodInfo _getEntryMethod; private static MethodInfo _startGettingEntriesMethod; private static MethodInfo _endGettingEntriesMethod; private static object _logEntryInstance; static ConsoleLogProvider() { // Initialize reflection types once _logEntriesType = Type.GetType("UnityEditor.LogEntries, UnityEditor.dll"); if (_logEntriesType == null) return; _getCountMethod = _logEntriesType.GetMethod("GetCount", BindingFlags.Static | BindingFlags.Public); _getEntryMethod = _logEntriesType.GetMethod("GetEntryInternal", BindingFlags.Static | BindingFlags.Public); _startGettingEntriesMethod = _logEntriesType.GetMethod("StartGettingEntries", BindingFlags.Static | BindingFlags.Public); _endGettingEntriesMethod = _logEntriesType.GetMethod("EndGettingEntries", BindingFlags.Static | BindingFlags.Public); // The LogEntry class is nested within LogEntries var logEntryType = Type.GetType("UnityEditor.LogEntry, UnityEditor.dll"); if (logEntryType != null) { _logEntryInstance = Activator.CreateInstance(logEntryType); } } public object GetContext(Object target, string qualifier) { if (_logEntriesType == null || _getCountMethod == null || _getEntryMethod == null || _logEntryInstance == null) { return "Error: Could not access Unity's internal console log API via reflection."; } // Parse the qualifier for filters (e.g., "errors:10") var parts = qualifier?.Split(':'); var filterType = parts is { Length: > 0 } ? parts[0].ToLower() : "all"; var count = (parts is { Length: > 1 } && int.TryParse(parts[1], out var num)) ? num : 10; var logEntries = new List(); _startGettingEntriesMethod.Invoke(null, null); var totalCount = (int)_getCountMethod.Invoke(null, null); count = Mathf.Min(count, totalCount); for (var i = 0; i < totalCount && logEntries.Count < count; i++) { var index = totalCount - 1 - i; // Read from the most recent _getEntryMethod.Invoke(null, new object[] { index, _logEntryInstance }); var message = (string)_logEntryInstance.GetType().GetField("message", BindingFlags.Instance | BindingFlags.Public)?.GetValue(_logEntryInstance); var mode = (int)_logEntryInstance.GetType().GetField("mode", BindingFlags.Instance | BindingFlags.Public)?.GetValue(_logEntryInstance)!; var logType = GetLogTypeFromMode(mode); if (filterType != "all" && (filterType != "errors" || logType != "Error") && (filterType != "warnings" || logType != "Warning")) continue; if (message != null) logEntries.Add(new ConsoleLogEntry { type = logType, message = message.Trim() }); } _endGettingEntriesMethod.Invoke(null, null); return logEntries; } private static string GetLogTypeFromMode(int mode) { // These mode values are constants defined internally in UnityEditor.LogEntry if ((mode & 1) != 0) return "Error"; // 0x0001 return (mode & 2) != 0 ? "Warning" : // 0x0002 "Log"; } } }