Merge pull request 'extra logging' (#135) from tron-136 into main
Some checks failed
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/chain_gateway Pipeline was successful
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/fx_ingestor Pipeline was successful
ci/woodpecker/push/frontend Pipeline failed
ci/woodpecker/push/fx_oracle Pipeline failed
ci/woodpecker/push/ledger Pipeline was successful
ci/woodpecker/push/mntx_gateway Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
ci/woodpecker/push/notification Pipeline was successful
ci/woodpecker/push/payments_orchestrator Pipeline was successful
Some checks failed
ci/woodpecker/push/billing_fees Pipeline was successful
ci/woodpecker/push/chain_gateway Pipeline was successful
ci/woodpecker/push/bff Pipeline was successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/fx_ingestor Pipeline was successful
ci/woodpecker/push/frontend Pipeline failed
ci/woodpecker/push/fx_oracle Pipeline failed
ci/woodpecker/push/ledger Pipeline was successful
ci/woodpecker/push/mntx_gateway Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
ci/woodpecker/push/notification Pipeline was successful
ci/woodpecker/push/payments_orchestrator Pipeline was successful
Reviewed-on: #135
This commit was merged in pull request #135.
This commit is contained in:
@@ -3,7 +3,6 @@ package wallet
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -30,7 +29,6 @@ func onChainWalletBalance(ctx context.Context, deps Deps, wallet *model.ManagedW
|
||||
zap.String("token_symbol", strings.ToUpper(strings.TrimSpace(wallet.TokenSymbol))),
|
||||
zap.String("contract", strings.ToLower(strings.TrimSpace(wallet.ContractAddress))),
|
||||
zap.String("wallet_address", strings.ToLower(strings.TrimSpace(wallet.DepositAddress))),
|
||||
zap.String("rpc_endpoint", safeRPCLabel(rpcURL)),
|
||||
}
|
||||
|
||||
if rpcURL == "" {
|
||||
@@ -154,14 +152,3 @@ const erc20ABIJSON = `
|
||||
"type": "function"
|
||||
}
|
||||
]`
|
||||
|
||||
func safeRPCLabel(raw string) string {
|
||||
parsed, err := url.Parse(strings.TrimSpace(raw))
|
||||
if err != nil || parsed.Host == "" {
|
||||
return ""
|
||||
}
|
||||
parsed.User = nil
|
||||
parsed.RawQuery = ""
|
||||
parsed.Fragment = ""
|
||||
return parsed.String()
|
||||
}
|
||||
|
||||
170
api/gateway/chain/internal/service/gateway/rpcclient/clients.go
Normal file
170
api/gateway/chain/internal/service/gateway/rpcclient/clients.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package rpcclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/tech/sendico/gateway/chain/internal/service/gateway/shared"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Clients holds pre-initialised RPC clients keyed by network name.
|
||||
type Clients struct {
|
||||
logger mlogger.Logger
|
||||
clients map[string]*ethclient.Client
|
||||
}
|
||||
|
||||
// Prepare dials all configured networks up front and returns a ready-to-use client set.
|
||||
func Prepare(ctx context.Context, logger mlogger.Logger, networks []shared.Network) (*Clients, error) {
|
||||
if logger == nil {
|
||||
return nil, merrors.Internal("rpc clients: logger is required")
|
||||
}
|
||||
clientLogger := logger.Named("rpc_client")
|
||||
result := &Clients{
|
||||
logger: clientLogger,
|
||||
clients: make(map[string]*ethclient.Client),
|
||||
}
|
||||
|
||||
for _, network := range networks {
|
||||
name := strings.ToLower(strings.TrimSpace(network.Name))
|
||||
rpcURL := strings.TrimSpace(network.RPCURL)
|
||||
if name == "" {
|
||||
clientLogger.Warn("skipping network with empty name during rpc client preparation")
|
||||
continue
|
||||
}
|
||||
if rpcURL == "" {
|
||||
result.Close()
|
||||
err := merrors.InvalidArgument(fmt.Sprintf("rpc url not configured for network %s", name))
|
||||
clientLogger.Warn("rpc url missing", zap.String("network", name))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fields := []zap.Field{
|
||||
zap.String("network", name),
|
||||
}
|
||||
clientLogger.Info("initialising rpc client", fields...)
|
||||
|
||||
dialCtx, cancel := context.WithTimeout(ctx, 15*time.Second)
|
||||
httpClient := &http.Client{
|
||||
Transport: &loggingRoundTripper{
|
||||
logger: clientLogger,
|
||||
network: name,
|
||||
base: http.DefaultTransport,
|
||||
},
|
||||
}
|
||||
rpcCli, err := rpc.DialOptions(dialCtx, rpcURL, rpc.WithHTTPClient(httpClient))
|
||||
cancel()
|
||||
if err != nil {
|
||||
result.Close()
|
||||
clientLogger.Warn("failed to dial rpc endpoint", append(fields, zap.Error(err))...)
|
||||
return nil, merrors.Internal(fmt.Sprintf("rpc dial failed for %s: %s", name, err.Error()))
|
||||
}
|
||||
client := ethclient.NewClient(rpcCli)
|
||||
|
||||
result.clients[name] = client
|
||||
clientLogger.Info("rpc client ready", fields...)
|
||||
}
|
||||
|
||||
if len(result.clients) == 0 {
|
||||
return nil, merrors.InvalidArgument("no rpc clients initialised")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Client returns a prepared client for the given network name.
|
||||
func (c *Clients) Client(network string) (*ethclient.Client, error) {
|
||||
if c == nil {
|
||||
return nil, merrors.Internal("rpc clients not initialised")
|
||||
}
|
||||
name := strings.ToLower(strings.TrimSpace(network))
|
||||
client, ok := c.clients[name]
|
||||
if !ok {
|
||||
return nil, merrors.InvalidArgument(fmt.Sprintf("rpc client not configured for network %s", name))
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// Close tears down all RPC clients, logging each close.
|
||||
func (c *Clients) Close() {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
for name, client := range c.clients {
|
||||
client.Close()
|
||||
if c.logger != nil {
|
||||
c.logger.Info("rpc client closed", zap.String("network", name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type loggingRoundTripper struct {
|
||||
logger mlogger.Logger
|
||||
network string
|
||||
endpoint string
|
||||
base http.RoundTripper
|
||||
}
|
||||
|
||||
func (l *loggingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if l.base == nil {
|
||||
l.base = http.DefaultTransport
|
||||
}
|
||||
|
||||
var reqBody []byte
|
||||
if req.Body != nil {
|
||||
raw, _ := io.ReadAll(req.Body)
|
||||
reqBody = raw
|
||||
req.Body = io.NopCloser(strings.NewReader(string(raw)))
|
||||
}
|
||||
|
||||
fields := []zap.Field{
|
||||
zap.String("network", l.network),
|
||||
zap.String("rpc_endpoint", l.endpoint),
|
||||
}
|
||||
if len(reqBody) > 0 {
|
||||
fields = append(fields, zap.String("rpc_request", truncate(string(reqBody), 2048)))
|
||||
}
|
||||
l.logger.Debug("rpc request", fields...)
|
||||
|
||||
resp, err := l.base.RoundTrip(req)
|
||||
if err != nil {
|
||||
l.logger.Warn("rpc http request failed", append(fields, zap.Error(err))...)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
resp.Body = io.NopCloser(strings.NewReader(string(bodyBytes)))
|
||||
|
||||
respFields := append(fields,
|
||||
zap.Int("status_code", resp.StatusCode),
|
||||
)
|
||||
if len(bodyBytes) > 0 {
|
||||
respFields = append(respFields, zap.String("rpc_response", truncate(string(bodyBytes), 2048)))
|
||||
}
|
||||
if resp.StatusCode >= 400 {
|
||||
l.logger.Warn("rpc response error", respFields...)
|
||||
} else {
|
||||
l.logger.Debug("rpc response", respFields...)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func truncate(s string, max int) string {
|
||||
if len(s) <= max {
|
||||
return s
|
||||
}
|
||||
if max <= 3 {
|
||||
return s[:max]
|
||||
}
|
||||
return s[:max-3] + "..."
|
||||
}
|
||||
@@ -47,12 +47,7 @@ func Error[T any](logger mlogger.Logger, service mservice.Type, code codes.Code,
|
||||
if err != nil {
|
||||
fields = append(fields, zap.Error(err))
|
||||
}
|
||||
logFn := logger.Warn
|
||||
switch code {
|
||||
case codes.Internal, codes.DataLoss, codes.Unavailable:
|
||||
logFn = logger.Error
|
||||
}
|
||||
logFn("gRPC request failed", fields...)
|
||||
logger.Warn("gRPC request failed", fields...)
|
||||
|
||||
msg := message(err)
|
||||
switch {
|
||||
|
||||
Reference in New Issue
Block a user