Limit MsgView log queue growth to reduce memory usage

This commit is contained in:
xpzhxhm 2026-03-14 22:37:20 +00:00
parent 214a09bc48
commit d186b1d8e9
3 changed files with 120 additions and 31 deletions

View file

@ -2,9 +2,15 @@ namespace ServiceLib.ViewModels;
public class MsgViewModel : MyReactiveObject
{
private const int MaxQueuedMessagesVisible = 2_000;
private const int MaxQueuedMessagesHidden = 500;
private const int MaxFlushMessages = 200;
private const int MaxFlushChars = 64 * 1024;
private readonly ConcurrentQueue<string> _queueMsg = new();
private volatile bool _lastMsgFilterNotAvailable;
private int _queuedMessageCount = 0;
private int _showLock = 0; // 0 = unlocked, 1 = locked
private long _droppedMessageCount = 0;
public int NumMaxMsg { get; } = 500;
[Reactive]
@ -31,19 +37,28 @@ public class MsgViewModel : MyReactiveObject
AppEvents.SendMsgViewRequested
.AsObservable()
//.ObserveOn(RxSchedulers.MainThreadScheduler)
//.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(content => _ = AppendQueueMsg(content));
}
private async Task AppendQueueMsg(string msg)
private Task AppendQueueMsg(string msg)
{
if (AutoRefresh == false)
{
return;
return Task.CompletedTask;
}
EnqueueQueueMsg(msg);
if (!EnqueueQueueMsg(msg))
{
return Task.CompletedTask;
}
TryScheduleDrain();
return Task.CompletedTask;
}
private void TryScheduleDrain()
{
if (!AppManager.Instance.ShowInTaskbar)
{
return;
@ -54,25 +69,59 @@ public class MsgViewModel : MyReactiveObject
return;
}
_ = DrainQueueMsgAsync();
}
private async Task DrainQueueMsgAsync()
{
try
{
await Task.Delay(500).ConfigureAwait(false);
var sb = new StringBuilder();
while (_queueMsg.TryDequeue(out var line))
var batch = DequeueBatch();
if (_updateView != null && !string.IsNullOrEmpty(batch))
{
sb.Append(line);
await _updateView(EViewAction.DispatcherShowMsg, batch);
}
await _updateView?.Invoke(EViewAction.DispatcherShowMsg, sb.ToString());
}
finally
{
Interlocked.Exchange(ref _showLock, 0);
}
if (AppManager.Instance.ShowInTaskbar && !_queueMsg.IsEmpty)
{
TryScheduleDrain();
}
}
private void EnqueueQueueMsg(string msg)
private string DequeueBatch()
{
var sb = new StringBuilder();
var droppedCount = Interlocked.Exchange(ref _droppedMessageCount, 0);
if (droppedCount > 0)
{
sb.Append($"----- Message queue trimmed: dropped {droppedCount} messages -----{Environment.NewLine}");
}
var dequeuedCount = 0;
while (dequeuedCount < MaxFlushMessages
&& _queueMsg.TryDequeue(out var line))
{
Interlocked.Decrement(ref _queuedMessageCount);
sb.Append(line);
dequeuedCount++;
if (sb.Length >= MaxFlushChars)
{
break;
}
}
return sb.ToString();
}
private bool EnqueueQueueMsg(string msg)
{
//filter msg
if (MsgFilter.IsNotEmpty() && !_lastMsgFilterNotAvailable)
@ -81,23 +130,41 @@ public class MsgViewModel : MyReactiveObject
{
if (!Regex.IsMatch(msg, MsgFilter))
{
return;
return false;
}
}
catch (Exception ex)
{
_queueMsg.Enqueue(ex.Message);
msg = ex.Message;
_lastMsgFilterNotAvailable = true;
}
}
_queueMsg.Enqueue(msg);
if (!msg.EndsWith(Environment.NewLine))
_queueMsg.Enqueue(NormalizeMessage(msg));
Interlocked.Increment(ref _queuedMessageCount);
TrimQueuedMessages();
return true;
}
private void TrimQueuedMessages()
{
var maxQueuedMessages = AppManager.Instance.ShowInTaskbar
? MaxQueuedMessagesVisible
: MaxQueuedMessagesHidden;
while (Volatile.Read(ref _queuedMessageCount) > maxQueuedMessages
&& _queueMsg.TryDequeue(out _))
{
_queueMsg.Enqueue(Environment.NewLine);
Interlocked.Decrement(ref _queuedMessageCount);
Interlocked.Increment(ref _droppedMessageCount);
}
}
private static string NormalizeMessage(string msg)
{
return msg.EndsWith(Environment.NewLine) ? msg : msg + Environment.NewLine;
}
//public void ClearMsg()
//{
// _queueMsg.Clear();

View file

@ -43,24 +43,32 @@ public partial class MsgView : ReactiveUserControl<MsgViewModel>
private void ShowMsg(object msg)
{
//var lineCount = txtMsg.LineCount;
//if (lineCount > ViewModel?.NumMaxMsg)
//{
// var cutLine = txtMsg.Document.GetLineByNumber(lineCount - KeepLines);
// txtMsg.Document.Remove(0, cutLine.Offset);
//}
if (txtMsg.LineCount > ViewModel?.NumMaxMsg)
{
ClearMsg();
}
txtMsg.AppendText(msg.ToString());
TrimMsg();
if (togScrollToEnd.IsChecked ?? true)
{
txtMsg.ScrollToEnd();
}
}
private void TrimMsg()
{
var maxLines = ViewModel?.NumMaxMsg ?? 500;
if (txtMsg.LineCount <= maxLines || txtMsg.Document == null)
{
return;
}
var firstKeepLine = txtMsg.LineCount - maxLines + 1;
var cutLine = txtMsg.Document.GetLineByNumber(firstKeepLine);
if (cutLine.Offset <= 0)
{
return;
}
txtMsg.Document.Remove(0, cutLine.Offset);
}
public void ClearMsg()
{
txtMsg.Clear();

View file

@ -45,18 +45,32 @@ public partial class MsgView
private void ShowMsg(object msg)
{
if (txtMsg.LineCount > ViewModel?.NumMaxMsg)
{
ClearMsg();
}
txtMsg.AppendText(msg.ToString());
TrimMsg();
if (togScrollToEnd.IsChecked ?? true)
{
txtMsg.ScrollToEnd();
}
}
private void TrimMsg()
{
var maxLines = ViewModel?.NumMaxMsg ?? 500;
if (txtMsg.LineCount <= maxLines)
{
return;
}
var keepFromCharIndex = txtMsg.GetCharacterIndexFromLineIndex(txtMsg.LineCount - maxLines);
if (keepFromCharIndex <= 0)
{
return;
}
txtMsg.Select(0, keepFromCharIndex);
txtMsg.SelectedText = string.Empty;
}
public void ClearMsg()
{
txtMsg.Clear();