Skip to main content

Communication Tracing

Every IChannel raises PacketTraced when raw bytes are sent or received. The event does not parse protocols; it records actual TX/RX bytes for field diagnostics, communication windows, and fault snapshots.

Subscribe Directly​

var meter = app.Channels.Get("meter");
meter.PacketTraced += (_, e) =>
{
var direction = e.Direction == ChannelTraceDirection.Sent ? "TX" : "RX";
Console.WriteLine($"{e.Timestamp:O} {meter.Name} {direction} {e.Hex}");
};

Hex is a continuous uppercase hex string. Format e.Data.Span yourself if you need spaces.

Keep the Last N Entries​

var meter = app.Channels.Get("meter");
using var trace = new ChannelTraceBuffer(meter, capacity: 200);

await app.StartAsync();
await meter.WriteAsync(new byte[] { 0x01, 0x03, 0x00, 0x00 });

foreach (var entry in trace.Entries)
{
Console.WriteLine($"{entry.Timestamp:HH:mm:ss.fff} {entry.ChannelName} {entry.Direction} {entry.Hex}");
}

Entries returns a snapshot from oldest to newest. When capacity is full, the oldest entries are removed.

Write to ILogger​

Host-level tracing uses AddCommunicationLogging. It attaches a logger to existing and later channels, and reattaches after hot reload rebuilds a channel with the same name. The default level is Debug; the EventId is ZeusLogEvents.PacketTrace.

await using var app = ZeusHost.Create(builder =>
{
builder.AddVirtualChannel("meter");
builder.AddCommunicationLogging();
});

To trace a single channel, or only while a diagnostic window is open, still use ChannelTraceLogger directly:

var meter = app.Channels.Get("meter");
var logger = app.Services.GetRequiredService<ILoggerFactory>().CreateLogger("comm.trace");

using var traceLog = new ChannelTraceLogger(meter, logger);
await app.StartAsync();
await meter.WriteAsync(new byte[] { 0x01, 0x03, 0x00, 0x00 });

The log template includes structured fields: Channel, Direction, ByteCount, and Hex. Dispose the handle to unsubscribe.

If you need files, databases, or packet-capture systems, subscribe to PacketTraced or configure an ILogger sink and choose the format, path, rotation, and retention in the application. Zeus no longer ships a file trace writer, so storage policy stays outside the framework.

Trace vs DataReceived​

APIPurpose
DataReceivedBusiness or protocol code consumes received bytes
PacketTracedDiagnostic code records sent and received raw bytes
ChannelTraceBufferRolling in-memory trace
ChannelTraceLoggerStructured ILogger trace
AddCommunicationLoggingAttaches ChannelTraceLogger to every channel and follows hot reload

PacketTraced and DataReceived may fire on IO threads. In WinForms, use control binding or BeginInvoke; in WPF, hand the data to a ViewModel and marshal through WpfUiDispatcher.