diff --git a/Plugins/IngameDebugConsole/Editor/DebugLogManagerEditor.cs b/Plugins/IngameDebugConsole/Editor/DebugLogManagerEditor.cs index 2309a38..1ce60b3 100644 --- a/Plugins/IngameDebugConsole/Editor/DebugLogManagerEditor.cs +++ b/Plugins/IngameDebugConsole/Editor/DebugLogManagerEditor.cs @@ -17,6 +17,8 @@ public class DebugLogManagerEditor : Editor private SerializedProperty toggleKey; private SerializedProperty enableSearchbar; private SerializedProperty topSearchbarMinWidth; + private SerializedProperty captureLogTimestamps; + private SerializedProperty alwaysDisplayTimestamps; private SerializedProperty clearCommandAfterExecution; private SerializedProperty commandHistorySize; private SerializedProperty showCommandSuggestions; @@ -41,6 +43,8 @@ private void OnEnable() #endif enableSearchbar = serializedObject.FindProperty( "enableSearchbar" ); topSearchbarMinWidth = serializedObject.FindProperty( "topSearchbarMinWidth" ); + captureLogTimestamps = serializedObject.FindProperty( "captureLogTimestamps" ); + alwaysDisplayTimestamps = serializedObject.FindProperty( "alwaysDisplayTimestamps" ); clearCommandAfterExecution = serializedObject.FindProperty( "clearCommandAfterExecution" ); commandHistorySize = serializedObject.FindProperty( "commandHistorySize" ); showCommandSuggestions = serializedObject.FindProperty( "showCommandSuggestions" ); @@ -76,6 +80,10 @@ public override void OnInspectorGUI() if( enableSearchbar.boolValue ) DrawSubProperty( topSearchbarMinWidth ); + EditorGUILayout.PropertyField( captureLogTimestamps ); + if( captureLogTimestamps.boolValue ) + DrawSubProperty( alwaysDisplayTimestamps ); + EditorGUILayout.PropertyField( clearCommandAfterExecution ); EditorGUILayout.PropertyField( commandHistorySize ); EditorGUILayout.PropertyField( showCommandSuggestions ); diff --git a/Plugins/IngameDebugConsole/Prefabs/DebugLogItem.prefab b/Plugins/IngameDebugConsole/Prefabs/DebugLogItem.prefab index c80442f..c4af8f9 100644 --- a/Plugins/IngameDebugConsole/Prefabs/DebugLogItem.prefab +++ b/Plugins/IngameDebugConsole/Prefabs/DebugLogItem.prefab @@ -71,23 +71,6 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!1 &173670 -GameObject: - m_ObjectHideFlags: 0 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - serializedVersion: 5 - m_Component: - - component: {fileID: 22457652} - - component: {fileID: 22240404} - - component: {fileID: 11465282} - m_Layer: 5 - m_Name: LogText - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 --- !u!114 &11404142 MonoBehaviour: m_ObjectHideFlags: 1 @@ -129,7 +112,7 @@ MonoBehaviour: transformComponent: {fileID: 22479264} imageComponent: {fileID: 11459012} canvasGroupComponent: {fileID: 225819852034701160} - logText: {fileID: 11465282} + logText: {fileID: 114694493629914950} logTypeImage: {fileID: 11404142} logCountParent: {fileID: 104862} logCountText: {fileID: 11432936} @@ -262,39 +245,6 @@ MonoBehaviour: m_FillAmount: 1 m_FillClockwise: 1 m_FillOrigin: 0 ---- !u!114 &11465282 -MonoBehaviour: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - m_GameObject: {fileID: 173670} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 0.83823526, g: 0.84439874, b: 0.84439874, a: 1} - m_RaycastTarget: 0 - m_OnCullStateChanged: - m_PersistentCalls: - m_Calls: [] - m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, - Version=1.0.0.0, Culture=neutral, PublicKeyToken=null - m_FontData: - m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} - m_FontSize: 15 - m_FontStyle: 0 - m_BestFit: 0 - m_MinSize: 1 - m_MaxSize: 40 - m_Alignment: 3 - m_AlignByGeometry: 0 - m_RichText: 1 - m_HorizontalOverflow: 1 - m_VerticalOverflow: 0 - m_LineSpacing: 1 - m_Text: Debug.Log summary --- !u!222 &22200920 CanvasRenderer: m_ObjectHideFlags: 1 @@ -307,12 +257,6 @@ CanvasRenderer: m_PrefabParentObject: {fileID: 0} m_PrefabInternal: {fileID: 100100000} m_GameObject: {fileID: 104862} ---- !u!222 &22240404 -CanvasRenderer: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - m_GameObject: {fileID: 173670} --- !u!222 &22262284 CanvasRenderer: m_ObjectHideFlags: 1 @@ -358,26 +302,8 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0.5} m_AnchorMax: {x: 0, y: 0.5} - m_AnchoredPosition: {x: 25, y: 0} - m_SizeDelta: {x: 32, y: 32} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!224 &22457652 -RectTransform: - m_ObjectHideFlags: 1 - m_PrefabParentObject: {fileID: 0} - m_PrefabInternal: {fileID: 100100000} - m_GameObject: {fileID: 173670} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: [] - m_Father: {fileID: 22479264} - m_RootOrder: 1 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 22.5, y: 0} - m_SizeDelta: {x: -55, y: -2} + m_AnchoredPosition: {x: 18, y: 0} + m_SizeDelta: {x: 25, y: 25} m_Pivot: {x: 0.5, y: 0.5} --- !u!224 &22461494 RectTransform: @@ -409,7 +335,7 @@ RectTransform: m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - {fileID: 22427300} - - {fileID: 22457652} + - {fileID: 224737693311518052} - {fileID: 22461494} - {fileID: 224006190298411330} m_Father: {fileID: 0} @@ -418,7 +344,7 @@ RectTransform: m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 1, y: 1} m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 40} + m_SizeDelta: {x: 0, y: 35} m_Pivot: {x: 0, y: 1} --- !u!1001 &100100000 Prefab: @@ -466,6 +392,23 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 +--- !u!1 &1785910143472904 +GameObject: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + serializedVersion: 5 + m_Component: + - component: {fileID: 224737693311518052} + - component: {fileID: 222175805939703770} + - component: {fileID: 114694493629914950} + m_Layer: 5 + m_Name: LogText + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 --- !u!114 &114119781176956926 MonoBehaviour: m_ObjectHideFlags: 1 @@ -526,6 +469,39 @@ MonoBehaviour: m_VerticalOverflow: 0 m_LineSpacing: 1 m_Text: Copy +--- !u!114 &114694493629914950 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1785910143472904} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.83823526, g: 0.84439874, b: 0.84439874, a: 1} + m_RaycastTarget: 0 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 15 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 1 + m_MaxSize: 40 + m_Alignment: 3 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Debug.Log summary --- !u!114 &114694923173451186 MonoBehaviour: m_ObjectHideFlags: 1 @@ -578,6 +554,12 @@ MonoBehaviour: m_CallState: 2 m_TypeName: UnityEngine.UI.Button+ButtonClickedEvent, UnityEngine.UI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null +--- !u!222 &222175805939703770 +CanvasRenderer: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1785910143472904} --- !u!222 &222313182602304162 CanvasRenderer: m_ObjectHideFlags: 1 @@ -607,8 +589,26 @@ RectTransform: m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 0} m_AnchoredPosition: {x: 0, y: 2} - m_SizeDelta: {x: -100, y: 36} + m_SizeDelta: {x: -70, y: 36} m_Pivot: {x: 0.5, y: 0} +--- !u!224 &224737693311518052 +RectTransform: + m_ObjectHideFlags: 1 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 100100000} + m_GameObject: {fileID: 1785910143472904} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 22479264} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 15, y: 0} + m_SizeDelta: {x: -40, y: -2} + m_Pivot: {x: 0.5, y: 0.5} --- !u!224 &224887990600088790 RectTransform: m_ObjectHideFlags: 1 diff --git a/Plugins/IngameDebugConsole/Scripts/DebugLogEntry.cs b/Plugins/IngameDebugConsole/Scripts/DebugLogEntry.cs index cc582c4..8914ece 100644 --- a/Plugins/IngameDebugConsole/Scripts/DebugLogEntry.cs +++ b/Plugins/IngameDebugConsole/Scripts/DebugLogEntry.cs @@ -1,4 +1,8 @@ -using UnityEngine; +//#define IDG_OMIT_ELAPSED_TIME +//#define IDG_OMIT_FRAMECOUNT + +using System.Text; +using UnityEngine; // Container for a simple debug entry namespace IngameDebugConsole @@ -89,4 +93,73 @@ public bool MatchesSearchTerm( string searchTerm ) ( stackTrace != null && stackTrace.IndexOf( searchTerm, System.StringComparison.OrdinalIgnoreCase ) >= 0 ); } } + + public struct DebugLogEntryTimestamp + { + public readonly System.DateTime dateTime; +#if !IDG_OMIT_ELAPSED_TIME + public readonly float elapsedSeconds; +#endif +#if !IDG_OMIT_FRAMECOUNT + public readonly int frameCount; +#endif + + public DebugLogEntryTimestamp( System.TimeSpan localTimeUtcOffset ) + { + // It is 10 times faster to cache local time's offset from UtcNow and add it to UtcNow to get local time at any time + dateTime = System.DateTime.UtcNow + localTimeUtcOffset; +#if !IDG_OMIT_ELAPSED_TIME + elapsedSeconds = Time.realtimeSinceStartup; +#endif +#if !IDG_OMIT_FRAMECOUNT + frameCount = Time.frameCount; +#endif + } + + public void AppendTime( StringBuilder sb ) + { + // Add DateTime in format: [HH:mm:ss] + sb.Append( "[" ); + + int hour = dateTime.Hour; + if( hour >= 10 ) + sb.Append( hour ); + else + sb.Append( "0" ).Append( hour ); + + sb.Append( ":" ); + + int minute = dateTime.Minute; + if( minute >= 10 ) + sb.Append( minute ); + else + sb.Append( "0" ).Append( minute ); + + sb.Append( ":" ); + + int second = dateTime.Second; + if( second >= 10 ) + sb.Append( second ); + else + sb.Append( "0" ).Append( second ); + + sb.Append( "]" ); + } + + public void AppendFullTimestamp( StringBuilder sb ) + { + AppendTime( sb ); + +#if !IDG_OMIT_ELAPSED_TIME && !IDG_OMIT_FRAMECOUNT + // Append elapsed seconds and frame count in format: [1.0s at #Frame] + sb.Append( "[" ).Append( elapsedSeconds.ToString( "F1" ) ).Append( "s at " ).Append( "#" ).Append( frameCount ).Append( "]" ); +#elif !IDG_OMIT_ELAPSED_TIME + // Append elapsed seconds in format: [1.0s] + sb.Append( "[" ).Append( elapsedSeconds.ToString( "F1" ) ).Append( "s]" ); +#elif !IDG_OMIT_FRAMECOUNT + // Append frame count in format: [#Frame] + sb.Append( "[#" ).Append( frameCount ).Append( "]" ); +#endif + } + } } \ No newline at end of file diff --git a/Plugins/IngameDebugConsole/Scripts/DebugLogIndexList.cs b/Plugins/IngameDebugConsole/Scripts/DebugLogIndexList.cs index 95bb069..e98dd53 100644 --- a/Plugins/IngameDebugConsole/Scripts/DebugLogIndexList.cs +++ b/Plugins/IngameDebugConsole/Scripts/DebugLogIndexList.cs @@ -1,25 +1,29 @@ namespace IngameDebugConsole { - public class DebugLogIndexList + public class DebugLogIndexList { - private int[] indices; + private T[] indices; private int size; public int Count { get { return size; } } - public int this[int index] { get { return indices[index]; } } + public T this[int index] + { + get { return indices[index]; } + set { indices[index] = value; } + } public DebugLogIndexList() { - indices = new int[64]; + indices = new T[64]; size = 0; } - public void Add( int index ) + public void Add( T value ) { if( size == indices.Length ) System.Array.Resize( ref indices, size * 2 ); - indices[size++] = index; + indices[size++] = value; } public void Clear() @@ -27,9 +31,9 @@ public void Clear() size = 0; } - public int IndexOf( int index ) + public int IndexOf( T value ) { - return System.Array.IndexOf( indices, index ); + return System.Array.IndexOf( indices, value ); } } } \ No newline at end of file diff --git a/Plugins/IngameDebugConsole/Scripts/DebugLogItem.cs b/Plugins/IngameDebugConsole/Scripts/DebugLogItem.cs index 8082388..7256de7 100644 --- a/Plugins/IngameDebugConsole/Scripts/DebugLogItem.cs +++ b/Plugins/IngameDebugConsole/Scripts/DebugLogItem.cs @@ -1,6 +1,7 @@ using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; +using System.Text; #if UNITY_EDITOR using UnityEditor; using System.Text.RegularExpressions; @@ -82,19 +83,25 @@ private static AndroidJavaObject Context private DebugLogEntry logEntry; public DebugLogEntry Entry { get { return logEntry; } } + private DebugLogEntryTimestamp? logEntryTimestamp; + public DebugLogEntryTimestamp? Timestamp { get { return logEntryTimestamp; } } + // Index of the entry in the list of entries private int entryIndex; public int Index { get { return entryIndex; } } + private bool isExpanded; + public bool Expanded { get { return isExpanded; } } + private Vector2 logTextOriginalPosition; private Vector2 logTextOriginalSize; private float copyLogButtonHeight; - private DebugLogRecycledListView manager; + private DebugLogRecycledListView listView; - public void Initialize( DebugLogRecycledListView manager ) + public void Initialize( DebugLogRecycledListView listView ) { - this.manager = manager; + this.listView = listView; logTextOriginalPosition = logText.rectTransform.anchoredPosition; logTextOriginalSize = logText.rectTransform.sizeDelta; @@ -105,16 +112,18 @@ public void Initialize( DebugLogRecycledListView manager ) #endif } - public void SetContent( DebugLogEntry logEntry, int entryIndex, bool isExpanded ) + public void SetContent( DebugLogEntry logEntry, DebugLogEntryTimestamp? logEntryTimestamp, int entryIndex, bool isExpanded ) { this.logEntry = logEntry; + this.logEntryTimestamp = logEntryTimestamp; this.entryIndex = entryIndex; + this.isExpanded = isExpanded; Vector2 size = transformComponent.sizeDelta; if( isExpanded ) { logText.horizontalOverflow = HorizontalWrapMode.Wrap; - size.y = manager.SelectedItemHeight; + size.y = listView.SelectedItemHeight; if( !copyLogButton.gameObject.activeSelf ) { @@ -127,7 +136,7 @@ public void SetContent( DebugLogEntry logEntry, int entryIndex, bool isExpanded else { logText.horizontalOverflow = HorizontalWrapMode.Overflow; - size.y = manager.ItemHeight; + size.y = listView.ItemHeight; if( copyLogButton.gameObject.activeSelf ) { @@ -137,9 +146,10 @@ public void SetContent( DebugLogEntry logEntry, int entryIndex, bool isExpanded logText.rectTransform.sizeDelta = logTextOriginalSize; } } + transformComponent.sizeDelta = size; - logText.text = isExpanded ? logEntry.ToString() : logEntry.logString; + SetText( logEntry, logEntryTimestamp, isExpanded ); logTypeImage.sprite = logEntry.logTypeSpriteRepresentation; } @@ -159,6 +169,39 @@ public void HideCount() logCountParent.SetActive( false ); } + // Update the debug entry's displayed timestamp + public void UpdateTimestamp( DebugLogEntryTimestamp timestamp ) + { + logEntryTimestamp = timestamp; + + if( isExpanded || listView.manager.alwaysDisplayTimestamps ) + SetText( logEntry, timestamp, isExpanded ); + } + + private void SetText( DebugLogEntry logEntry, DebugLogEntryTimestamp? logEntryTimestamp, bool isExpanded ) + { + if( !logEntryTimestamp.HasValue || ( !isExpanded && !listView.manager.alwaysDisplayTimestamps ) ) + logText.text = isExpanded ? logEntry.ToString() : logEntry.logString; + else + { + StringBuilder sb = listView.manager.sharedStringBuilder; + sb.Length = 0; + + if( isExpanded ) + { + logEntryTimestamp.Value.AppendFullTimestamp( sb ); + sb.Append( ": " ).Append( logEntry.ToString() ); + } + else + { + logEntryTimestamp.Value.AppendTime( sb ); + sb.Append( " " ).Append( logEntry.logString ); + } + + logText.text = sb.ToString(); + } + } + // This log item is clicked, show the debug entry's stack trace public void OnPointerClick( PointerEventData eventData ) { @@ -176,16 +219,16 @@ public void OnPointerClick( PointerEventData eventData ) } } else - manager.OnLogItemClicked( this ); + listView.OnLogItemClicked( this ); #else - manager.OnLogItemClicked( this ); + listView.OnLogItemClicked( this ); #endif } public void CopyLog() { #if UNITY_EDITOR || !UNITY_WEBGL - string log = logEntry.ToString(); + string log = GetCopyContent(); if( string.IsNullOrEmpty( log ) ) return; @@ -199,12 +242,28 @@ public void CopyLog() #endif } - public float CalculateExpandedHeight( string content ) + internal string GetCopyContent() + { + if( !logEntryTimestamp.HasValue ) + return logEntry.ToString(); + else + { + StringBuilder sb = listView.manager.sharedStringBuilder; + sb.Length = 0; + + logEntryTimestamp.Value.AppendFullTimestamp( sb ); + sb.Append( ": " ).Append( logEntry.ToString() ); + + return sb.ToString(); + } + } + + public float CalculateExpandedHeight( DebugLogEntry logEntry, DebugLogEntryTimestamp? logEntryTimestamp ) { string text = logText.text; HorizontalWrapMode wrapMode = logText.horizontalOverflow; - logText.text = content; + SetText( logEntry, logEntryTimestamp, true ); logText.horizontalOverflow = HorizontalWrapMode.Wrap; float result = logText.preferredHeight + copyLogButtonHeight; @@ -212,7 +271,7 @@ public float CalculateExpandedHeight( string content ) logText.text = text; logText.horizontalOverflow = wrapMode; - return Mathf.Max( manager.ItemHeight, result ); + return Mathf.Max( listView.ItemHeight, result ); } // Return a string containing complete information about the debug entry diff --git a/Plugins/IngameDebugConsole/Scripts/DebugLogItemCopyWebGL.cs b/Plugins/IngameDebugConsole/Scripts/DebugLogItemCopyWebGL.cs index 60d5e38..a02f3ad 100644 --- a/Plugins/IngameDebugConsole/Scripts/DebugLogItemCopyWebGL.cs +++ b/Plugins/IngameDebugConsole/Scripts/DebugLogItemCopyWebGL.cs @@ -21,7 +21,7 @@ public void Initialize( DebugLogItem logItem ) public void OnPointerDown( PointerEventData eventData ) { - string log = logItem.Entry.ToString(); + string log = logItem.GetCopyContent(); if( !string.IsNullOrEmpty( log ) ) IngameDebugConsoleStartCopy( log ); } diff --git a/Plugins/IngameDebugConsole/Scripts/DebugLogManager.cs b/Plugins/IngameDebugConsole/Scripts/DebugLogManager.cs index 8b202a1..58dff4c 100644 --- a/Plugins/IngameDebugConsole/Scripts/DebugLogManager.cs +++ b/Plugins/IngameDebugConsole/Scripts/DebugLogManager.cs @@ -106,6 +106,16 @@ public class DebugLogManager : MonoBehaviour [Tooltip( "Width of the canvas determines whether the searchbar will be located inside the menu bar or underneath the menu bar. This way, the menu bar doesn't get too crowded on narrow screens. This value determines the minimum width of the canvas for the searchbar to appear inside the menu bar" )] private float topSearchbarMinWidth = 360f; + [SerializeField] + [HideInInspector] + [Tooltip( "If enabled, the arrival times of logs will be recorded and displayed when a log is expanded" )] + private bool captureLogTimestamps = false; + + [SerializeField] + [HideInInspector] + [Tooltip( "If enabled, timestamps will be displayed for logs even if they aren't expanded" )] + internal bool alwaysDisplayTimestamps = false; + [SerializeField] [HideInInspector] [Tooltip( "If enabled, the command input field at the bottom of the console window will automatically be cleared after entering a command" )] @@ -291,16 +301,19 @@ public bool PopupEnabled // List of unique debug entries (duplicates of entries are not kept) private List collapsedLogEntries; + private List collapsedLogEntriesTimestamps; // Dictionary to quickly find if a log already exists in collapsedLogEntries private Dictionary collapsedLogEntriesMap; // The order the collapsedLogEntries are received // (duplicate entries have the same index (value)) - private DebugLogIndexList uncollapsedLogEntriesIndices; + private DebugLogIndexList uncollapsedLogEntriesIndices; + private DebugLogIndexList uncollapsedLogEntriesTimestamps; // Filtered list of debug entries to show - private DebugLogIndexList indicesOfListEntriesToShow; + private DebugLogIndexList indicesOfListEntriesToShow; + private DebugLogIndexList timestampsOfListEntriesToShow; // The log entry that must be focused this frame private int indexOfLogEntryToSelectAndFocus = -1; @@ -318,7 +331,6 @@ public bool PopupEnabled private int visibleCommandSuggestionInstances = 0; private List matchingCommandSuggestions; private List commandCaretIndexIncrements; - private StringBuilder commandSuggestionsStringBuilder; private string commandInputFieldPrevCommand; private string commandInputFieldPrevCommandName; private int commandInputFieldPrevParamCount = -1; @@ -334,6 +346,12 @@ public bool PopupEnabled private int commandHistoryIndex = -1; private string unfinishedCommand; + // StringBuilder used by various functions + internal StringBuilder sharedStringBuilder; + + // Offset of DateTime.Now from DateTime.UtcNow + private System.TimeSpan localTimeUtcOffset; + // Required in ValidateScrollPosition() function private PointerEventData nullPointerEventData; @@ -374,7 +392,7 @@ private void Awake() commandHistory = new CircularBuffer( commandHistorySize ); logEntriesLock = new object(); - commandSuggestionsStringBuilder = new StringBuilder( 128 ); + sharedStringBuilder = new StringBuilder( 1024 ); canvasTR = (RectTransform) transform; logItemsScrollRectTR = (RectTransform) logItemsScrollRect.transform; @@ -399,10 +417,17 @@ private void Awake() collapsedLogEntries = new List( 128 ); collapsedLogEntriesMap = new Dictionary( 128 ); - uncollapsedLogEntriesIndices = new DebugLogIndexList(); - indicesOfListEntriesToShow = new DebugLogIndexList(); + uncollapsedLogEntriesIndices = new DebugLogIndexList(); + indicesOfListEntriesToShow = new DebugLogIndexList(); + + if( captureLogTimestamps ) + { + collapsedLogEntriesTimestamps = new List( 128 ); + uncollapsedLogEntriesTimestamps = new DebugLogIndexList(); + timestampsOfListEntriesToShow = new DebugLogIndexList(); + } - recycledListView.Initialize( this, collapsedLogEntries, indicesOfListEntriesToShow, logItemPrefab.Transform.sizeDelta.y ); + recycledListView.Initialize( this, collapsedLogEntries, indicesOfListEntriesToShow, timestampsOfListEntriesToShow, logItemPrefab.Transform.sizeDelta.y ); recycledListView.UpdateItemsInTheList( true ); if( minimumWidth < 100f ) @@ -444,6 +469,7 @@ private void Awake() filterErrorButton.GetComponent