Add docs to the logger
This commit is contained in:
@@ -49,11 +49,6 @@ namespace Editor
|
|||||||
|
|
||||||
#region UI State
|
#region UI State
|
||||||
|
|
||||||
private bool showClassFilter = true;
|
|
||||||
private bool showMethodFilter = false;
|
|
||||||
private bool showLevelFilter = true;
|
|
||||||
private bool showTimeFilter = false;
|
|
||||||
|
|
||||||
// Colors for log levels
|
// Colors for log levels
|
||||||
private readonly Color debugColor = Color.white;
|
private readonly Color debugColor = Color.white;
|
||||||
private readonly Color infoColor = Color.white;
|
private readonly Color infoColor = Color.white;
|
||||||
@@ -123,7 +118,6 @@ namespace Editor
|
|||||||
private void OnGUI()
|
private void OnGUI()
|
||||||
{
|
{
|
||||||
DrawToolbar();
|
DrawToolbar();
|
||||||
DrawFilters();
|
|
||||||
DrawLogEntries();
|
DrawLogEntries();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,11 +138,46 @@ namespace Editor
|
|||||||
// Auto-scroll toggle
|
// Auto-scroll toggle
|
||||||
autoScroll = GUILayout.Toggle(autoScroll, "Auto-scroll", EditorStyles.toolbarButton, GUILayout.Width(80));
|
autoScroll = GUILayout.Toggle(autoScroll, "Auto-scroll", EditorStyles.toolbarButton, GUILayout.Width(80));
|
||||||
|
|
||||||
|
GUILayout.Space(10);
|
||||||
|
|
||||||
|
// Class Filter Button
|
||||||
|
string classLabel = selectedClassFilters.Count == 0 ? "Classes: All" :
|
||||||
|
selectedClassFilters.Count == activeClassTags.Count ? "Classes: All" :
|
||||||
|
$"Classes: {selectedClassFilters.Count}";
|
||||||
|
if (GUILayout.Button(new GUIContent(classLabel, "Filter by class"), EditorStyles.toolbarDropDown, GUILayout.Width(100)))
|
||||||
|
{
|
||||||
|
ShowClassFilterMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method Filter Button
|
||||||
|
string methodLabel = selectedMethodFilters.Count == 0 ? "Methods: All" :
|
||||||
|
selectedMethodFilters.Count == activeMethodTags.Count ? "Methods: All" :
|
||||||
|
$"Methods: {selectedMethodFilters.Count}";
|
||||||
|
if (GUILayout.Button(new GUIContent(methodLabel, "Filter by method"), EditorStyles.toolbarDropDown, GUILayout.Width(110)))
|
||||||
|
{
|
||||||
|
ShowMethodFilterMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log Level Filter Button
|
||||||
|
string levelLabel = selectedLevelFilters.Count == 4 ? "Levels: All" :
|
||||||
|
selectedLevelFilters.Count == 0 ? "Levels: None" :
|
||||||
|
$"Levels: {selectedLevelFilters.Count}";
|
||||||
|
if (GUILayout.Button(new GUIContent(levelLabel, "Filter by log level"), EditorStyles.toolbarDropDown, GUILayout.Width(90)))
|
||||||
|
{
|
||||||
|
ShowLevelFilterMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time Range Filter Button
|
||||||
|
if (GUILayout.Button(new GUIContent("⏱", "Time range filter"), EditorStyles.toolbarButton, GUILayout.Width(25)))
|
||||||
|
{
|
||||||
|
ShowTimeRangeWindow();
|
||||||
|
}
|
||||||
|
|
||||||
GUILayout.FlexibleSpace();
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
// Log count
|
// Search box
|
||||||
var filteredCount = GetFilteredLogs().Count();
|
GUILayout.Label("Search:", GUILayout.Width(50));
|
||||||
GUILayout.Label($"{filteredCount} / {allLogs.Count} logs", GUILayout.Width(100));
|
searchText = GUILayout.TextField(searchText, EditorStyles.toolbarSearchField, GUILayout.Width(150));
|
||||||
|
|
||||||
GUILayout.Space(10);
|
GUILayout.Space(10);
|
||||||
|
|
||||||
@@ -158,135 +187,55 @@ namespace Editor
|
|||||||
ExportLogs();
|
ExportLogs();
|
||||||
}
|
}
|
||||||
|
|
||||||
GUILayout.Space(10);
|
GUILayout.Space(5);
|
||||||
|
|
||||||
// Search box
|
// Log count
|
||||||
GUILayout.Label("Search:", GUILayout.Width(50));
|
var filteredCount = GetFilteredLogs().Count();
|
||||||
searchText = GUILayout.TextField(searchText, EditorStyles.toolbarSearchField, GUILayout.Width(200));
|
GUILayout.Label($"{filteredCount}/{allLogs.Count}", GUILayout.Width(80));
|
||||||
|
|
||||||
EditorGUILayout.EndHorizontal();
|
EditorGUILayout.EndHorizontal();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawFilters()
|
private void ShowClassFilterMenu()
|
||||||
{
|
{
|
||||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
ClassFilterWindow.ShowWindow(this, activeClassTags, selectedClassFilters,
|
||||||
|
(newSelection) =>
|
||||||
// Class Filters
|
|
||||||
showClassFilter = EditorGUILayout.Foldout(showClassFilter, $"Class Filters ({selectedClassFilters.Count} selected)", true);
|
|
||||||
if (showClassFilter)
|
|
||||||
{
|
{
|
||||||
EditorGUI.indentLevel++;
|
selectedClassFilters = newSelection;
|
||||||
|
Repaint();
|
||||||
EditorGUILayout.BeginHorizontal();
|
});
|
||||||
if (GUILayout.Button("All", GUILayout.Width(50)))
|
|
||||||
{
|
|
||||||
selectedClassFilters = new HashSet<string>(activeClassTags);
|
|
||||||
}
|
|
||||||
if (GUILayout.Button("None", GUILayout.Width(50)))
|
|
||||||
{
|
|
||||||
selectedClassFilters.Clear();
|
|
||||||
}
|
|
||||||
EditorGUILayout.EndHorizontal();
|
|
||||||
|
|
||||||
EditorGUILayout.BeginVertical();
|
|
||||||
foreach (var tag in activeClassTags.OrderBy(t => t))
|
|
||||||
{
|
|
||||||
bool isSelected = selectedClassFilters.Contains(tag);
|
|
||||||
bool newSelection = EditorGUILayout.ToggleLeft(tag, isSelected);
|
|
||||||
|
|
||||||
if (newSelection && !isSelected)
|
|
||||||
selectedClassFilters.Add(tag);
|
|
||||||
else if (!newSelection && isSelected)
|
|
||||||
selectedClassFilters.Remove(tag);
|
|
||||||
}
|
|
||||||
EditorGUILayout.EndVertical();
|
|
||||||
|
|
||||||
EditorGUI.indentLevel--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorGUILayout.Space(5);
|
private void ShowMethodFilterMenu()
|
||||||
|
|
||||||
// Method Filters
|
|
||||||
showMethodFilter = EditorGUILayout.Foldout(showMethodFilter, $"Method Filters ({selectedMethodFilters.Count} selected)", true);
|
|
||||||
if (showMethodFilter)
|
|
||||||
{
|
{
|
||||||
EditorGUI.indentLevel++;
|
MethodFilterWindow.ShowWindow(this, activeMethodTags, selectedMethodFilters,
|
||||||
|
(newSelection) =>
|
||||||
EditorGUILayout.BeginHorizontal();
|
|
||||||
if (GUILayout.Button("All", GUILayout.Width(50)))
|
|
||||||
{
|
{
|
||||||
selectedMethodFilters = new HashSet<string>(activeMethodTags);
|
selectedMethodFilters = newSelection;
|
||||||
}
|
Repaint();
|
||||||
if (GUILayout.Button("None", GUILayout.Width(50)))
|
});
|
||||||
{
|
|
||||||
selectedMethodFilters.Clear();
|
|
||||||
}
|
|
||||||
EditorGUILayout.EndHorizontal();
|
|
||||||
|
|
||||||
EditorGUILayout.BeginVertical();
|
|
||||||
foreach (var tag in activeMethodTags.OrderBy(t => t))
|
|
||||||
{
|
|
||||||
bool isSelected = selectedMethodFilters.Contains(tag);
|
|
||||||
bool newSelection = EditorGUILayout.ToggleLeft(tag, isSelected);
|
|
||||||
|
|
||||||
if (newSelection && !isSelected)
|
|
||||||
selectedMethodFilters.Add(tag);
|
|
||||||
else if (!newSelection && isSelected)
|
|
||||||
selectedMethodFilters.Remove(tag);
|
|
||||||
}
|
|
||||||
EditorGUILayout.EndVertical();
|
|
||||||
|
|
||||||
EditorGUI.indentLevel--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorGUILayout.Space(5);
|
private void ShowLevelFilterMenu()
|
||||||
|
|
||||||
// Log Level Filters
|
|
||||||
showLevelFilter = EditorGUILayout.Foldout(showLevelFilter, "Log Level Filters", true);
|
|
||||||
if (showLevelFilter)
|
|
||||||
{
|
{
|
||||||
EditorGUI.indentLevel++;
|
LevelFilterWindow.ShowWindow(this, selectedLevelFilters,
|
||||||
foreach (LogLevel level in Enum.GetValues(typeof(LogLevel)))
|
(newSelection) =>
|
||||||
{
|
{
|
||||||
bool isSelected = selectedLevelFilters.Contains(level);
|
selectedLevelFilters = newSelection;
|
||||||
bool newSelection = EditorGUILayout.ToggleLeft(level.ToString(), isSelected);
|
Repaint();
|
||||||
|
});
|
||||||
if (newSelection && !isSelected)
|
|
||||||
selectedLevelFilters.Add(level);
|
|
||||||
else if (!newSelection && isSelected)
|
|
||||||
selectedLevelFilters.Remove(level);
|
|
||||||
}
|
|
||||||
EditorGUI.indentLevel--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorGUILayout.Space(5);
|
private void ShowTimeRangeWindow()
|
||||||
|
|
||||||
// Time Range Filter
|
|
||||||
showTimeFilter = EditorGUILayout.Foldout(showTimeFilter, "Time Range Filter", true);
|
|
||||||
if (showTimeFilter)
|
|
||||||
{
|
{
|
||||||
EditorGUI.indentLevel++;
|
TimeRangeFilterWindow.ShowWindow(this, enableTimeFilter, minTimestamp, maxTimestamp, currentMaxTimestamp,
|
||||||
|
(enabled, min, max) =>
|
||||||
enableTimeFilter = EditorGUILayout.Toggle("Enable Time Filter", enableTimeFilter);
|
|
||||||
|
|
||||||
if (enableTimeFilter && currentMaxTimestamp > 0)
|
|
||||||
{
|
{
|
||||||
EditorGUILayout.BeginHorizontal();
|
enableTimeFilter = enabled;
|
||||||
EditorGUILayout.LabelField($"Min: {minTimestamp:F2}s", GUILayout.Width(100));
|
minTimestamp = min;
|
||||||
EditorGUILayout.LabelField($"Max: {maxTimestamp:F2}s", GUILayout.Width(100));
|
maxTimestamp = max;
|
||||||
EditorGUILayout.EndHorizontal();
|
Repaint();
|
||||||
|
});
|
||||||
EditorGUILayout.MinMaxSlider(
|
|
||||||
ref minTimestamp,
|
|
||||||
ref maxTimestamp,
|
|
||||||
0,
|
|
||||||
currentMaxTimestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorGUI.indentLevel--;
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorGUILayout.EndVertical();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawLogEntries()
|
private void DrawLogEntries()
|
||||||
@@ -508,5 +457,357 @@ namespace Editor
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Persistent popup window for class filtering with multi-selection support
|
||||||
|
/// </summary>
|
||||||
|
public class ClassFilterWindow : EditorWindow
|
||||||
|
{
|
||||||
|
private HashSet<string> availableTags;
|
||||||
|
private HashSet<string> selectedTags;
|
||||||
|
private System.Action<HashSet<string>> onApply;
|
||||||
|
private Vector2 scrollPosition;
|
||||||
|
private string searchFilter = "";
|
||||||
|
|
||||||
|
public static void ShowWindow(CustomLogWindow parent, HashSet<string> available, HashSet<string> selected,
|
||||||
|
System.Action<HashSet<string>> applyCallback)
|
||||||
|
{
|
||||||
|
var window = CreateInstance<ClassFilterWindow>();
|
||||||
|
window.titleContent = new GUIContent("Class Filter");
|
||||||
|
window.availableTags = available;
|
||||||
|
window.selectedTags = new HashSet<string>(selected);
|
||||||
|
window.onApply = applyCallback;
|
||||||
|
|
||||||
|
var parentRect = parent.position;
|
||||||
|
window.position = new Rect(parentRect.x + 50, parentRect.y + 50, 300, 400);
|
||||||
|
window.ShowUtility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
// Control buttons
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (GUILayout.Button("All", GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
selectedTags = new HashSet<string>(availableTags);
|
||||||
|
}
|
||||||
|
if (GUILayout.Button("None", GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
selectedTags.Clear();
|
||||||
|
}
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
searchFilter = EditorGUILayout.TextField(searchFilter, EditorStyles.toolbarSearchField, GUILayout.Width(150));
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
// Scrollable list of toggles
|
||||||
|
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||||
|
|
||||||
|
var filteredTags = string.IsNullOrEmpty(searchFilter)
|
||||||
|
? availableTags.OrderBy(t => t)
|
||||||
|
: availableTags.Where(t => t.Contains(searchFilter, StringComparison.OrdinalIgnoreCase)).OrderBy(t => t);
|
||||||
|
|
||||||
|
foreach (var tag in filteredTags)
|
||||||
|
{
|
||||||
|
bool isSelected = selectedTags.Contains(tag);
|
||||||
|
bool newSelection = EditorGUILayout.ToggleLeft(tag, isSelected);
|
||||||
|
|
||||||
|
if (newSelection != isSelected)
|
||||||
|
{
|
||||||
|
if (newSelection)
|
||||||
|
selectedTags.Add(tag);
|
||||||
|
else
|
||||||
|
selectedTags.Remove(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
// Apply/Close buttons
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Apply", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
onApply?.Invoke(selectedTags);
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Close", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Persistent popup window for method filtering with multi-selection support
|
||||||
|
/// </summary>
|
||||||
|
public class MethodFilterWindow : EditorWindow
|
||||||
|
{
|
||||||
|
private HashSet<string> availableTags;
|
||||||
|
private HashSet<string> selectedTags;
|
||||||
|
private System.Action<HashSet<string>> onApply;
|
||||||
|
private Vector2 scrollPosition;
|
||||||
|
private string searchFilter = "";
|
||||||
|
|
||||||
|
public static void ShowWindow(CustomLogWindow parent, HashSet<string> available, HashSet<string> selected,
|
||||||
|
System.Action<HashSet<string>> applyCallback)
|
||||||
|
{
|
||||||
|
var window = CreateInstance<MethodFilterWindow>();
|
||||||
|
window.titleContent = new GUIContent("Method Filter");
|
||||||
|
window.availableTags = available;
|
||||||
|
window.selectedTags = new HashSet<string>(selected);
|
||||||
|
window.onApply = applyCallback;
|
||||||
|
|
||||||
|
var parentRect = parent.position;
|
||||||
|
window.position = new Rect(parentRect.x + 50, parentRect.y + 50, 300, 400);
|
||||||
|
window.ShowUtility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
// Control buttons
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (GUILayout.Button("All", GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
selectedTags = new HashSet<string>(availableTags);
|
||||||
|
}
|
||||||
|
if (GUILayout.Button("None", GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
selectedTags.Clear();
|
||||||
|
}
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
searchFilter = EditorGUILayout.TextField(searchFilter, EditorStyles.toolbarSearchField, GUILayout.Width(150));
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
// Scrollable list of toggles
|
||||||
|
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||||
|
|
||||||
|
var filteredTags = string.IsNullOrEmpty(searchFilter)
|
||||||
|
? availableTags.OrderBy(t => t)
|
||||||
|
: availableTags.Where(t => t.Contains(searchFilter, StringComparison.OrdinalIgnoreCase)).OrderBy(t => t);
|
||||||
|
|
||||||
|
foreach (var tag in filteredTags)
|
||||||
|
{
|
||||||
|
bool isSelected = selectedTags.Contains(tag);
|
||||||
|
bool newSelection = EditorGUILayout.ToggleLeft(tag, isSelected);
|
||||||
|
|
||||||
|
if (newSelection != isSelected)
|
||||||
|
{
|
||||||
|
if (newSelection)
|
||||||
|
selectedTags.Add(tag);
|
||||||
|
else
|
||||||
|
selectedTags.Remove(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
// Apply/Close buttons
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Apply", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
onApply?.Invoke(selectedTags);
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Close", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Persistent popup window for log level filtering with multi-selection support
|
||||||
|
/// </summary>
|
||||||
|
public class LevelFilterWindow : EditorWindow
|
||||||
|
{
|
||||||
|
private HashSet<LogLevel> selectedLevels;
|
||||||
|
private System.Action<HashSet<LogLevel>> onApply;
|
||||||
|
|
||||||
|
public static void ShowWindow(CustomLogWindow parent, HashSet<LogLevel> selected,
|
||||||
|
System.Action<HashSet<LogLevel>> applyCallback)
|
||||||
|
{
|
||||||
|
var window = CreateInstance<LevelFilterWindow>();
|
||||||
|
window.titleContent = new GUIContent("Log Level Filter");
|
||||||
|
window.selectedLevels = new HashSet<LogLevel>(selected);
|
||||||
|
window.onApply = applyCallback;
|
||||||
|
|
||||||
|
var parentRect = parent.position;
|
||||||
|
window.position = new Rect(parentRect.x + 50, parentRect.y + 50, 250, 180);
|
||||||
|
window.ShowUtility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
// Control buttons
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (GUILayout.Button("All", GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
selectedLevels = new HashSet<LogLevel>
|
||||||
|
{
|
||||||
|
LogLevel.Debug, LogLevel.Info, LogLevel.Warning, LogLevel.Error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (GUILayout.Button("None", GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
selectedLevels.Clear();
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(10);
|
||||||
|
|
||||||
|
// Log level toggles
|
||||||
|
foreach (LogLevel level in Enum.GetValues(typeof(LogLevel)))
|
||||||
|
{
|
||||||
|
bool isSelected = selectedLevels.Contains(level);
|
||||||
|
bool newSelection = EditorGUILayout.ToggleLeft(level.ToString(), isSelected);
|
||||||
|
|
||||||
|
if (newSelection != isSelected)
|
||||||
|
{
|
||||||
|
if (newSelection)
|
||||||
|
selectedLevels.Add(level);
|
||||||
|
else
|
||||||
|
selectedLevels.Remove(level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space(10);
|
||||||
|
|
||||||
|
// Apply/Close buttons
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Apply", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
onApply?.Invoke(selectedLevels);
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Close", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Popup window for configuring time range filters
|
||||||
|
/// </summary>
|
||||||
|
public class TimeRangeFilterWindow : EditorWindow
|
||||||
|
{
|
||||||
|
private bool enableTimeFilter;
|
||||||
|
private float minTimestamp;
|
||||||
|
private float maxTimestamp;
|
||||||
|
private float currentMaxTimestamp;
|
||||||
|
private System.Action<bool, float, float> onApply;
|
||||||
|
|
||||||
|
public static void ShowWindow(CustomLogWindow parent, bool enabled, float min, float max, float currentMax,
|
||||||
|
System.Action<bool, float, float> applyCallback)
|
||||||
|
{
|
||||||
|
var window = CreateInstance<TimeRangeFilterWindow>();
|
||||||
|
window.titleContent = new GUIContent("Time Range Filter");
|
||||||
|
window.enableTimeFilter = enabled;
|
||||||
|
window.minTimestamp = min;
|
||||||
|
window.maxTimestamp = max;
|
||||||
|
window.currentMaxTimestamp = currentMax;
|
||||||
|
window.onApply = applyCallback;
|
||||||
|
|
||||||
|
// Position near the parent window
|
||||||
|
var parentRect = parent.position;
|
||||||
|
window.position = new Rect(parentRect.x + 100, parentRect.y + 100, 350, 120);
|
||||||
|
|
||||||
|
window.ShowUtility();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
EditorGUILayout.Space(10);
|
||||||
|
|
||||||
|
enableTimeFilter = EditorGUILayout.Toggle("Enable Time Filter", enableTimeFilter);
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
if (enableTimeFilter && currentMaxTimestamp > 0)
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField($"Min: {minTimestamp:F2}s", GUILayout.Width(100));
|
||||||
|
EditorGUILayout.LabelField($"Max: {maxTimestamp:F2}s", GUILayout.Width(100));
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.MinMaxSlider(
|
||||||
|
ref minTimestamp,
|
||||||
|
ref maxTimestamp,
|
||||||
|
0,
|
||||||
|
currentMaxTimestamp);
|
||||||
|
|
||||||
|
EditorGUILayout.Space(5);
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (GUILayout.Button("Reset Range"))
|
||||||
|
{
|
||||||
|
minTimestamp = 0;
|
||||||
|
maxTimestamp = currentMaxTimestamp;
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
else if (enableTimeFilter)
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("No logs available for time filtering", MessageType.Info);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space(10);
|
||||||
|
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Apply", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
onApply?.Invoke(enableTimeFilter, minTimestamp, maxTimestamp);
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUILayout.Button("Cancel", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
120
docs/custom_log_console.md
Normal file
120
docs/custom_log_console.md
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
# Custom Log Console
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
A centralized logging system with an advanced filtering console that automatically tags log entries with class and method names. Provides powerful filtering capabilities beyond Unity's default console, including multi-select filters, text search, time-range filtering, and log export.
|
||||||
|
|
||||||
|
## Using the Logging System in Code
|
||||||
|
|
||||||
|
All logging automatically captures the calling class and method name using `CallerMemberName` and `CallerFilePath` attributes. Simply call the static logging methods:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Core;
|
||||||
|
|
||||||
|
public class MyClass : ManagedBehaviour
|
||||||
|
{
|
||||||
|
internal override void OnManagedStart()
|
||||||
|
{
|
||||||
|
Logging.Debug("Initialization complete");
|
||||||
|
Logging.Info("Player spawned at position");
|
||||||
|
Logging.Warning("Missing configuration, using defaults");
|
||||||
|
Logging.Error("Failed to load required asset");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output format:**
|
||||||
|
```
|
||||||
|
[ClassName][MethodName] Your message
|
||||||
|
```
|
||||||
|
|
||||||
|
**Available methods:**
|
||||||
|
- `Logging.Debug(string message)` - Detailed diagnostic information
|
||||||
|
- `Logging.Info(string message)` - General informational messages
|
||||||
|
- `Logging.Warning(string message)` - Non-critical issues
|
||||||
|
- `Logging.Error(string message)` - Critical errors
|
||||||
|
|
||||||
|
**Note:** All logs are broadcast via the `Logging.OnLogEntryAdded` event and stored in a central buffer accessible via `Logging.GetRecentLogs()`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Opening the Console Window
|
||||||
|
|
||||||
|
**Menu:** `AppleHills > Custom Log Console`
|
||||||
|
|
||||||
|
You can open multiple independent console instances with different filter configurations to monitor separate systems simultaneously
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Console Interface
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Toolbar Controls
|
||||||
|
|
||||||
|
#### 🔵 Basic Controls (Blue outline)
|
||||||
|
- **Clear** - Clears all log entries and resets tag lists
|
||||||
|
- **Auto-scroll** - Automatically scrolls to newest entries when enabled
|
||||||
|
|
||||||
|
#### Filter Buttons (Persistent Popups)
|
||||||
|
|
||||||
|
All filter buttons open persistent popup windows that remain open during multi-selection. Changes apply when you click "Apply" or dismiss with "Close".
|
||||||
|
|
||||||
|
- **🔴 Classes Filter (Red outline)**
|
||||||
|
- Multi-select which classes to display
|
||||||
|
- Includes search box for quick filtering
|
||||||
|
- All/None quick actions
|
||||||
|
|
||||||
|
- **🟢 Methods Filter (Green outline)**
|
||||||
|
- Multi-select which methods to display
|
||||||
|
- Includes search box for quick filtering
|
||||||
|
- All/None quick actions
|
||||||
|
|
||||||
|
- **🟡 Levels Filter (Yellow outline)**
|
||||||
|
- Toggle Debug, Info, Warning, Error levels
|
||||||
|
- All/None quick actions
|
||||||
|
|
||||||
|
- **⏱ Time Filter**
|
||||||
|
- Opens utility window with MinMaxSlider
|
||||||
|
- Filter logs by timestamp range
|
||||||
|
- Enable/disable toggle with reset option
|
||||||
|
|
||||||
|
#### Search & Export
|
||||||
|
- **Search** - Full-text search across class names, method names, and message content
|
||||||
|
- **Export** - Save filtered logs to .txt file with timestamp
|
||||||
|
- **Count** - Shows `filtered/total` log count
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Visual Indicators
|
||||||
|
|
||||||
|
**Color Coding:**
|
||||||
|
- White: Debug/Info (normal operation)
|
||||||
|
- Yellow: Warning (non-critical issues)
|
||||||
|
- Red: Error (critical failures)
|
||||||
|
|
||||||
|
**Alternating Rows:** Light/dark grey backgrounds improve readability for dense log output.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
**Event Broadcasting:**
|
||||||
|
```csharp
|
||||||
|
Logging.OnLogEntryAdded += (LogEntry entry) => { /* handle */ };
|
||||||
|
```
|
||||||
|
|
||||||
|
**Manual Log Retrieval:**
|
||||||
|
```csharp
|
||||||
|
List<LogEntry> recentLogs = Logging.GetRecentLogs();
|
||||||
|
```
|
||||||
|
|
||||||
|
**LogEntry Structure:**
|
||||||
|
- `ClassName` - Captured from calling file path
|
||||||
|
- `MethodName` - Captured from `CallerMemberName`
|
||||||
|
- `Message` - User-provided message text
|
||||||
|
- `Level` - Debug/Info/Warning/Error enum
|
||||||
|
- `Timestamp` - Time.realtimeSinceStartup
|
||||||
|
- `FullFormattedMessage` - Complete formatted string
|
||||||
|
|
||||||
|
---
|
||||||
BIN
docs/media/custom_console.png
Normal file
BIN
docs/media/custom_console.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 146 KiB |
Reference in New Issue
Block a user