diff --git a/src/main/java/bisq/price/spot/ArsBlueRateTransformer.java b/src/main/java/bisq/price/spot/ArsBlueRateTransformer.java index 5d6b2f9..f3ac069 100644 --- a/src/main/java/bisq/price/spot/ArsBlueRateTransformer.java +++ b/src/main/java/bisq/price/spot/ArsBlueRateTransformer.java @@ -19,14 +19,12 @@ import bisq.price.spot.providers.BlueRateProvider; import bisq.price.util.bluelytics.ArsBlueMarketGapProvider; -import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.util.Optional; import java.util.OptionalDouble; @Component -@Slf4j public class ArsBlueRateTransformer implements ExchangeRateTransformer { private final ArsBlueMarketGapProvider blueMarketGapProvider; @@ -54,8 +52,8 @@ public Optional apply(ExchangeRateProvider provider, ExchangeRate originalExchangeRate.getProvider() ); - log.info(String.format("%s transformed from %s to %s", - originalExchangeRate.getCurrency(), originalExchangeRate.getPrice(), blueRate)); + provider.getGatedLogging().maybeLogInfo(String.format("%s transformed from %s to %s", + originalExchangeRate.getCurrency(), originalExchangeRate.getPrice(), blueRate)); return Optional.of(newExchangeRate); } diff --git a/src/main/java/bisq/price/spot/ExchangeRateProvider.java b/src/main/java/bisq/price/spot/ExchangeRateProvider.java index d442a51..5dafb61 100644 --- a/src/main/java/bisq/price/spot/ExchangeRateProvider.java +++ b/src/main/java/bisq/price/spot/ExchangeRateProvider.java @@ -20,6 +20,8 @@ import bisq.core.locale.CurrencyUtil; import bisq.core.locale.TradeCurrency; import bisq.price.PriceProvider; +import bisq.price.util.GatedLogging; +import lombok.Getter; import org.knowm.xchange.Exchange; import org.knowm.xchange.ExchangeFactory; import org.knowm.xchange.currency.Currency; @@ -32,7 +34,6 @@ import org.knowm.xchange.service.marketdata.params.Params; import org.springframework.core.env.Environment; -import javax.annotation.Nullable; import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; @@ -59,6 +60,8 @@ public abstract class ExchangeRateProvider extends PriceProvider providers; private final List transformers; + private final GatedLogging gatedLogging = new GatedLogging(); /** * Construct an {@link ExchangeRateService} with a list of all @@ -87,6 +88,7 @@ public Map getAllMarketPrices() { * by currency code */ private Map getAggregateExchangeRates() { + boolean maybeLogDetails = gatedLogging.gatingOperation(); Map aggregateExchangeRates = new HashMap<>(); // Query all providers and collect all exchange rates, grouped by currency code @@ -110,7 +112,7 @@ private Map getAggregateExchangeRates() { } else { // If multiple providers have rates for this currency, then // aggregate = average of the rates - double priceAvg = priceAverageWithOutliersRemoved(exchangeRateList, currencyCode); + double priceAvg = priceAverageWithOutliersRemoved(exchangeRateList, currencyCode, maybeLogDetails); aggregateExchangeRate = new ExchangeRate( currencyCode, BigDecimal.valueOf(priceAvg), @@ -123,7 +125,8 @@ private Map getAggregateExchangeRates() { return aggregateExchangeRates; } - private double priceAverageWithOutliersRemoved(List exchangeRateList, String contextInfo) { + private double priceAverageWithOutliersRemoved( + List exchangeRateList, String contextInfo, boolean logOutliers) { final List yValues = exchangeRateList.stream(). mapToDouble(ExchangeRate::getPrice).boxed().collect(Collectors.toList()); Tuple2 tuple = InlierUtil.findInlierRange(yValues, 0, getOutlierStdDeviation()); @@ -145,16 +148,18 @@ private double priceAverageWithOutliersRemoved(List exchangeRateLi double priceAvg = opt.orElseThrow(IllegalStateException::new); // log the outlier prices which were removed from the average, if any. - for (ExchangeRate badRate : exchangeRateList.stream() - .filter(e -> !filteredPrices.contains(e)) - .collect(Collectors.toList())) { - log.info("{} {} outlier price removed:{}, lower/upper bounds:{}/{}, consensus price:{}", - badRate.getProvider(), - badRate.getCurrency(), - badRate.getPrice(), - lowerBound, - upperBound, - priceAvg); + if (logOutliers) { + for (ExchangeRate badRate : exchangeRateList.stream() + .filter(e -> !filteredPrices.contains(e)) + .collect(Collectors.toList())) { + log.info("{} {} outlier price removed:{}, lower/upper bounds:{}/{}, consensus price:{}", + badRate.getProvider(), + badRate.getCurrency(), + badRate.getPrice(), + lowerBound, + upperBound, + priceAvg); + } } return priceAvg; } diff --git a/src/main/java/bisq/price/util/GatedLogging.java b/src/main/java/bisq/price/util/GatedLogging.java new file mode 100644 index 0000000..b4070cd --- /dev/null +++ b/src/main/java/bisq/price/util/GatedLogging.java @@ -0,0 +1,46 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.price.util; + +import lombok.extern.slf4j.Slf4j; + +import java.time.Instant; + +/* Per https://github.com/bisq-network/bisq-pricenode/issues/33 + * There's too much logging of outlier filtering data, fills up the logs too fast and obliterates other valid logging. + * It correlates with client requests. Change that logging so its output once per minute. + */ + +@Slf4j +public class GatedLogging { + private long timestampOfLastLogMessage = 0; + + public void maybeLogInfo(String format, Object... params) { + if (gatingOperation()) { + log.info(format, params); + } + } + + public boolean gatingOperation() { + if (Instant.now().getEpochSecond() - timestampOfLastLogMessage > 60) { + timestampOfLastLogMessage = Instant.now().getEpochSecond(); + return true; + } + return false; + } +}