in src/Azure.IIoT.OpcUa.Publisher/src/Services/RuntimeStateReporter.cs [521:721]
private static void WriteDiagnosticsToConsole(
IEnumerable<(string, WriterGroupDiagnosticModel)> diagnostics, bool includeResourceInfo)
{
var builder = new StringBuilder();
foreach (var (writerGroupId, info) in diagnostics)
{
builder = Append(builder, writerGroupId, info, includeResourceInfo);
}
if (builder.Length > 0)
{
Console.Out.WriteLine(builder.ToString());
}
static StringBuilder Append(StringBuilder builder, string writerGroupId,
WriterGroupDiagnosticModel info, bool includeResourceInfo)
{
var s = info.IngestionDuration.TotalSeconds == 0 ? 1 : info.IngestionDuration.TotalSeconds;
var min = info.IngestionDuration.TotalMinutes == 0 ? 1 : info.IngestionDuration.TotalMinutes;
var eventsPerSec = info.IngressEvents / s;
var eventNotificationsPerSec = info.IngressEventNotifications / s;
var sentMessagesPerSecFormatted = info.OutgressIoTMessageCount > 0 ? $"({info.SentMessagesPerSec:n2}/s)"
: string.Empty;
var keepAliveChangesPerSecFormatted = info.IngressKeepAliveNotifications > 0 ?
$"(All time ~{info.IngressKeepAliveNotifications / min:n2}/min)"
: string.Empty;
var dataChangesPerSecFormatted =
Format(info.IngressDataChanges, info.IngressDataChangesInLastMinute, s);
var valueChangesPerSecFormatted =
Format(info.IngressValueChanges, info.IngressValueChangesInLastMinute, s);
var eventsPerSecFormatted =
Format(info.IngressEvents, info.IngressEventsInLastMinute, s);
var eventNotificationsPerSecFormatted =
Format(info.IngressEventNotifications, info.IngressEventNotificationsInLastMinute, s);
var heartbeatsPerSecFormatted =
Format(info.IngressHeartbeats, info.IngressHeartbeatsInLastMinute, s);
var cyclicReadsPerSecFormatted =
Format(info.IngressCyclicReads, info.IngressCyclicReadsInLastMinute, s);
var sampledValuesPerSecFormatted =
Format(info.IngressSampledValues, info.IngressSampledValuesInLastMinute, s);
var modelChangesPerSecFormatted =
Format(info.IngressModelChanges, info.IngressModelChangesInLastMinute, s);
var serverQueueOverflowsPerSecFormatted =
Format(info.ServerQueueOverflows, info.ServerQueueOverflowsInLastMinute, s);
static string Format(long changes, long lastMinute, double s)
{
var dataChangesPerSecLastMin = lastMinute / Math.Min(s, 60d);
return changes > 0 ?
$"(All time ~{changes / s:n2}/s; {lastMinute:n0} in last 60s ~{dataChangesPerSecLastMin:n2}/s)"
: string.Empty;
}
var chunkUsageFormatted = Math.Round(info.EncoderAvgIoTChunkUsage, 2) > 0 ?
$"(Avg Chunk (4 KB) usage {info.EncoderAvgIoTChunkUsage:n2}; {info.EstimatedIoTChunksPerDay:n1}/day estimated)"
: string.Empty;
var connectivityState = info.NumberOfConnectedEndpoints > 0 ? (info.NumberOfDisconnectedEndpoints > 0 ?
"(Partially Connected)" : "(Connected)") : "(Disconnected)";
var sb = builder.AppendLine()
.Append(" DIAGNOSTICS INFORMATION for : ")
.Append(info.WriterGroupName ?? Constants.DefaultWriterGroupName)
.Append(" (")
.AppendFormat(CultureInfo.CurrentCulture, "{0:0}", writerGroupId)
.AppendLine(")")
.Append(" # OPC Publisher Version (Runtime) : ")
.AppendLine(info.PublisherVersion)
;
if (includeResourceInfo)
{
sb = sb
.Append(" # Cpu (%limit/%req/%used) : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:p2}", info.CpuLimitUtilization)
.Append(" | ")
.AppendFormat(CultureInfo.CurrentCulture, "{0:p2}", info.CpuRequestUtilization)
.Append(" (")
.AppendFormat(CultureInfo.CurrentCulture, "{0:p2}", info.CpuUsedPercentage)
.AppendLine(")")
.Append(" # Memory (%limit/%used/total used) : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:p2}", info.MemoryLimitUtilization)
.Append(" | ")
.AppendFormat(CultureInfo.CurrentCulture, "{0:p2}", info.MemoryUsedPercentage)
.Append(" (")
.AppendFormat(CultureInfo.CurrentCulture, "{0:n0}", info.MemoryUsedInBytes / 1000d)
.AppendLine(" KB)")
;
}
return sb
.Append(" # Ingest duration (dd:hh:mm:ss)/Time : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:dd\\:hh\\:mm\\:ss}", info.IngestionDuration)
.Append(" | ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:O}", info.Timestamp)
.AppendLine()
.Append(" # Number of writers in group : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:0}", info.NumberOfWriters)
.AppendLine()
.Append(" # Good/Total number of items : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.MonitoredOpcNodesSucceededCount).Append(" | ")
.AppendFormat(CultureInfo.CurrentCulture, "{0:n0}", info.MonitoredOpcNodesCount)
.AppendLine()
.Append(" # Bad/Late number of items : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.MonitoredOpcNodesFailedCount).Append(" | ")
.AppendFormat(CultureInfo.CurrentCulture, "{0:n0}", info.MonitoredOpcNodesLateCount)
.AppendLine()
.Append(" # Heartbeats/Condition items active : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.ActiveHeartbeatCount).Append(" | ")
.AppendFormat(CultureInfo.CurrentCulture, "{0:n0}", info.ActiveConditionCount)
.AppendLine()
.Append(" # Endpoints connected/disconnected : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:0}", info.NumberOfConnectedEndpoints).Append(" | ")
.AppendFormat(CultureInfo.CurrentCulture, "{0:0}", info.NumberOfDisconnectedEndpoints).Append(' ')
.AppendLine(connectivityState)
.Append(" # Connections created/retries : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:0}", info.ConnectionCount).Append(" | ")
.AppendFormat(CultureInfo.CurrentCulture, "{0:0}", info.ConnectionRetries)
.AppendLine()
.Append(" # Queued/Minimum request totals : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:0.##}", info.TotalPublishRequests).Append(" | ")
.AppendFormat(CultureInfo.CurrentCulture, "{0:0.##}", info.TotalMinPublishRequests)
.AppendLine()
.Append(" # Good/Bad Publish request totals : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:0.##}", info.TotalGoodPublishRequests).Append(" | ")
.AppendFormat(CultureInfo.CurrentCulture, "{0:0.##}", info.TotalBadPublishRequests)
.AppendLine()
.Append(" # Ingress value changes : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.IngressValueChanges).Append(' ')
.AppendLine(valueChangesPerSecFormatted)
.Append(" # Ingress sampled values : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.IngressSampledValues).Append(' ')
.AppendLine(sampledValuesPerSecFormatted)
.Append(" # Ingress events : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.IngressEvents).Append(' ')
.AppendLine(eventsPerSecFormatted)
.Append(" # Server queue overflows : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.ServerQueueOverflows).Append(' ')
.AppendLine(serverQueueOverflowsPerSecFormatted)
.Append(" # Received Data Change Notifications : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.IngressDataChanges).Append(' ')
.AppendLine(dataChangesPerSecFormatted)
.Append(" # Received Event Notifications : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.IngressEventNotifications).Append(' ')
.AppendLine(eventNotificationsPerSecFormatted)
.Append(" # Received Keep Alive Notifications : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.IngressKeepAliveNotifications).Append(' ')
.AppendLine(keepAliveChangesPerSecFormatted)
.Append(" # Received Cyclic read Notifications : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.IngressCyclicReads).Append(' ')
.AppendLine(cyclicReadsPerSecFormatted)
.Append(" # Generated Heartbeat Notifications : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.IngressHeartbeats).Append(' ')
.AppendLine(heartbeatsPerSecFormatted)
.Append(" # Generated Model Changes : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.IngressModelChanges).Append(' ')
.AppendLine(modelChangesPerSecFormatted)
.Append(" # Publish queue partitions/active : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.TotalPublishQueuePartitions).Append(" | ")
.AppendFormat(CultureInfo.CurrentCulture, "{0:n0}", info.ActivePublishQueuePartitions)
.AppendLine()
.Append(" # Notifications buffered/dropped : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.IngressBatchBlockBufferSize).Append(" | ")
.AppendFormat(CultureInfo.CurrentCulture, "{0:n0}", info.IngressNotificationsDropped)
.AppendLine()
.Append(" # Encoder input buffer size : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.EncodingBlockInputSize)
.AppendLine()
.Append(" # Encoder Notif. processed/dropped : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.EncoderNotificationsProcessed).Append(" | ")
.AppendFormat(CultureInfo.CurrentCulture, "{0:n0}", info.EncoderNotificationsDropped)
.AppendLine()
.Append(" # Encoder Network Messages produced : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.EncoderIoTMessagesProcessed)
.AppendLine()
.Append(" # Encoder avg Notifications/Message : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.EncoderAvgNotificationsMessage)
.AppendLine()
.Append(" # Encoder worst Message split ratio : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:0.##}", info.EncoderMaxMessageSplitRatio)
.AppendLine()
.Append(" # Encoder avg Message body size : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:0.##}", info.EncoderAvgIoTMessageBodySize).Append(' ')
.AppendLine(chunkUsageFormatted)
.Append(" # Encoder output buffer size : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.EncodingBlockOutputSize)
.AppendLine()
.Append(" # Egress Messages queued/dropped : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.OutgressInputBufferCount).Append(" | ")
.AppendFormat(CultureInfo.CurrentCulture, "{0:n0}", info.OutgressInputBufferDropped)
.AppendLine()
.Append(" # Egress Message send failures : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.OutgressIoTMessageFailedCount)
.AppendLine()
.Append(" # Egress Messages successfully sent : ")
.AppendFormat(CultureInfo.CurrentCulture, "{0,14:n0}", info.OutgressIoTMessageCount)
.Append(' ')
.AppendLine(sentMessagesPerSecFormatted)
;
}
}