diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 8b9d6fb6f4..54afe6d76c 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1124,7 +1124,8 @@ settings.net.p2PPeersLabel=Connected peers settings.net.onionAddressColumn=Onion address settings.net.creationDateColumn=Established settings.net.connectionTypeColumn=In/Out -settings.net.totalTrafficLabel=Total traffic +settings.net.sentDataLabel=Sent data statistics +settings.net.receivedDataLabel=Received data statistics settings.net.roundTripTimeColumn=Roundtrip settings.net.sentBytesColumn=Sent settings.net.receivedBytesColumn=Received @@ -1137,7 +1138,8 @@ settings.net.heightColumn=Height settings.net.needRestart=You need to restart the application to apply that change.\nDo you want to do that now? settings.net.notKnownYet=Not known yet... -settings.net.sentReceived=Sent: {0}, received: {1} +settings.net.sentData=Sent data: {0}, {1} messages, {2} messages/sec, {3} messages received in last second +settings.net.receivedData=Received data: {0}, {1} messages, {2} messages/sec, {3} messages received in last second settings.net.ips=[IP address:port | host name:port | onion address:port] (comma separated). Port can be omitted if default is used (8333). settings.net.seedNode=Seed node settings.net.directPeer=Peer (direct) diff --git a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.fxml b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.fxml index d583b18a09..5b966f0973 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.fxml +++ b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.fxml @@ -153,10 +153,13 @@ - - + + + diff --git a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java index 0e82e9b9e0..d99b4f6454 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java +++ b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java @@ -73,6 +73,8 @@ import javafx.collections.transformation.SortedList; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import static javafx.beans.binding.Bindings.createStringBinding; + @FxmlView public class NetworkSettingsView extends ActivatableView { @@ -83,7 +85,7 @@ public class NetworkSettingsView extends ActivatableView { @FXML InputTextField btcNodesInputTextField; @FXML - TextField onionAddress, totalTrafficTextField; + TextField onionAddress, sentDataTextField, receivedDataTextField; @FXML Label p2PPeersLabel, bitcoinPeersLabel; @FXML @@ -175,7 +177,8 @@ public class NetworkSettingsView extends ActivatableView { onionAddressColumn.getStyleClass().add("first-column"); creationDateColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.creationDateColumn"))); connectionTypeColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.connectionTypeColumn"))); - totalTrafficTextField.setPromptText(Res.get("settings.net.totalTrafficLabel")); + sentDataTextField.setPromptText(Res.get("settings.net.sentDataLabel")); + receivedDataTextField.setPromptText(Res.get("settings.net.receivedDataLabel")); roundTripTimeColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.roundTripTimeColumn"))); sentBytesColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.sentBytesColumn"))); receivedBytesColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.receivedBytesColumn"))); @@ -297,11 +300,20 @@ public class NetworkSettingsView extends ActivatableView { Res.get("settings.net.notKnownYet") : nodeAddress.getFullAddress())); numP2PPeersSubscription = EasyBind.subscribe(p2PService.getNumConnectedPeers(), numPeers -> updateP2PTable()); - totalTrafficTextField.textProperty().bind(EasyBind.combine(Statistic.totalSentBytesProperty(), - Statistic.totalReceivedBytesProperty(), - (sent, received) -> Res.get("settings.net.sentReceived", - FormattingUtils.formatBytes((long) sent), - FormattingUtils.formatBytes((long) received)))); + + sentDataTextField.textProperty().bind(createStringBinding(() -> Res.get("settings.net.sentData", + FormattingUtils.formatBytes(Statistic.totalSentBytesProperty().get()), + Statistic.numTotalSentMessagesProperty().get(), + Statistic.numTotalSentMessagesPerSecProperty().get(), + Statistic.numTotalSentMessagesLastSecProperty().get()), + Statistic.numTotalSentMessagesLastSecProperty())); + + receivedDataTextField.textProperty().bind(createStringBinding(() -> Res.get("settings.net.receivedData", + FormattingUtils.formatBytes(Statistic.totalReceivedBytesProperty().get()), + Statistic.numTotalReceivedMessagesProperty().get(), + Statistic.numTotalReceivedMessagesPerSecProperty().get(), + Statistic.numTotalReceivedMessagesLastSecProperty().get()), + Statistic.numTotalReceivedMessagesLastSecProperty())); bitcoinSortedList.comparatorProperty().bind(bitcoinPeersTableView.comparatorProperty()); bitcoinPeersTableView.setItems(bitcoinSortedList); @@ -338,7 +350,8 @@ public class NetworkSettingsView extends ActivatableView { if (numP2PPeersSubscription != null) numP2PPeersSubscription.unsubscribe(); - totalTrafficTextField.textProperty().unbind(); + sentDataTextField.textProperty().unbind(); + receivedDataTextField.textProperty().unbind(); bitcoinSortedList.comparatorProperty().unbind(); p2pSortedList.comparatorProperty().unbind(); diff --git a/p2p/src/main/java/bisq/network/p2p/network/Statistic.java b/p2p/src/main/java/bisq/network/p2p/network/Statistic.java index 0809e49f54..ea987ae409 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/Statistic.java +++ b/p2p/src/main/java/bisq/network/p2p/network/Statistic.java @@ -20,8 +20,10 @@ package bisq.network.p2p.network; import bisq.common.UserThread; import bisq.common.proto.network.NetworkEnvelope; +import javafx.beans.property.DoubleProperty; import javafx.beans.property.IntegerProperty; import javafx.beans.property.LongProperty; +import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleLongProperty; @@ -29,31 +31,98 @@ import java.util.Date; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import lombok.extern.slf4j.Slf4j; + +/** + * Network statistics per connection. As we are also interested in total network statistics + * we use static properties to get traffic of all connections combined. + */ +@Slf4j public class Statistic { /////////////////////////////////////////////////////////////////////////////////////////// // Static /////////////////////////////////////////////////////////////////////////////////////////// + + private final static long startTime = System.currentTimeMillis(); private final static LongProperty totalSentBytes = new SimpleLongProperty(0); private final static LongProperty totalReceivedBytes = new SimpleLongProperty(0); + private final static Map totalReceivedMessages = new ConcurrentHashMap<>(); + private final static Map totalSentMessages = new ConcurrentHashMap<>(); + private final static Map totalReceivedMessagesPerSec = new ConcurrentHashMap<>(); + private final static Map totalSentMessagesPerSec = new ConcurrentHashMap<>(); + private final static LongProperty numTotalSentMessages = new SimpleLongProperty(0); + private final static LongProperty numTotalSentMessagesLastSec = new SimpleLongProperty(0); + private final static DoubleProperty numTotalSentMessagesPerSec = new SimpleDoubleProperty(0); + private final static LongProperty numTotalReceivedMessages = new SimpleLongProperty(0); + private final static LongProperty numTotalReceivedMessagesLastSec = new SimpleLongProperty(0); + private final static DoubleProperty numTotalReceivedMessagesPerSec = new SimpleDoubleProperty(0); - public static long getTotalSentBytes() { - return totalSentBytes.get(); + static { + UserThread.runPeriodically(() -> { + numTotalSentMessages.set(totalSentMessages.values().stream().mapToInt(Integer::intValue).sum()); + numTotalSentMessagesLastSec.set(totalSentMessagesPerSec.values().stream().mapToInt(Integer::intValue).sum()); + numTotalReceivedMessages.set(totalReceivedMessages.values().stream().mapToInt(Integer::intValue).sum()); + numTotalReceivedMessagesLastSec.set(totalReceivedMessagesPerSec.values().stream().mapToInt(Integer::intValue).sum()); + + long passed = (System.currentTimeMillis() - startTime) / 1000; + numTotalSentMessagesPerSec.set(((double) numTotalSentMessages.get()) / passed); + numTotalReceivedMessagesPerSec.set(((double) numTotalReceivedMessages.get()) / passed); + totalSentMessagesPerSec.clear(); + totalReceivedMessagesPerSec.clear(); + }, 1); + + // We log statistics every minute + UserThread.runPeriodically(() -> { + log.error("Network statistics:\n" + + "totalSentBytes: {} kb;\n" + + "numTotalSentMessages/totalSentMessages: {} / {};\n" + + "numTotalSentMessagesLastSec/totalSentMessagesPerSec: {} / {};\n" + + "totalReceivedBytes: {} kb" + + "numTotalReceivedMessages/totalReceivedMessages: {} / {};\n" + + "numTotalReceivedMessagesLastSec/totalReceivedMessagesPerSec: {} / {};", + totalSentBytes.get() / 1024d, + numTotalSentMessages.get(), totalSentMessages, + numTotalSentMessagesLastSec.get(), totalSentMessagesPerSec, + totalReceivedBytes.get() / 1024d, + numTotalReceivedMessages.get(), totalReceivedMessages, + numTotalReceivedMessagesLastSec.get(), totalReceivedMessagesPerSec); + }, 60); } public static LongProperty totalSentBytesProperty() { return totalSentBytes; } - public static long getTotalReceivedBytes() { - return totalReceivedBytes.get(); - } - public static LongProperty totalReceivedBytesProperty() { return totalReceivedBytes; } + public static LongProperty numTotalSentMessagesProperty() { + return numTotalSentMessages; + } + + public static LongProperty numTotalSentMessagesLastSecProperty() { + return numTotalSentMessagesLastSec; + } + + public static DoubleProperty numTotalSentMessagesPerSecProperty() { + return numTotalSentMessagesPerSec; + } + + public static LongProperty numTotalReceivedMessagesProperty() { + return numTotalReceivedMessages; + } + + public static LongProperty numTotalReceivedMessagesLastSecProperty() { + return numTotalReceivedMessagesLastSec; + } + + public static DoubleProperty numTotalReceivedMessagesPerSecProperty() { + return numTotalReceivedMessagesPerSec; + } + /////////////////////////////////////////////////////////////////////////////////////////// // Instance fields @@ -72,7 +141,7 @@ public class Statistic { // Constructor /////////////////////////////////////////////////////////////////////////////////////////// - public Statistic() { + Statistic() { creationDate = new Date(); } @@ -80,18 +149,18 @@ public class Statistic { // Update, increment /////////////////////////////////////////////////////////////////////////////////////////// - public void updateLastActivityTimestamp() { + void updateLastActivityTimestamp() { UserThread.execute(() -> lastActivityTimestamp = System.currentTimeMillis()); } - public void addSentBytes(int value) { + void addSentBytes(int value) { UserThread.execute(() -> { sentBytes.set(sentBytes.get() + value); totalSentBytes.set(totalSentBytes.get() + value); }); } - public void addReceivedBytes(int value) { + void addReceivedBytes(int value) { UserThread.execute(() -> { receivedBytes.set(receivedBytes.get() + value); totalReceivedBytes.set(totalReceivedBytes.get() + value); @@ -99,22 +168,46 @@ public class Statistic { } // TODO would need msg inspection to get useful information... - public void addReceivedMessage(NetworkEnvelope networkEnvelope) { + void addReceivedMessage(NetworkEnvelope networkEnvelope) { String messageClassName = networkEnvelope.getClass().getSimpleName(); int counter = 1; - if (receivedMessages.containsKey(messageClassName)) + if (receivedMessages.containsKey(messageClassName)) { counter = receivedMessages.get(messageClassName) + 1; - + } receivedMessages.put(messageClassName, counter); + + counter = 1; + if (totalReceivedMessages.containsKey(messageClassName)) { + counter = totalReceivedMessages.get(messageClassName) + 1; + } + totalReceivedMessages.put(messageClassName, counter); + + counter = 1; + if (totalReceivedMessagesPerSec.containsKey(messageClassName)) { + counter = totalReceivedMessagesPerSec.get(messageClassName) + 1; + } + totalReceivedMessagesPerSec.put(messageClassName, counter); } - public void addSentMessage(NetworkEnvelope networkEnvelope) { + void addSentMessage(NetworkEnvelope networkEnvelope) { String messageClassName = networkEnvelope.getClass().getSimpleName(); int counter = 1; - if (sentMessages.containsKey(messageClassName)) + if (sentMessages.containsKey(messageClassName)) { counter = sentMessages.get(messageClassName) + 1; - + } sentMessages.put(messageClassName, counter); + + counter = 1; + if (totalSentMessages.containsKey(messageClassName)) { + counter = totalSentMessages.get(messageClassName) + 1; + } + totalSentMessages.put(messageClassName, counter); + + counter = 1; + if (totalSentMessagesPerSec.containsKey(messageClassName)) { + counter = totalSentMessagesPerSec.get(messageClassName) + 1; + } + totalSentMessagesPerSec.put(messageClassName, counter); } public void setRoundTripTime(int roundTripTime) { @@ -160,11 +253,13 @@ public class Statistic { @Override public String toString() { return "Statistic{" + - "creationDate=" + creationDate + - ", lastActivityTimestamp=" + lastActivityTimestamp + - ", sentBytes=" + sentBytes + - ", receivedBytes=" + receivedBytes + - '}'; + "\n creationDate=" + creationDate + + ",\n lastActivityTimestamp=" + lastActivityTimestamp + + ",\n sentBytes=" + sentBytes + + ",\n receivedBytes=" + receivedBytes + + ",\n receivedMessages=" + receivedMessages + + ",\n sentMessages=" + sentMessages + + ",\n roundTripTime=" + roundTripTime + + "\n}"; } - }