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.
114 lines
3.3 KiB
C#
114 lines
3.3 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Runtime.Serialization.Json;
|
|
|
|
namespace AppDiagnostic.Logging
|
|
{
|
|
public static class DiagnosticMessageSerializer
|
|
{
|
|
private static readonly DataContractJsonSerializer Serializer =
|
|
new DataContractJsonSerializer(typeof(DiagnosticLogEntry));
|
|
|
|
private static readonly object SerializerLock = new object();
|
|
|
|
public static byte[] Serialize(DiagnosticLogEntry entry)
|
|
{
|
|
if (entry == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(entry));
|
|
}
|
|
|
|
using (var stream = new MemoryStream())
|
|
{
|
|
lock (SerializerLock)
|
|
{
|
|
Serializer.WriteObject(stream, entry);
|
|
}
|
|
|
|
return stream.ToArray();
|
|
}
|
|
}
|
|
|
|
public static DiagnosticLogEntry Deserialize(byte[] payload)
|
|
{
|
|
if (payload == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(payload));
|
|
}
|
|
|
|
if (payload.Length == 0 ||
|
|
payload.Length > DiagnosticProtocol.MaximumMessageSize)
|
|
{
|
|
throw new InvalidDataException("Invalid diagnostic message size.");
|
|
}
|
|
|
|
using (var stream = new MemoryStream(payload, false))
|
|
{
|
|
lock (SerializerLock)
|
|
{
|
|
return (DiagnosticLogEntry)Serializer.ReadObject(stream);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void WriteFramed(Stream stream, DiagnosticLogEntry entry)
|
|
{
|
|
if (stream == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(stream));
|
|
}
|
|
|
|
byte[] payload = Serialize(entry);
|
|
|
|
if (payload.Length > DiagnosticProtocol.MaximumMessageSize)
|
|
{
|
|
throw new InvalidDataException("Diagnostic message is too large.");
|
|
}
|
|
|
|
byte[] length = BitConverter.GetBytes(payload.Length);
|
|
stream.Write(length, 0, length.Length);
|
|
stream.Write(payload, 0, payload.Length);
|
|
stream.Flush();
|
|
}
|
|
|
|
public static DiagnosticLogEntry ReadFramed(Stream stream)
|
|
{
|
|
if (stream == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(stream));
|
|
}
|
|
|
|
byte[] lengthBuffer = ReadExactly(stream, sizeof(int));
|
|
int payloadLength = BitConverter.ToInt32(lengthBuffer, 0);
|
|
|
|
if (payloadLength <= 0 ||
|
|
payloadLength > DiagnosticProtocol.MaximumMessageSize)
|
|
{
|
|
throw new InvalidDataException("Invalid diagnostic message size.");
|
|
}
|
|
|
|
return Deserialize(ReadExactly(stream, payloadLength));
|
|
}
|
|
|
|
private static byte[] ReadExactly(Stream stream, int count)
|
|
{
|
|
byte[] buffer = new byte[count];
|
|
int offset = 0;
|
|
|
|
while (offset < count)
|
|
{
|
|
int bytesRead = stream.Read(buffer, offset, count - offset);
|
|
|
|
if (bytesRead == 0)
|
|
{
|
|
throw new EndOfStreamException();
|
|
}
|
|
|
|
offset += bytesRead;
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
}
|
|
}
|