problem: unnecessary exceptions in logs when an unavailable blockchain is requested

This commit is contained in:
Igor Artamonov
2019-12-30 22:22:30 -05:00
parent 38bb9a0fa5
commit d69ee41e7a
4 changed files with 38 additions and 11 deletions

View File

@@ -0,0 +1,17 @@
package io.emeraldpay.dshackle
import io.emeraldpay.grpc.Chain
/**
* Exception that should be handled/logged without a stacktrace in production
*/
open class SilentException(message: String) : Exception(message) {
/**
* Blockchain is not available or not supported by current instance of the Dshackle
*/
class UnsupportedBlockchain(val blockchainId: Int): SilentException("Unsupported blockchain $blockchainId") {
constructor(chain: Chain) : this(chain.id)
}
}

View File

@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.rpc
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import com.google.protobuf.ByteString import com.google.protobuf.ByteString
import io.emeraldpay.api.proto.BlockchainOuterClass import io.emeraldpay.api.proto.BlockchainOuterClass
import io.emeraldpay.dshackle.SilentException
import io.emeraldpay.dshackle.upstream.* import io.emeraldpay.dshackle.upstream.*
import io.emeraldpay.dshackle.quorum.AlwaysQuorum import io.emeraldpay.dshackle.quorum.AlwaysQuorum
import io.emeraldpay.dshackle.quorum.CallQuorum import io.emeraldpay.dshackle.quorum.CallQuorum
@@ -81,10 +82,10 @@ class NativeCall(
fun prepareCall(request: BlockchainOuterClass.NativeCallRequest): Flux<CallContext<RawCallDetails>> { fun prepareCall(request: BlockchainOuterClass.NativeCallRequest): Flux<CallContext<RawCallDetails>> {
val chain = Chain.byId(request.chain.number) val chain = Chain.byId(request.chain.number)
if (chain == Chain.UNSPECIFIED) { if (chain == Chain.UNSPECIFIED) {
return Flux.error(CallFailure(0, Exception("Invalid chain id: ${request.chain.number}"))) return Flux.error(CallFailure(0, SilentException.UnsupportedBlockchain(request.chain.number)))
} }
val upstream = upstreams.getUpstream(chain) val upstream = upstreams.getUpstream(chain)
?: return Flux.error(CallFailure(0, Exception("Chain ${chain.id} is unavailable"))) ?: return Flux.error(CallFailure(0, SilentException.UnsupportedBlockchain(chain)))
return prepareCall(request, upstream) return prepareCall(request, upstream)
} }

View File

@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.rpc
import io.emeraldpay.api.proto.BlockchainOuterClass import io.emeraldpay.api.proto.BlockchainOuterClass
import io.emeraldpay.api.proto.Common import io.emeraldpay.api.proto.Common
import io.emeraldpay.dshackle.Defaults import io.emeraldpay.dshackle.Defaults
import io.emeraldpay.dshackle.SilentException
import io.emeraldpay.dshackle.upstream.Selector import io.emeraldpay.dshackle.upstream.Selector
import io.emeraldpay.dshackle.upstream.Upstreams import io.emeraldpay.dshackle.upstream.Upstreams
import io.emeraldpay.grpc.Chain import io.emeraldpay.grpc.Chain
@@ -34,7 +35,6 @@ import reactor.core.publisher.Mono
import reactor.core.publisher.TopicProcessor import reactor.core.publisher.TopicProcessor
import reactor.core.publisher.toFlux import reactor.core.publisher.toFlux
import reactor.core.scheduler.Scheduler import reactor.core.scheduler.Scheduler
import java.lang.Exception
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@@ -95,10 +95,10 @@ class TrackAddress(
private fun initializeSimple(request: BlockchainOuterClass.BalanceRequest): Flux<SimpleAddress> { private fun initializeSimple(request: BlockchainOuterClass.BalanceRequest): Flux<SimpleAddress> {
val chain = Chain.byId(request.asset.chainValue) val chain = Chain.byId(request.asset.chainValue)
if (!upstreams.isAvailable(chain)) { if (!upstreams.isAvailable(chain)) {
return Flux.error(Exception("Unsupported chain ${request.asset.chainValue}")) return Flux.error(SilentException.UnsupportedBlockchain(request.asset.chainValue))
} }
if (request.asset.code?.toLowerCase() != "ether") { if (request.asset.code?.toLowerCase() != "ether") {
return Flux.error(Exception("Unsupported asset ${request.asset.code}")) return Flux.error(SilentException("Unsupported asset ${request.asset.code}"))
} }
return when { return when {
request.address.addrTypeCase == Common.AnyAddress.AddrTypeCase.ADDRESS_SINGLE -> request.address.addrTypeCase == Common.AnyAddress.AddrTypeCase.ADDRESS_SINGLE ->
@@ -133,8 +133,16 @@ class TrackAddress(
buildResponse(it) buildResponse(it)
} }
Flux.merge(current, bus).doFinally { stopTracking(tracked) } Flux.merge(current, bus).doFinally { stopTracking(tracked) }
}.doOnError { t -> }
log.warn("Failed to process subscription", t) .doOnError { t ->
if (t is SilentException) {
if (t is SilentException.UnsupportedBlockchain) {
log.warn("Unsupported blockchain: ${t.blockchainId}")
}
log.debug("Failed to process subscription", t)
} else {
log.warn("Failed to process subscription", t)
}
} }
} }
} }
@@ -167,7 +175,7 @@ class TrackAddress(
} }
fun getBalance(addr: SimpleAddress): Mono<Wei> { fun getBalance(addr: SimpleAddress): Mono<Wei> {
val up = upstreams.getUpstream(addr.chain) ?: return Mono.error(Exception("Unsupported chain: ${addr.chain}")) val up = upstreams.getUpstream(addr.chain) ?: return Mono.error(SilentException.UnsupportedBlockchain(addr.chain))
return up.getApi(Selector.empty) return up.getApi(Selector.empty)
.flatMap { api -> api.executeAndConvert(Commands.eth().getBalance(addr.address, BlockTag.LATEST)) } .flatMap { api -> api.executeAndConvert(Commands.eth().getBalance(addr.address, BlockTag.LATEST)) }
.timeout(Defaults.timeout) .timeout(Defaults.timeout)

View File

@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.rpc
import com.google.protobuf.ByteString import com.google.protobuf.ByteString
import io.emeraldpay.api.proto.BlockchainOuterClass import io.emeraldpay.api.proto.BlockchainOuterClass
import io.emeraldpay.api.proto.Common import io.emeraldpay.api.proto.Common
import io.emeraldpay.dshackle.SilentException
import io.emeraldpay.dshackle.upstream.Selector import io.emeraldpay.dshackle.upstream.Selector
import io.emeraldpay.dshackle.upstream.Upstream import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.dshackle.upstream.Upstreams import io.emeraldpay.dshackle.upstream.Upstreams
@@ -142,7 +143,7 @@ class TrackTx(
fun prepareTracking(request: BlockchainOuterClass.TxStatusRequest): TxDetails { fun prepareTracking(request: BlockchainOuterClass.TxStatusRequest): TxDetails {
val chain = Chain.byId(request.chainValue) val chain = Chain.byId(request.chainValue)
if (!clients.containsKey(chain)) { if (!clients.containsKey(chain)) {
throw Exception("Unsupported blockchain: ${chain}") throw SilentException.UnsupportedBlockchain(chain)
} }
val bus = TopicProcessor.create<Notification>() val bus = TopicProcessor.create<Notification>()
val details = TxDetails( val details = TxDetails(
@@ -208,7 +209,7 @@ class TrackTx(
private fun loadWeight(tx: TxDetails): Mono<TxDetails> { private fun loadWeight(tx: TxDetails): Mono<TxDetails> {
val upstream = upstreams.getUpstream(tx.chain) val upstream = upstreams.getUpstream(tx.chain)
?: return Mono.error(Exception("Unsupported blockchain: ${tx.chain}")) ?: return Mono.error(SilentException.UnsupportedBlockchain(tx.chain))
return upstream.getApi(Selector.empty) return upstream.getApi(Selector.empty)
.flatMap { api -> api.executeAndConvert(Commands.eth().getBlock(tx.status.blockHash)) } .flatMap { api -> api.executeAndConvert(Commands.eth().getBlock(tx.status.blockHash)) }
.map { block -> .map { block ->
@@ -249,7 +250,7 @@ class TrackTx(
private fun checkForUpdate(tx: TxDetails): Mono<TxDetails> { private fun checkForUpdate(tx: TxDetails): Mono<TxDetails> {
val initialStatus = tx.status val initialStatus = tx.status
val upstream = upstreams.getUpstream(tx.chain) ?: return Mono.error(Exception("Unsupported blockchain: ${tx.chain}")) val upstream = upstreams.getUpstream(tx.chain) ?: return Mono.error(SilentException.UnsupportedBlockchain(tx.chain))
val execution = upstream.getApi(Selector.empty) val execution = upstream.getApi(Selector.empty)
.flatMap { api -> api.executeAndConvert(Commands.eth().getTransaction(tx.txid)) } .flatMap { api -> api.executeAndConvert(Commands.eth().getTransaction(tx.txid)) }
return execution return execution