mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-24 07:07:43 +01:00
Merge pull request #4734 from chimp1984/show-past-deviation-in-monitor
Show past deviation in monitor
This commit is contained in:
commit
9da7100ef2
13 changed files with 335 additions and 163 deletions
|
@ -24,9 +24,12 @@ import bisq.core.dao.monitoring.model.BlindVoteStateBlock;
|
|||
import bisq.core.dao.monitoring.model.DaoStateBlock;
|
||||
import bisq.core.dao.monitoring.model.ProposalStateBlock;
|
||||
import bisq.core.dao.state.DaoStateService;
|
||||
import bisq.core.filter.Filter;
|
||||
import bisq.core.filter.FilterManager;
|
||||
import bisq.core.network.p2p.inventory.messages.GetInventoryRequest;
|
||||
import bisq.core.network.p2p.inventory.messages.GetInventoryResponse;
|
||||
import bisq.core.network.p2p.inventory.model.InventoryItem;
|
||||
import bisq.core.network.p2p.inventory.model.RequestInfo;
|
||||
|
||||
import bisq.network.p2p.network.Connection;
|
||||
import bisq.network.p2p.network.MessageListener;
|
||||
|
@ -46,12 +49,12 @@ import javax.inject.Inject;
|
|||
import javax.inject.Named;
|
||||
|
||||
import com.google.common.base.Enums;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Optional;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
|
@ -66,6 +69,7 @@ public class GetInventoryRequestHandler implements MessageListener {
|
|||
private final DaoStateMonitoringService daoStateMonitoringService;
|
||||
private final ProposalStateMonitoringService proposalStateMonitoringService;
|
||||
private final BlindVoteStateMonitoringService blindVoteStateMonitoringService;
|
||||
private final FilterManager filterManager;
|
||||
private final int maxConnections;
|
||||
|
||||
@Inject
|
||||
|
@ -76,6 +80,7 @@ public class GetInventoryRequestHandler implements MessageListener {
|
|||
DaoStateMonitoringService daoStateMonitoringService,
|
||||
ProposalStateMonitoringService proposalStateMonitoringService,
|
||||
BlindVoteStateMonitoringService blindVoteStateMonitoringService,
|
||||
FilterManager filterManager,
|
||||
@Named(Config.MAX_CONNECTIONS) int maxConnections) {
|
||||
this.networkNode = networkNode;
|
||||
this.peerManager = peerManager;
|
||||
|
@ -84,6 +89,7 @@ public class GetInventoryRequestHandler implements MessageListener {
|
|||
this.daoStateMonitoringService = daoStateMonitoringService;
|
||||
this.proposalStateMonitoringService = proposalStateMonitoringService;
|
||||
this.blindVoteStateMonitoringService = blindVoteStateMonitoringService;
|
||||
this.filterManager = filterManager;
|
||||
this.maxConnections = maxConnections;
|
||||
|
||||
this.networkNode.addMessageListener(this);
|
||||
|
@ -97,28 +103,11 @@ public class GetInventoryRequestHandler implements MessageListener {
|
|||
Map<InventoryItem, Integer> dataObjects = new HashMap<>();
|
||||
p2PDataStorage.getMapForDataResponse(getInventoryRequest.getVersion()).values().stream()
|
||||
.map(e -> e.getClass().getSimpleName())
|
||||
.forEach(className -> {
|
||||
Optional<InventoryItem> optionalEnum = Enums.getIfPresent(InventoryItem.class, className);
|
||||
if (optionalEnum.isPresent()) {
|
||||
InventoryItem key = optionalEnum.get();
|
||||
dataObjects.putIfAbsent(key, 0);
|
||||
int prev = dataObjects.get(key);
|
||||
dataObjects.put(key, prev + 1);
|
||||
}
|
||||
});
|
||||
.forEach(className -> addClassNameToMap(dataObjects, className));
|
||||
p2PDataStorage.getMap().values().stream()
|
||||
.map(ProtectedStorageEntry::getProtectedStoragePayload)
|
||||
.filter(Objects::nonNull)
|
||||
.map(e -> e.getClass().getSimpleName())
|
||||
.forEach(className -> {
|
||||
Optional<InventoryItem> optionalEnum = Enums.getIfPresent(InventoryItem.class, className);
|
||||
if (optionalEnum.isPresent()) {
|
||||
InventoryItem key = optionalEnum.get();
|
||||
dataObjects.putIfAbsent(key, 0);
|
||||
int prev = dataObjects.get(key);
|
||||
dataObjects.put(key, prev + 1);
|
||||
}
|
||||
});
|
||||
.forEach(className -> addClassNameToMap(dataObjects, className));
|
||||
Map<InventoryItem, String> inventory = new HashMap<>();
|
||||
dataObjects.forEach((key, value) -> inventory.put(key, String.valueOf(value)));
|
||||
|
||||
|
@ -152,6 +141,7 @@ public class GetInventoryRequestHandler implements MessageListener {
|
|||
inventory.put(InventoryItem.numConnections, String.valueOf(networkNode.getAllConnections().size()));
|
||||
inventory.put(InventoryItem.peakNumConnections, String.valueOf(peerManager.getPeakNumConnections()));
|
||||
inventory.put(InventoryItem.numAllConnectionsLostEvents, String.valueOf(peerManager.getNumAllConnectionsLostEvents()));
|
||||
peerManager.resetNumAllConnectionsLostEvents();
|
||||
inventory.put(InventoryItem.sentBytes, String.valueOf(Statistic.totalSentBytesProperty().get()));
|
||||
inventory.put(InventoryItem.sentBytesPerSec, String.valueOf(Statistic.totalSentBytesPerSecProperty().get()));
|
||||
inventory.put(InventoryItem.receivedBytes, String.valueOf(Statistic.totalReceivedBytesProperty().get()));
|
||||
|
@ -161,9 +151,15 @@ public class GetInventoryRequestHandler implements MessageListener {
|
|||
|
||||
// node
|
||||
inventory.put(InventoryItem.version, Version.VERSION);
|
||||
inventory.put(InventoryItem.commitHash, RequestInfo.COMMIT_HASH);
|
||||
inventory.put(InventoryItem.usedMemory, String.valueOf(Profiler.getUsedMemoryInBytes()));
|
||||
inventory.put(InventoryItem.jvmStartTime, String.valueOf(ManagementFactory.getRuntimeMXBean().getStartTime()));
|
||||
|
||||
Filter filter = filterManager.getFilter();
|
||||
if (filter != null) {
|
||||
inventory.put(InventoryItem.filteredSeeds, Joiner.on("," + System.getProperty("line.separator")).join(filter.getSeedNodes()));
|
||||
}
|
||||
|
||||
log.info("Send inventory {} to {}", inventory, connection.getPeersNodeAddressOptional());
|
||||
GetInventoryResponse getInventoryResponse = new GetInventoryResponse(inventory);
|
||||
networkNode.sendMessage(connection, getInventoryResponse);
|
||||
|
@ -173,4 +169,14 @@ public class GetInventoryRequestHandler implements MessageListener {
|
|||
public void shutDown() {
|
||||
networkNode.removeMessageListener(this);
|
||||
}
|
||||
|
||||
private void addClassNameToMap(Map<InventoryItem, Integer> dataObjects, String className) {
|
||||
Optional<InventoryItem> optionalEnum = Enums.getIfPresent(InventoryItem.class, className);
|
||||
if (optionalEnum.isPresent()) {
|
||||
InventoryItem key = optionalEnum.get();
|
||||
dataObjects.putIfAbsent(key, 0);
|
||||
int prev = dataObjects.get(key);
|
||||
dataObjects.put(key, prev + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,10 +36,11 @@ public class Average {
|
|||
|
||||
public static double getAverage(Set<RequestInfo> requestInfoSet, InventoryItem inventoryItem) {
|
||||
return requestInfoSet.stream()
|
||||
.map(RequestInfo::getInventory)
|
||||
.map(RequestInfo::getDataMap)
|
||||
.filter(map -> map.containsKey(inventoryItem))
|
||||
.map(map -> map.get(inventoryItem).getValue())
|
||||
.filter(Objects::nonNull)
|
||||
.filter(inventory -> inventory.containsKey(inventoryItem))
|
||||
.mapToDouble(inventory -> Double.parseDouble((inventory.get(inventoryItem))))
|
||||
.mapToDouble(Double::parseDouble)
|
||||
.average()
|
||||
.orElse(0d);
|
||||
}
|
||||
|
|
|
@ -51,9 +51,9 @@ public class DeviationByIntegerDiff implements DeviationType {
|
|||
collection.stream()
|
||||
.filter(list -> !list.isEmpty())
|
||||
.map(list -> list.get(list.size() - 1)) // We use last item only
|
||||
.map(RequestInfo::getInventory)
|
||||
.map(RequestInfo::getDataMap)
|
||||
.map(e -> e.get(inventoryItem).getValue())
|
||||
.filter(Objects::nonNull)
|
||||
.map(e -> e.get(inventoryItem))
|
||||
.forEach(e -> {
|
||||
sameItemsByValue.putIfAbsent(e, 0);
|
||||
int prev = sameItemsByValue.get(e);
|
||||
|
|
|
@ -23,6 +23,11 @@ public class DeviationByPercentage implements DeviationType {
|
|||
private final double lowerWarnTrigger;
|
||||
private final double upperWarnTrigger;
|
||||
|
||||
// In case want to see the % deviation but not trigger any warnings or alerts
|
||||
public DeviationByPercentage() {
|
||||
this(0, Double.MAX_VALUE, 0, Double.MAX_VALUE);
|
||||
}
|
||||
|
||||
public DeviationByPercentage(double lowerAlertTrigger,
|
||||
double upperAlertTrigger,
|
||||
double lowerWarnTrigger,
|
||||
|
|
|
@ -44,10 +44,10 @@ public class DeviationOfHashes implements DeviationType {
|
|||
collection.stream()
|
||||
.filter(list -> !list.isEmpty())
|
||||
.map(list -> list.get(list.size() - 1)) // We use last item only
|
||||
.map(RequestInfo::getInventory)
|
||||
.map(RequestInfo::getDataMap)
|
||||
.filter(map -> currentBlockHeight.equals(map.get(InventoryItem.daoStateChainHeight).getValue()))
|
||||
.map(map -> map.get(inventoryItem).getValue())
|
||||
.filter(Objects::nonNull)
|
||||
.filter(inventory -> inventory.get(InventoryItem.daoStateChainHeight).equals(currentBlockHeight))
|
||||
.map(inventory -> inventory.get(inventoryItem))
|
||||
.forEach(v -> {
|
||||
sameHashesPerHashListByHash.putIfAbsent(v, 0);
|
||||
int prev = sameHashesPerHashListByHash.get(v);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package bisq.core.network.p2p.inventory.model;
|
||||
|
||||
public enum DeviationSeverity {
|
||||
IGNORED,
|
||||
OK,
|
||||
WARN,
|
||||
ALERT
|
||||
|
|
|
@ -17,101 +17,104 @@
|
|||
|
||||
package bisq.core.network.p2p.inventory.model;
|
||||
|
||||
import bisq.common.util.Tuple2;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public enum InventoryItem {
|
||||
// Percentage deviation
|
||||
OfferPayload("OfferPayload",
|
||||
true,
|
||||
new DeviationByPercentage(0.9, 1.1, 0.95, 1.05)),
|
||||
new DeviationByPercentage(0.9, 1.1, 0.95, 1.05), 5),
|
||||
MailboxStoragePayload("MailboxStoragePayload",
|
||||
true,
|
||||
new DeviationByPercentage(0.9, 1.1, 0.95, 1.05)),
|
||||
TradeStatistics3("MailboxStoragePayload",
|
||||
new DeviationByPercentage(0.9, 1.1, 0.95, 1.05), 2),
|
||||
TradeStatistics3("TradeStatistics3",
|
||||
true,
|
||||
new DeviationByPercentage(0.9, 1.1, 0.95, 1.05)),
|
||||
AccountAgeWitness("MailboxStoragePayload",
|
||||
new DeviationByPercentage(0.9, 1.1, 0.95, 1.05), 2),
|
||||
AccountAgeWitness("AccountAgeWitness",
|
||||
true,
|
||||
new DeviationByPercentage(0.9, 1.1, 0.95, 1.05)),
|
||||
SignedWitness("MailboxStoragePayload",
|
||||
new DeviationByPercentage(0.9, 1.1, 0.95, 1.05), 2),
|
||||
SignedWitness("SignedWitness",
|
||||
true,
|
||||
new DeviationByPercentage(0.9, 1.1, 0.95, 1.05)),
|
||||
new DeviationByPercentage(0.9, 1.1, 0.95, 1.05), 2),
|
||||
|
||||
// Should be same value
|
||||
Alert("Alert",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 1)),
|
||||
new DeviationByIntegerDiff(1, 1), 2),
|
||||
Filter("Filter",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 1)),
|
||||
new DeviationByIntegerDiff(1, 1), 2),
|
||||
Mediator("Mediator",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 1)),
|
||||
new DeviationByIntegerDiff(1, 1), 2),
|
||||
RefundAgent("RefundAgent",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 1)),
|
||||
new DeviationByIntegerDiff(1, 1), 2),
|
||||
|
||||
// Should be very close values
|
||||
TempProposalPayload("TempProposalPayload",
|
||||
true,
|
||||
new DeviationByIntegerDiff(3, 5)),
|
||||
new DeviationByIntegerDiff(3, 5), 2),
|
||||
ProposalPayload("ProposalPayload",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 2)),
|
||||
new DeviationByIntegerDiff(1, 2), 2),
|
||||
BlindVotePayload("BlindVotePayload",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 2)),
|
||||
new DeviationByIntegerDiff(1, 2), 2),
|
||||
|
||||
// Should be very close values
|
||||
daoStateChainHeight("daoStateChainHeight",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 3)),
|
||||
new DeviationByIntegerDiff(2, 4), 3),
|
||||
numBsqBlocks("numBsqBlocks",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 3)),
|
||||
new DeviationByIntegerDiff(2, 4), 3),
|
||||
|
||||
// Has to be same values at same block
|
||||
daoStateHash("daoStateHash",
|
||||
false,
|
||||
new DeviationOfHashes()),
|
||||
new DeviationOfHashes(), 1),
|
||||
proposalHash("proposalHash",
|
||||
false,
|
||||
new DeviationOfHashes()),
|
||||
new DeviationOfHashes(), 1),
|
||||
blindVoteHash("blindVoteHash",
|
||||
false,
|
||||
new DeviationOfHashes()),
|
||||
new DeviationOfHashes(), 1),
|
||||
|
||||
// Percentage deviation
|
||||
maxConnections("maxConnections",
|
||||
true,
|
||||
new DeviationByPercentage(0.33, 3, 0.4, 2.5)),
|
||||
new DeviationByPercentage(0.33, 3, 0.4, 2.5), 2),
|
||||
numConnections("numConnections",
|
||||
true,
|
||||
new DeviationByPercentage(0.33, 3, 0.4, 2.5)),
|
||||
new DeviationByPercentage(0, 3, 0, 2.5), 2),
|
||||
peakNumConnections("peakNumConnections",
|
||||
true,
|
||||
new DeviationByPercentage(0.33, 3, 0.4, 2.5)),
|
||||
new DeviationByPercentage(0, 3, 0, 2.5), 2),
|
||||
numAllConnectionsLostEvents("numAllConnectionsLostEvents",
|
||||
true,
|
||||
new DeviationByIntegerDiff(1, 2)),
|
||||
new DeviationByIntegerDiff(1, 2), 3),
|
||||
sentBytesPerSec("sentBytesPerSec",
|
||||
true,
|
||||
new DeviationByPercentage(0.33, 3, 0.4, 2.5)),
|
||||
new DeviationByPercentage(), 5),
|
||||
receivedBytesPerSec("receivedBytesPerSec",
|
||||
true,
|
||||
new DeviationByPercentage(0.33, 3, 0.4, 2.5)),
|
||||
new DeviationByPercentage(), 5),
|
||||
receivedMessagesPerSec("receivedMessagesPerSec",
|
||||
true,
|
||||
new DeviationByPercentage(0.33, 3, 0.4, 2.5)),
|
||||
new DeviationByPercentage(), 5),
|
||||
sentMessagesPerSec("sentMessagesPerSec",
|
||||
true,
|
||||
new DeviationByPercentage(0.33, 3, 0.4, 2.5)),
|
||||
new DeviationByPercentage(), 5),
|
||||
|
||||
// No deviation check
|
||||
sentBytes("sentBytes", true),
|
||||
|
@ -119,8 +122,10 @@ public enum InventoryItem {
|
|||
|
||||
// No deviation check
|
||||
version("version", false),
|
||||
commitHash("commitHash", false),
|
||||
usedMemory("usedMemory", true),
|
||||
jvmStartTime("jvmStartTime", true);
|
||||
jvmStartTime("jvmStartTime", true),
|
||||
filteredSeeds("filteredSeeds", false);
|
||||
|
||||
@Getter
|
||||
private final String key;
|
||||
|
@ -130,21 +135,28 @@ public enum InventoryItem {
|
|||
@Nullable
|
||||
private DeviationType deviationType;
|
||||
|
||||
// The number of past requests we check to see if there have been repeated alerts or warnings. The higher the
|
||||
// number the more repeated alert need to have happened to cause a notification alert.
|
||||
@Getter
|
||||
private int deviationTolerance = 1;
|
||||
|
||||
InventoryItem(String key, boolean isNumberValue) {
|
||||
this.key = key;
|
||||
this.isNumberValue = isNumberValue;
|
||||
}
|
||||
|
||||
InventoryItem(String key, boolean isNumberValue, DeviationType deviationType) {
|
||||
InventoryItem(String key, boolean isNumberValue, @NotNull DeviationType deviationType, int deviationTolerance) {
|
||||
this(key, isNumberValue);
|
||||
this.deviationType = deviationType;
|
||||
this.deviationTolerance = deviationTolerance;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Double getDeviation(Map<InventoryItem, Double> averageValues, @Nullable String value) {
|
||||
public Tuple2<Double, Double> getDeviationAndAverage(Map<InventoryItem, Double> averageValues,
|
||||
@Nullable String value) {
|
||||
if (averageValues.containsKey(this) && value != null) {
|
||||
double averageValue = averageValues.get(this);
|
||||
return getDeviation(value, averageValue);
|
||||
return new Tuple2<>(getDeviation(value, averageValue), averageValue);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -17,25 +17,29 @@
|
|||
|
||||
package bisq.core.network.p2p.inventory.model;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.Value;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@Getter
|
||||
public class RequestInfo {
|
||||
// Carries latest commit hash of feature changes (not latest commit as that is then the commit for editing that field)
|
||||
public static final String COMMIT_HASH = "d789282b";
|
||||
|
||||
private final long requestStartTime;
|
||||
@Setter
|
||||
private long responseTime;
|
||||
@Nullable
|
||||
@Setter
|
||||
private Map<InventoryItem, String> inventory;
|
||||
@Nullable
|
||||
@Setter
|
||||
private String errorMessage;
|
||||
|
||||
private final Map<InventoryItem, Data> dataMap = new HashMap<>();
|
||||
|
||||
public RequestInfo(long requestStartTime) {
|
||||
this.requestStartTime = requestStartTime;
|
||||
}
|
||||
|
@ -47,8 +51,45 @@ public class RequestInfo {
|
|||
|
||||
@Nullable
|
||||
public String getValue(InventoryItem inventoryItem) {
|
||||
return inventory != null && inventory.containsKey(inventoryItem) ?
|
||||
inventory.get(inventoryItem) :
|
||||
return dataMap.containsKey(inventoryItem) ?
|
||||
dataMap.get(inventoryItem).getValue() :
|
||||
null;
|
||||
}
|
||||
|
||||
@Value
|
||||
public static class Data {
|
||||
private final String value;
|
||||
@Nullable
|
||||
private final Double average;
|
||||
private final Double deviation;
|
||||
private final DeviationSeverity deviationSeverity;
|
||||
private final boolean persistentWarning;
|
||||
private final boolean persistentAlert;
|
||||
|
||||
public Data(String value,
|
||||
@Nullable Double average,
|
||||
Double deviation,
|
||||
DeviationSeverity deviationSeverity,
|
||||
boolean persistentWarning,
|
||||
boolean persistentAlert) {
|
||||
this.value = value;
|
||||
this.average = average;
|
||||
this.deviation = deviation;
|
||||
this.deviationSeverity = deviationSeverity;
|
||||
this.persistentWarning = persistentWarning;
|
||||
this.persistentAlert = persistentAlert;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "InventoryData{" +
|
||||
"\n value='" + value + '\'' +
|
||||
",\n average=" + average +
|
||||
",\n deviation=" + deviation +
|
||||
",\n deviationSeverity=" + deviationSeverity +
|
||||
",\n persistentWarning=" + persistentWarning +
|
||||
",\n persistentAlert=" + persistentAlert +
|
||||
"\n}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package bisq.inventory;
|
|||
|
||||
import bisq.core.network.p2p.inventory.GetInventoryRequestManager;
|
||||
import bisq.core.network.p2p.inventory.model.Average;
|
||||
import bisq.core.network.p2p.inventory.model.DeviationSeverity;
|
||||
import bisq.core.network.p2p.inventory.model.InventoryItem;
|
||||
import bisq.core.network.p2p.inventory.model.RequestInfo;
|
||||
import bisq.core.network.p2p.seed.DefaultSeedNodeRepository;
|
||||
|
@ -33,6 +34,7 @@ import bisq.network.p2p.network.SetupListener;
|
|||
import bisq.common.UserThread;
|
||||
import bisq.common.config.BaseCurrencyNetwork;
|
||||
import bisq.common.file.JsonFileManager;
|
||||
import bisq.common.util.Tuple2;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import java.time.Clock;
|
||||
|
@ -40,10 +42,12 @@ import java.time.Clock;
|
|||
import java.io.File;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -155,26 +159,78 @@ public class InventoryMonitor implements SetupListener {
|
|||
requestInfo.setErrorMessage(errorMessage);
|
||||
}
|
||||
|
||||
boolean ignoreDeviationAtStartup;
|
||||
if (result != null) {
|
||||
log.info("nodeAddress={}, result={}", nodeAddress, result.toString());
|
||||
requestInfo.setInventory(result);
|
||||
long responseTime = System.currentTimeMillis();
|
||||
requestInfo.setResponseTime(responseTime);
|
||||
|
||||
// If seed just started up we ignore the deviation as it can be expected that seed is still syncing
|
||||
// DAO state/blocks. P2P data should be ready but as we received it from other seeds it is not that
|
||||
// valuable information either, so we apply the ignore to all data.
|
||||
if (result.containsKey(InventoryItem.jvmStartTime)) {
|
||||
String jvmStartTimeString = result.get(InventoryItem.jvmStartTime);
|
||||
long jvmStartTime = Long.parseLong(jvmStartTimeString);
|
||||
ignoreDeviationAtStartup = jvmStartTime < TimeUnit.MINUTES.toMillis(2);
|
||||
} else {
|
||||
ignoreDeviationAtStartup = false;
|
||||
}
|
||||
} else {
|
||||
ignoreDeviationAtStartup = false;
|
||||
}
|
||||
|
||||
requestInfoListByNode.putIfAbsent(nodeAddress, new ArrayList<>());
|
||||
List<RequestInfo> requestInfoList = requestInfoListByNode.get(nodeAddress);
|
||||
requestInfoList.add(requestInfo);
|
||||
|
||||
|
||||
// We create average of all nodes latest results. It might be that the nodes last result is
|
||||
// from a previous request as the response has not arrived yet.
|
||||
Set<RequestInfo> requestInfoSet = requestInfoListByNode.values().stream()
|
||||
//TODO might be not a good idea to use the last result if its not a recent one. a faulty node would distort
|
||||
// the average calculation.
|
||||
// As we add at the end our own result the average is excluding our own value
|
||||
Collection<List<RequestInfo>> requestInfoListByNodeValues = requestInfoListByNode.values();
|
||||
Set<RequestInfo> requestInfoSet = requestInfoListByNodeValues.stream()
|
||||
.filter(list -> !list.isEmpty())
|
||||
.map(list -> list.get(list.size() - 1))
|
||||
.collect(Collectors.toSet());
|
||||
Map<InventoryItem, Double> averageValues = Average.of(requestInfoSet);
|
||||
|
||||
inventoryWebServer.onNewRequestInfo(requestInfoListByNode, averageValues, requestCounter);
|
||||
String daoStateChainHeight = result != null &&
|
||||
result.containsKey(InventoryItem.daoStateChainHeight) ?
|
||||
result.get(InventoryItem.daoStateChainHeight) :
|
||||
null;
|
||||
List.of(InventoryItem.values()).forEach(inventoryItem -> {
|
||||
String value = result != null ? result.get(inventoryItem) : null;
|
||||
Tuple2<Double, Double> tuple = inventoryItem.getDeviationAndAverage(averageValues, value);
|
||||
Double deviation = tuple != null ? tuple.first : null;
|
||||
Double average = tuple != null ? tuple.second : null;
|
||||
DeviationSeverity deviationSeverity = ignoreDeviationAtStartup ? DeviationSeverity.IGNORED :
|
||||
inventoryItem.getDeviationSeverity(deviation,
|
||||
requestInfoListByNodeValues,
|
||||
value,
|
||||
daoStateChainHeight);
|
||||
int endIndex = Math.max(0, requestInfoList.size() - 1);
|
||||
int deviationTolerance = inventoryItem.getDeviationTolerance();
|
||||
int fromIndex = Math.max(0, endIndex - deviationTolerance);
|
||||
List<DeviationSeverity> lastDeviationSeverityEntries = requestInfoList.subList(fromIndex, endIndex).stream()
|
||||
.filter(e -> e.getDataMap().containsKey(inventoryItem))
|
||||
.map(e -> e.getDataMap().get(inventoryItem).getDeviationSeverity())
|
||||
.collect(Collectors.toList());
|
||||
long numWarnings = lastDeviationSeverityEntries.stream()
|
||||
.filter(e -> e == DeviationSeverity.WARN)
|
||||
.count();
|
||||
long numAlerts = lastDeviationSeverityEntries.stream()
|
||||
.filter(e -> e == DeviationSeverity.ALERT)
|
||||
.count();
|
||||
boolean persistentWarning = numWarnings == deviationTolerance;
|
||||
boolean persistentAlert = numAlerts == deviationTolerance;
|
||||
RequestInfo.Data data = new RequestInfo.Data(value, average, deviation, deviationSeverity, persistentWarning, persistentAlert);
|
||||
requestInfo.getDataMap().put(inventoryItem, data);
|
||||
});
|
||||
|
||||
requestInfoList.add(requestInfo);
|
||||
|
||||
inventoryWebServer.onNewRequestInfo(requestInfoListByNode, requestCounter);
|
||||
|
||||
String json = Utilities.objectToJson(requestInfo);
|
||||
jsonFileManagerByNodeAddress.get(nodeAddress).writeToDisc(json, String.valueOf(requestInfo.getRequestStartTime()));
|
||||
|
|
|
@ -53,7 +53,7 @@ public class InventoryMonitorMain {
|
|||
// prog args for regtest: 10 1 BTC_REGTEST
|
||||
public static void main(String[] args) {
|
||||
// Default values
|
||||
int intervalSec = 300;
|
||||
int intervalSec = 120;
|
||||
boolean useLocalhostForP2P = false;
|
||||
BaseCurrencyNetwork network = BaseCurrencyNetwork.BTC_MAINNET;
|
||||
int port = 80;
|
||||
|
@ -71,7 +71,7 @@ public class InventoryMonitorMain {
|
|||
port = Integer.parseInt(args[3]);
|
||||
}
|
||||
|
||||
String appName = "bisq-InventoryMonitor-" + network + "-" + intervalSec;
|
||||
String appName = "bisq-inventory-monitor-" + network;
|
||||
File appDir = new File(Utilities.getUserDataDir(), appName);
|
||||
if (!appDir.exists() && !appDir.mkdir()) {
|
||||
log.warn("make appDir failed");
|
||||
|
@ -100,11 +100,11 @@ public class InventoryMonitorMain {
|
|||
UserThread.setExecutor(Executors.newSingleThreadExecutor(threadFactory));
|
||||
|
||||
Signal.handle(new Signal("INT"), signal -> {
|
||||
shutDown();
|
||||
UserThread.execute(InventoryMonitorMain::shutDown);
|
||||
});
|
||||
|
||||
Signal.handle(new Signal("TERM"), signal -> {
|
||||
shutDown();
|
||||
UserThread.execute(InventoryMonitorMain::shutDown);
|
||||
});
|
||||
keepRunning();
|
||||
}
|
||||
|
|
|
@ -24,12 +24,15 @@ import bisq.core.util.FormattingUtils;
|
|||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
|
||||
import bisq.common.app.Version;
|
||||
import bisq.common.util.MathUtils;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -77,11 +80,9 @@ public class InventoryWebServer {
|
|||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void onNewRequestInfo(Map<NodeAddress, List<RequestInfo>> requestInfoListByNode,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
int requestCounter) {
|
||||
public void onNewRequestInfo(Map<NodeAddress, List<RequestInfo>> requestInfoListByNode, int requestCounter) {
|
||||
this.requestCounter = requestCounter;
|
||||
html = generateHtml(requestInfoListByNode, averageValues);
|
||||
html = generateHtml(requestInfoListByNode);
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
|
@ -93,14 +94,22 @@ public class InventoryWebServer {
|
|||
// HTML
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private String generateHtml(Map<NodeAddress, List<RequestInfo>> map,
|
||||
Map<InventoryItem, Double> averageValues) {
|
||||
private String generateHtml(Map<NodeAddress, List<RequestInfo>> map) {
|
||||
StringBuilder html = new StringBuilder();
|
||||
html.append("<html>" +
|
||||
"<head><style>table, th, td {border: 1px solid black;}</style></head>" +
|
||||
"<head>" +
|
||||
"<style type=\"text/css\">" +
|
||||
" a {" +
|
||||
" text-decoration:none; color: black;" +
|
||||
" }" +
|
||||
" #warn { color: #ff7700; } " +
|
||||
" #alert { color: #ff0000; } " +
|
||||
"table, th, td {border: 1px solid black;}" +
|
||||
"</style></head>" +
|
||||
"<body><h3>")
|
||||
.append("Current time: ").append(new Date().toString()).append("<br/>")
|
||||
.append("Request cycle: ").append(requestCounter).append("<br/>")
|
||||
.append("Version/commit: ").append(Version.VERSION).append(" / ").append(RequestInfo.COMMIT_HASH).append("<br/>")
|
||||
.append("<table style=\"width:100%\">")
|
||||
.append("<tr>")
|
||||
.append("<th align=\"left\">Seed node info</th>")
|
||||
|
@ -117,9 +126,9 @@ public class InventoryWebServer {
|
|||
RequestInfo requestInfo = list.get(numResponses - 1);
|
||||
html.append("<td>").append(getSeedNodeInfo(seedNode, requestInfo)).append("</td>")
|
||||
.append("<td>").append(getRequestInfo(requestInfo, numResponses)).append("</td>")
|
||||
.append("<td>").append(getDataInfo(requestInfo, averageValues, map)).append("</td>")
|
||||
.append("<td>").append(getDaoInfo(requestInfo, averageValues, map)).append("</td>")
|
||||
.append("<td>").append(getNetworkInfo(requestInfo, averageValues, map)).append("</td>");
|
||||
.append("<td>").append(getDataInfo(seedNode, requestInfo, map)).append("</td>")
|
||||
.append("<td>").append(getDaoInfo(seedNode, requestInfo, map)).append("</td>")
|
||||
.append("<td>").append(getNetworkInfo(seedNode, requestInfo, map)).append("</td>");
|
||||
} else {
|
||||
html.append("<td>").append(getSeedNodeInfo(seedNode, null)).append("</td>")
|
||||
.append("<td>").append("n/a").append("</td>")
|
||||
|
@ -147,10 +156,18 @@ public class InventoryWebServer {
|
|||
sb.append("Operator: ").append(operator).append("<br/>");
|
||||
|
||||
String address = nodeAddress.getFullAddress();
|
||||
|
||||
String filteredSeeds = requestInfo != null ? requestInfo.getValue(InventoryItem.filteredSeeds) : null;
|
||||
if (filteredSeeds != null && filteredSeeds.contains(address)) {
|
||||
sb.append(getColorTagByDeviationSeverity(DeviationSeverity.ALERT)).append("Node address: ")
|
||||
.append(address).append(" (is filtered!)").append(CLOSE_TAG);
|
||||
} else {
|
||||
sb.append("Node address: ").append(address).append("<br/>");
|
||||
}
|
||||
|
||||
if (requestInfo != null) {
|
||||
sb.append("Version: ").append(requestInfo.getDisplayValue(InventoryItem.version)).append("<br/>");
|
||||
sb.append("Commit hash: ").append(requestInfo.getDisplayValue(InventoryItem.commitHash)).append("<br/>");
|
||||
String memory = requestInfo.getValue(InventoryItem.usedMemory);
|
||||
String memoryString = memory != null ? Utilities.readableFileSize(Long.parseLong(memory)) : "n/a";
|
||||
sb.append("Memory used: ")
|
||||
|
@ -168,6 +185,10 @@ public class InventoryWebServer {
|
|||
true, true) :
|
||||
"n/a";
|
||||
sb.append("Run duration: ").append(duration).append("<br/>");
|
||||
|
||||
sb.append("Filtered seed nodes: ")
|
||||
.append(requestInfo.getDisplayValue(InventoryItem.filteredSeeds).replace(System.getProperty("line.separator"), "<br/>"))
|
||||
.append("<br/>");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
|
@ -215,74 +236,75 @@ public class InventoryWebServer {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
private String getDataInfo(RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
private String getDataInfo(NodeAddress seedNode,
|
||||
RequestInfo requestInfo,
|
||||
Map<NodeAddress, List<RequestInfo>> map) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(getLine(InventoryItem.OfferPayload, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.MailboxStoragePayload, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.TradeStatistics3, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.AccountAgeWitness, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.SignedWitness, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.OfferPayload, seedNode, requestInfo, map));
|
||||
sb.append(getLine(InventoryItem.MailboxStoragePayload, seedNode, requestInfo, map));
|
||||
sb.append(getLine(InventoryItem.TradeStatistics3, seedNode, requestInfo, map));
|
||||
sb.append(getLine(InventoryItem.AccountAgeWitness, seedNode, requestInfo, map));
|
||||
sb.append(getLine(InventoryItem.SignedWitness, seedNode, requestInfo, map));
|
||||
|
||||
sb.append(getLine(InventoryItem.Alert, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.Filter, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.Mediator, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.RefundAgent, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.Alert, seedNode, requestInfo, map));
|
||||
sb.append(getLine(InventoryItem.Filter, seedNode, requestInfo, map));
|
||||
sb.append(getLine(InventoryItem.Mediator, seedNode, requestInfo, map));
|
||||
sb.append(getLine(InventoryItem.RefundAgent, seedNode, requestInfo, map));
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String getDaoInfo(RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
private String getDaoInfo(NodeAddress seedNode,
|
||||
RequestInfo requestInfo,
|
||||
Map<NodeAddress, List<RequestInfo>> map) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(getLine("Number of BSQ blocks: ", InventoryItem.numBsqBlocks, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.TempProposalPayload, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.ProposalPayload, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine(InventoryItem.BlindVotePayload, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine("DAO state block height: ", InventoryItem.daoStateChainHeight, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine("Number of BSQ blocks: ", InventoryItem.numBsqBlocks, seedNode, requestInfo, map));
|
||||
sb.append(getLine(InventoryItem.TempProposalPayload, seedNode, requestInfo, map));
|
||||
sb.append(getLine(InventoryItem.ProposalPayload, seedNode, requestInfo, map));
|
||||
sb.append(getLine(InventoryItem.BlindVotePayload, seedNode, requestInfo, map));
|
||||
sb.append(getLine("DAO state block height: ", InventoryItem.daoStateChainHeight, seedNode, requestInfo, map));
|
||||
|
||||
String daoStateChainHeight = null;
|
||||
if (requestInfo.getInventory() != null && requestInfo.getInventory().containsKey(InventoryItem.daoStateChainHeight)) {
|
||||
daoStateChainHeight = requestInfo.getInventory().get(InventoryItem.daoStateChainHeight);
|
||||
}
|
||||
|
||||
sb.append(getLine("DAO state hash: ", InventoryItem.daoStateHash, requestInfo, averageValues, map.values(), daoStateChainHeight));
|
||||
sb.append(getLine("DAO state hash: ", InventoryItem.daoStateHash, seedNode, requestInfo, map));
|
||||
|
||||
// The hash for proposal changes only at first block of blind vote phase but as we do not want to initialize the
|
||||
// dao domain we cannot check that. But we also don't need that as we can just compare that all hashes at all
|
||||
// blocks from all seeds are the same. Same for blindVoteHash.
|
||||
sb.append(getLine("Proposal state hash: ", InventoryItem.proposalHash, requestInfo, averageValues, map.values(), daoStateChainHeight));
|
||||
sb.append(getLine("Blind vote state hash: ", InventoryItem.blindVoteHash, requestInfo, averageValues, map.values(), daoStateChainHeight));
|
||||
sb.append(getLine("Proposal state hash: ", InventoryItem.proposalHash, seedNode, requestInfo, map));
|
||||
sb.append(getLine("Blind vote state hash: ", InventoryItem.blindVoteHash, seedNode, requestInfo, map));
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String getNetworkInfo(RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
private String getNetworkInfo(NodeAddress seedNode,
|
||||
RequestInfo requestInfo,
|
||||
Map<NodeAddress, List<RequestInfo>> map) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(getLine("Max. connections: ", InventoryItem.maxConnections, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine("Number of connections: ", InventoryItem.numConnections, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine("Peak number of connections: ", InventoryItem.peakNumConnections, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine("Number of 'All connections lost' events: ", InventoryItem.numAllConnectionsLostEvents, requestInfo, averageValues, map.values()));
|
||||
sb.append(getLine("Max. connections: ",
|
||||
InventoryItem.maxConnections, seedNode, requestInfo, map));
|
||||
sb.append(getLine("Number of connections: ",
|
||||
InventoryItem.numConnections, seedNode, requestInfo, map));
|
||||
sb.append(getLine("Peak number of connections: ",
|
||||
InventoryItem.peakNumConnections, seedNode, requestInfo, map));
|
||||
sb.append(getLine("Number of 'All connections lost' events: ",
|
||||
InventoryItem.numAllConnectionsLostEvents, seedNode, requestInfo, map));
|
||||
|
||||
sb.append(getLine("Sent messages/sec: ", InventoryItem.sentMessagesPerSec, requestInfo,
|
||||
averageValues, map.values(), null, this::getRounded));
|
||||
sb.append(getLine("Received messages/sec: ", InventoryItem.receivedMessagesPerSec, requestInfo,
|
||||
averageValues, map.values(), null, this::getRounded));
|
||||
sb.append(getLine("Sent kB/sec: ", InventoryItem.sentBytesPerSec, requestInfo,
|
||||
averageValues, map.values(), null, this::getRounded));
|
||||
sb.append(getLine("Received kB/sec: ", InventoryItem.receivedBytesPerSec, requestInfo,
|
||||
averageValues, map.values(), null, this::getRounded));
|
||||
sb.append(getLine("Sent data: ", InventoryItem.sentBytes, requestInfo,
|
||||
averageValues, map.values(), null, value -> Utilities.readableFileSize(Long.parseLong(value))));
|
||||
sb.append(getLine("Received data: ", InventoryItem.receivedBytes, requestInfo,
|
||||
averageValues, map.values(), null, value -> Utilities.readableFileSize(Long.parseLong(value))));
|
||||
sb.append(getLine("Sent messages/sec: ",
|
||||
InventoryItem.sentMessagesPerSec, seedNode, requestInfo, map, this::getRounded));
|
||||
sb.append(getLine("Received messages/sec: ",
|
||||
InventoryItem.receivedMessagesPerSec, seedNode, requestInfo, map, this::getRounded));
|
||||
sb.append(getLine("Sent kB/sec: ",
|
||||
InventoryItem.sentBytesPerSec, seedNode, requestInfo, map, this::getRounded));
|
||||
sb.append(getLine("Received kB/sec: ",
|
||||
InventoryItem.receivedBytesPerSec, seedNode, requestInfo, map, this::getRounded));
|
||||
sb.append(getLine("Sent data: ",
|
||||
InventoryItem.sentBytes, seedNode, requestInfo, map,
|
||||
value -> Utilities.readableFileSize(Long.parseLong(value))));
|
||||
sb.append(getLine("Received data: ",
|
||||
InventoryItem.receivedBytes, seedNode, requestInfo, map,
|
||||
value -> Utilities.readableFileSize(Long.parseLong(value))));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
@ -292,63 +314,87 @@ public class InventoryWebServer {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private String getLine(InventoryItem inventoryItem,
|
||||
NodeAddress seedNode,
|
||||
RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
Collection<List<RequestInfo>> collection) {
|
||||
Map<NodeAddress, List<RequestInfo>> map) {
|
||||
return getLine(getTitle(inventoryItem),
|
||||
inventoryItem,
|
||||
seedNode,
|
||||
requestInfo,
|
||||
averageValues,
|
||||
collection);
|
||||
map);
|
||||
}
|
||||
|
||||
private String getLine(String title,
|
||||
InventoryItem inventoryItem,
|
||||
NodeAddress seedNode,
|
||||
RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
Collection<List<RequestInfo>> collection) {
|
||||
Map<NodeAddress, List<RequestInfo>> map) {
|
||||
return getLine(title,
|
||||
inventoryItem,
|
||||
seedNode,
|
||||
requestInfo,
|
||||
averageValues,
|
||||
collection,
|
||||
null,
|
||||
map,
|
||||
null);
|
||||
}
|
||||
|
||||
private String getLine(String title,
|
||||
InventoryItem inventoryItem,
|
||||
NodeAddress seedNode,
|
||||
RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
Collection<List<RequestInfo>> collection,
|
||||
@Nullable String daoStateChainHeight) {
|
||||
return getLine(title,
|
||||
inventoryItem,
|
||||
requestInfo,
|
||||
averageValues,
|
||||
collection,
|
||||
daoStateChainHeight,
|
||||
null);
|
||||
}
|
||||
|
||||
private String getLine(String title,
|
||||
InventoryItem inventoryItem,
|
||||
RequestInfo requestInfo,
|
||||
Map<InventoryItem, Double> averageValues,
|
||||
Collection<List<RequestInfo>> collection,
|
||||
@Nullable String daoStateChainHeight,
|
||||
Map<NodeAddress, List<RequestInfo>> map,
|
||||
@Nullable Function<String, String> formatter) {
|
||||
String displayValue = requestInfo.getDisplayValue(inventoryItem);
|
||||
String value = requestInfo.getValue(inventoryItem);
|
||||
if (formatter != null && value != null) {
|
||||
displayValue = formatter.apply(value);
|
||||
}
|
||||
Double deviation = inventoryItem.getDeviation(averageValues, value);
|
||||
DeviationSeverity deviationSeverity = inventoryItem.getDeviationSeverity(deviation, collection, value, daoStateChainHeight);
|
||||
|
||||
String deviationAsPercentString = "";
|
||||
DeviationSeverity deviationSeverity = DeviationSeverity.OK;
|
||||
if (requestInfo.getDataMap().containsKey(inventoryItem)) {
|
||||
RequestInfo.Data data = requestInfo.getDataMap().get(inventoryItem);
|
||||
deviationAsPercentString = getDeviationAsPercentString(data.getDeviation());
|
||||
deviationSeverity = data.getDeviationSeverity();
|
||||
}
|
||||
|
||||
List<RequestInfo> requestInfoList = map.get(seedNode);
|
||||
String historicalWarnings = "";
|
||||
String historicalAlerts = "";
|
||||
List<Integer> warningsAtRequestNumber = new ArrayList<>();
|
||||
List<Integer> alertsAtRequestNumber = new ArrayList<>();
|
||||
if (requestInfoList != null) {
|
||||
for (int i = 0; i < requestInfoList.size(); i++) {
|
||||
RequestInfo reqInfo = requestInfoList.get(i);
|
||||
Map<InventoryItem, RequestInfo.Data> deviationInfoMap = reqInfo.getDataMap();
|
||||
if (deviationInfoMap.containsKey(inventoryItem)) {
|
||||
if (deviationInfoMap.get(inventoryItem).isPersistentWarning()) {
|
||||
warningsAtRequestNumber.add(i + 1);
|
||||
} else if (deviationInfoMap.get(inventoryItem).isPersistentAlert()) {
|
||||
alertsAtRequestNumber.add(i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!warningsAtRequestNumber.isEmpty()) {
|
||||
historicalWarnings = inventoryItem.getDeviationTolerance() + " repeated warning(s) at request(s) " + Joiner.on(", ").join(warningsAtRequestNumber);
|
||||
}
|
||||
if (!alertsAtRequestNumber.isEmpty()) {
|
||||
historicalAlerts = inventoryItem.getDeviationTolerance() + " repeated alert(s) at request(s): " + Joiner.on(", ").join(alertsAtRequestNumber);
|
||||
}
|
||||
}
|
||||
String warningIcon = "⚠ ";
|
||||
String historicalWarningsHtml = warningsAtRequestNumber.isEmpty() ? "" :
|
||||
", <b><a id=\"warn\" href=\"#\" title=\"" + historicalWarnings + "\">" + warningIcon + warningsAtRequestNumber.size() + "</a></b>";
|
||||
String errorIcon = "☠ "; // ⚡ ⚡
|
||||
String historicalAlertsHtml = alertsAtRequestNumber.isEmpty() ? "" :
|
||||
", <b><a id=\"alert\" href=\"#\" title=\"" + historicalAlerts + "\">" + errorIcon + alertsAtRequestNumber.size() + "</a></b>";
|
||||
|
||||
return title +
|
||||
getColorTagByDeviationSeverity(deviationSeverity) +
|
||||
displayValue +
|
||||
getDeviationAsPercentString(deviation) +
|
||||
deviationAsPercentString +
|
||||
historicalWarningsHtml +
|
||||
historicalAlertsHtml +
|
||||
CLOSE_TAG;
|
||||
}
|
||||
|
||||
|
@ -367,9 +413,11 @@ public class InventoryWebServer {
|
|||
|
||||
switch (deviationSeverity) {
|
||||
case WARN:
|
||||
return "<font color=\"blue\">";
|
||||
return "<font color=\"#0000cc\">";
|
||||
case ALERT:
|
||||
return "<font color=\"red\">";
|
||||
return "<font color=\"#cc0000\">";
|
||||
case IGNORED:
|
||||
return "<font color=\"#333333\">";
|
||||
case OK:
|
||||
default:
|
||||
return "<font color=\"black\">";
|
||||
|
|
|
@ -15,5 +15,3 @@ devinsn3xuzxhj6pmammrxpydhwwmwp75qkksedo5dn2tlmu7jggo7id.onion:8000 (@devinbilec
|
|||
s67qglwhkgkyvr74.onion:8000 (@emzy)
|
||||
5quyxpxheyvzmb2d.onion:8000 (@miker)
|
||||
rm7b56wbrcczpjvl.onion:8000 (@miker)
|
||||
3f3cu2yw7u457ztq.onion:8000 (@devinbileck)
|
||||
fl3mmribyxgrv63c.onion:8000 (@devinbileck)
|
||||
|
|
|
@ -326,6 +326,10 @@ public final class PeerManager implements ConnectionListener, PersistedDataHost
|
|||
}
|
||||
}
|
||||
|
||||
public void resetNumAllConnectionsLostEvents() {
|
||||
numAllConnectionsLostEvents = 0;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Peer
|
||||
|
|
Loading…
Add table
Reference in a new issue