private static void AddCommonDetail( List <Tuple <string, string> > data, StringBuilder sb, Message message, ConnectionMultiplexer multiplexer, ServerEndPoint server ) { if (message != null) { message.TryGetHeadMessages(out var now, out var next); if (now != null) { Add(data, sb, "Message-Current", "active", multiplexer.IncludeDetailInExceptions ? now.CommandAndKey : now.Command.ToString()); } if (next != null) { Add(data, sb, "Message-Next", "next", multiplexer.IncludeDetailInExceptions ? next.CommandAndKey : next.Command.ToString()); } } // Add server data, if we have it if (server != null && message != null) { var bs = server.GetBridgeStatus(message.IsForSubscriptionBridge ? ConnectionType.Subscription: ConnectionType.Interactive); switch (bs.Connection.ReadStatus) { case PhysicalConnection.ReadStatus.CompletePendingMessageAsync: case PhysicalConnection.ReadStatus.CompletePendingMessageSync: sb.Append(" ** possible thread-theft indicated; see https://stackexchange.github.io/StackExchange.Redis/ThreadTheft ** "); break; } Add(data, sb, "OpsSinceLastHeartbeat", "inst", bs.MessagesSinceLastHeartbeat.ToString()); Add(data, sb, "Queue-Awaiting-Write", "qu", bs.BacklogMessagesPending.ToString()); Add(data, sb, "Queue-Awaiting-Response", "qs", bs.Connection.MessagesSentAwaitingResponse.ToString()); Add(data, sb, "Active-Writer", "aw", bs.IsWriterActive.ToString()); if (bs.BacklogMessagesPending != 0) { Add(data, sb, "Backlog-Writer", "bw", bs.BacklogStatus.ToString()); } if (bs.Connection.ReadStatus != PhysicalConnection.ReadStatus.NA) { Add(data, sb, "Read-State", "rs", bs.Connection.ReadStatus.ToString()); } if (bs.Connection.WriteStatus != PhysicalConnection.WriteStatus.NA) { Add(data, sb, "Write-State", "ws", bs.Connection.WriteStatus.ToString()); } if (bs.Connection.BytesAvailableOnSocket >= 0) { Add(data, sb, "Inbound-Bytes", "in", bs.Connection.BytesAvailableOnSocket.ToString()); } if (bs.Connection.BytesInReadPipe >= 0) { Add(data, sb, "Inbound-Pipe-Bytes", "in-pipe", bs.Connection.BytesInReadPipe.ToString()); } if (bs.Connection.BytesInWritePipe >= 0) { Add(data, sb, "Outbound-Pipe-Bytes", "out-pipe", bs.Connection.BytesInWritePipe.ToString()); } if (multiplexer.StormLogThreshold >= 0 && bs.Connection.MessagesSentAwaitingResponse >= multiplexer.StormLogThreshold && Interlocked.CompareExchange(ref multiplexer.haveStormLog, 1, 0) == 0) { var log = server.GetStormLog(message); if (string.IsNullOrWhiteSpace(log)) { Interlocked.Exchange(ref multiplexer.haveStormLog, 0); } else { Interlocked.Exchange(ref multiplexer.stormLogSnapshot, log); } } Add(data, sb, "Server-Endpoint", "serverEndpoint", server.EndPoint.ToString().Replace("Unspecified/", "")); } Add(data, sb, "Multiplexer-Connects", "mc", $"{multiplexer._connectAttemptCount}/{multiplexer._connectCompletedCount}/{multiplexer._connectionCloseCount}"); Add(data, sb, "Manager", "mgr", multiplexer.SocketManager?.GetState()); Add(data, sb, "Client-Name", "clientName", multiplexer.ClientName); if (message != null) { var hashSlot = message.GetHashSlot(multiplexer.ServerSelectionStrategy); // only add keyslot if its a valid cluster key slot if (hashSlot != ServerSelectionStrategy.NoSlot) { Add(data, sb, "Key-HashSlot", "PerfCounterHelperkeyHashSlot", message.GetHashSlot(multiplexer.ServerSelectionStrategy).ToString()); } } int busyWorkerCount = PerfCounterHelper.GetThreadPoolStats(out string iocp, out string worker, out string workItems); Add(data, sb, "ThreadPool-IO-Completion", "IOCP", iocp); Add(data, sb, "ThreadPool-Workers", "WORKER", worker); if (workItems != null) { Add(data, sb, "ThreadPool-Items", "POOL", workItems); } data.Add(Tuple.Create("Busy-Workers", busyWorkerCount.ToString())); if (multiplexer.IncludePerformanceCountersInExceptions) { Add(data, sb, "Local-CPU", "Local-CPU", PerfCounterHelper.GetSystemCpuPercent()); } Add(data, sb, "Version", "v", GetLibVersion()); }