Cause: - Live diagnostics required logging from multiple applications without blocking TBF. - Shared nlog.config files caused configuration conflicts. - The original viewer lacked configurable colors and practical log controls. - Inconsistent diagnostic deployment caused log transfer failures and locked shared DLLs during builds. Solution: 1. Added universal diagnostic transport - Added AppDiagnostic.Logging with an NLog target and named-pipe communication. - Added background buffering and automatic reconnection. - Registered the diagnostic connector programmatically. 2. Separated diagnostic window lifecycle - Run AppDiagnostic as an independent process. - Activate the existing window on repeated launches. - Keep TBF running when the diagnostic window closes. 3. Corrected diagnostic deployment - Copy the complete diagnostic output into a dedicated AppDiagnostic subdirectory during build. - Overwrite existing deployment files to prevent inconsistent versions. - Validate and launch the executable from that subdirectory with an explicit working directory. 4. Improved the log viewer - Added configurable substring colors with a palette and live preview. - Added text, level and application filters. - Added line wrapping, clipboard actions and log export. - Added Pause/Resume, Refresh, Clear, auto-scroll and Always on top. - Added connection and log counters. - Persist color rules between sessions. 5. Removed legacy dependencies - Replaced LiveLogCache with structured diagnostic storage. - Removed AppDiagnostic dependencies on log4net and SharedComponents. Validation: - Confirmed successful log transfer when launching AppDiagnostic from TBF.
83 lines
1.8 KiB
C#
83 lines
1.8 KiB
C#
using AppDiagnostic.Logging;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace AppDiagnostic
|
|
{
|
|
public sealed class DiagnosticLogStore
|
|
{
|
|
private const int MaximumLogCount = 10000;
|
|
private static readonly DiagnosticLogStore instance = new DiagnosticLogStore();
|
|
private readonly object syncRoot = new object();
|
|
private readonly List<DiagnosticLogEntry> entries = new List<DiagnosticLogEntry>();
|
|
private long totalReceived;
|
|
private long version;
|
|
|
|
private DiagnosticLogStore()
|
|
{
|
|
}
|
|
|
|
public static DiagnosticLogStore Instance => instance;
|
|
|
|
public long TotalReceived
|
|
{
|
|
get
|
|
{
|
|
lock (syncRoot)
|
|
{
|
|
return totalReceived;
|
|
}
|
|
}
|
|
}
|
|
|
|
public long Version
|
|
{
|
|
get
|
|
{
|
|
lock (syncRoot)
|
|
{
|
|
return version;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Add(DiagnosticLogEntry entry)
|
|
{
|
|
if (entry == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
lock (syncRoot)
|
|
{
|
|
entries.Add(entry);
|
|
totalReceived++;
|
|
version++;
|
|
|
|
int excessCount = entries.Count - MaximumLogCount;
|
|
if (excessCount > 0)
|
|
{
|
|
entries.RemoveRange(0, excessCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
public List<DiagnosticLogEntry> GetSnapshot()
|
|
{
|
|
lock (syncRoot)
|
|
{
|
|
return entries.ToList();
|
|
}
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
lock (syncRoot)
|
|
{
|
|
entries.Clear();
|
|
version++;
|
|
}
|
|
}
|
|
}
|
|
}
|