From 97831af542a8f779f31e3328bf21c54e7c17e1c6 Mon Sep 17 00:00:00 2001 From: wangdayong228 Date: Thu, 22 Apr 2021 10:41:33 +0800 Subject: [PATCH] Rpc logger (#61) * update SubscribeEpochs * add logger for rpc request * update doc * remove comment * simplify logger Co-authored-by: dayong --- api.md | 25 ++++++++----- client.go | 66 +++++++++++++++++++++++----------- client_test.go | 4 +-- example/context/config.toml | 10 +++--- example/context/prepare.go | 5 +-- example/example_client/main.go | 15 ++++++++ go.sum | 4 +++ interface.go | 12 ++++++- types/rpc_log.go | 25 +++++++++++++ 9 files changed, 126 insertions(+), 40 deletions(-) create mode 100644 types/rpc_log.go diff --git a/api.md b/api.md index 65be76d..1b7692a 100644 --- a/api.md +++ b/api.md @@ -211,7 +211,7 @@ creat account manager if option.KeystorePath not empty. #### func NewClientWithRPCRequester ```go -func NewClientWithRPCRequester(rpcRequester rpcRequester) (*Client, error) +func NewClientWithRPCRequester(rpcRequester RpcRequester) (*Client, error) ``` NewClientWithRPCRequester creates client with specified rpcRequester @@ -631,8 +631,8 @@ GetVoteList returns vote list of the given account. func (client *Client) MustNewAddress(base32OrHex string) types.Address ``` MustNewAddress create conflux address by base32 string or hex40 string, if -base32OrHex is base32 and networkID is setted it will check if networkID match. -it will painc if error occured. +base32OrHex is base32 and networkID is passed it will create cfx Address use +networkID of current client. it will painc if error occured. #### func (*Client) NewAddress @@ -640,7 +640,8 @@ it will painc if error occured. func (client *Client) NewAddress(base32OrHex string) (types.Address, error) ``` NewAddress create conflux address by base32 string or hex40 string, if -base32OrHex is base32 and networkID is setted it will check if networkID match. +base32OrHex is base32 and networkID is passed it will create cfx Address use +networkID of current client. #### func (*Client) SendRawTransaction @@ -675,10 +676,11 @@ signature "r,s,v" and sends it to node, and returns responsed transaction. #### func (*Client) SubscribeEpochs ```go -func (client *Client) SubscribeEpochs(channel chan types.WebsocketEpochResponse) (*rpc.ClientSubscription, error) +func (client *Client) SubscribeEpochs(channel chan types.WebsocketEpochResponse, subscriptionEpochType ...types.Epoch) (*rpc.ClientSubscription, error) ``` SubscribeEpochs subscribes consensus results: the total order of blocks, as -expressed by a sequence of epochs. +expressed by a sequence of epochs. Currently subscriptionEpochType only support +"latest_mined" and "latest_state" #### func (*Client) SubscribeLogs @@ -713,14 +715,19 @@ WaitForTransationReceipt waits for transaction receipt valid ```go type ClientOption struct { - KeystorePath string - RetryCount int - RetryInterval time.Duration + KeystorePath string + RetryCount int + RetryInterval time.Duration + CallRpcLogger CallRPCLogger + BatchCallRPCLogger BatchCallRPCLogger } ``` ClientOption for set keystore path and flags for retry +The simplest way to set logger is to use the types.DefaultCallRpcLogger and +types.DefaultBatchCallRPCLogger + ### type Contract ```go diff --git a/client.go b/client.go index 72ae826..54b5acb 100644 --- a/client.go +++ b/client.go @@ -26,15 +26,20 @@ const errMsgApplyTxValues = "failed to apply default transaction values" type Client struct { AccountManager AccountManagerOperator nodeURL string - rpcRequester rpcRequester + rpcRequester RpcRequester networkID uint32 + option ClientOption } // ClientOption for set keystore path and flags for retry +// +// The simplest way to set logger is to use the types.DefaultCallRpcLog and types.DefaultBatchCallRPCLog type ClientOption struct { - KeystorePath string - RetryCount int - RetryInterval time.Duration + KeystorePath string + RetryCount int + RetryInterval time.Duration + CallRpcLog func(method string, args []interface{}, result interface{}, resultError error, duration time.Duration) + BatchCallRPCLog func(b []rpc.BatchElem, err error, duration time.Duration) } // NewClient creates an instance of Client with specified conflux node url, it will creat account manager if option.KeystorePath not empty. @@ -44,7 +49,7 @@ func NewClient(nodeURL string, option ...ClientOption) (*Client, error) { realOption = option[0] } - client, err := newClientWithRetry(nodeURL, realOption.KeystorePath, realOption.RetryCount, realOption.RetryInterval) + client, err := newClientWithRetry(nodeURL, realOption) if err != nil { return nil, errors.Wrap(err, "failed to new client with retry") } @@ -53,7 +58,7 @@ func NewClient(nodeURL string, option ...ClientOption) (*Client, error) { } // NewClientWithRPCRequester creates client with specified rpcRequester -func NewClientWithRPCRequester(rpcRequester rpcRequester) (*Client, error) { +func NewClientWithRPCRequester(rpcRequester RpcRequester) (*Client, error) { return &Client{ rpcRequester: rpcRequester, }, nil @@ -61,39 +66,49 @@ func NewClientWithRPCRequester(rpcRequester rpcRequester) (*Client, error) { // NewClientWithRetry creates a retryable new instance of Client with specified conflux node url and retry options. // -// the retryInterval will be set to 1 second if pass 0 -func newClientWithRetry(nodeURL string, keystorePath string, retryCount int, retryInterval time.Duration) (*Client, error) { +// the clientOption.RetryInterval will be set to 1 second if pass 0 +func newClientWithRetry(nodeURL string, clientOption ClientOption) (*Client, error) { var client Client client.nodeURL = nodeURL + client.option = clientOption rpcClient, err := rpc.Dial(nodeURL) if err != nil { return nil, errors.Wrap(err, "failed to dial to fullnode") } - if retryCount == 0 { + if client.option.RetryCount == 0 { client.rpcRequester = rpcClient } else { // Interval 0 is meaningless and may lead full node busy, so default sets it to 1 second - if retryInterval == 0 { - retryInterval = time.Second + if client.option.RetryInterval == 0 { + client.option.RetryInterval = time.Second } client.rpcRequester = &rpcClientWithRetry{ inner: rpcClient, - retryCount: retryCount, - interval: retryInterval, + retryCount: client.option.RetryCount, + interval: client.option.RetryInterval, + } + } + + if client.option.CallRpcLog == nil { + client.option.CallRpcLog = func(method string, args []interface{}, result interface{}, resultError error, duration time.Duration) { } } + if client.option.BatchCallRPCLog == nil { + client.option.BatchCallRPCLog = func(b []rpc.BatchElem, err error, duration time.Duration) {} + } + _, err = client.GetNetworkID() if err != nil { return nil, errors.Wrap(err, "failed to get networkID") } - if keystorePath != "" { - am := NewAccountManager(keystorePath, client.networkID) + if client.option.KeystorePath != "" { + am := NewAccountManager(client.option.KeystorePath, client.networkID) client.SetAccountManager(am) } @@ -105,7 +120,7 @@ func (client *Client) GetNodeURL() string { return client.nodeURL } -// NewAddress create conflux address by base32 string or hex40 string, if base32OrHex is base32 and networkID is setted it will check if networkID match. +// NewAddress create conflux address by base32 string or hex40 string, if base32OrHex is base32 and networkID is passed it will create cfx Address use networkID of current client. func (client *Client) NewAddress(base32OrHex string) (types.Address, error) { networkID, err := client.GetNetworkID() if err != nil { @@ -114,7 +129,7 @@ func (client *Client) NewAddress(base32OrHex string) (types.Address, error) { return cfxaddress.New(base32OrHex, networkID) } -// MustNewAddress create conflux address by base32 string or hex40 string, if base32OrHex is base32 and networkID is setted it will check if networkID match. +// MustNewAddress create conflux address by base32 string or hex40 string, if base32OrHex is base32 and networkID is passed it will create cfx Address use networkID of current client. // it will painc if error occured. func (client *Client) MustNewAddress(base32OrHex string) types.Address { address, err := client.NewAddress(base32OrHex) @@ -130,7 +145,10 @@ func (client *Client) MustNewAddress(base32OrHex string) types.Address { // The result must be a pointer so that package json can unmarshal into it. You // can also pass nil, in which case the result is ignored. func (client *Client) CallRPC(result interface{}, method string, args ...interface{}) error { - return client.rpcRequester.Call(result, method, args...) + start := time.Now() + err := client.rpcRequester.Call(result, method, args...) + client.option.CallRpcLog(method, args, result, err, time.Since(start)) + return err } // BatchCallRPC sends all given requests as a single batch and waits for the server @@ -141,7 +159,10 @@ func (client *Client) CallRPC(result interface{}, method string, args ...interfa // // Note that batch calls may not be executed atomically on the server side. func (client *Client) BatchCallRPC(b []rpc.BatchElem) error { - return client.rpcRequester.BatchCall(b) + start := time.Now() + err := client.rpcRequester.BatchCall(b) + client.option.BatchCallRPCLog(b, err, time.Since(start)) + return err } // SetAccountManager sets account manager for sign transaction @@ -932,8 +953,11 @@ func (client *Client) SubscribeNewHeads(channel chan types.BlockHeader) (*rpc.Cl return client.rpcRequester.Subscribe(context.Background(), "cfx", channel, "newHeads") } -// SubscribeEpochs subscribes consensus results: the total order of blocks, as expressed by a sequence of epochs. -func (client *Client) SubscribeEpochs(channel chan types.WebsocketEpochResponse) (*rpc.ClientSubscription, error) { +// SubscribeEpochs subscribes consensus results: the total order of blocks, as expressed by a sequence of epochs. Currently subscriptionEpochType only support "latest_mined" and "latest_state" +func (client *Client) SubscribeEpochs(channel chan types.WebsocketEpochResponse, subscriptionEpochType ...types.Epoch) (*rpc.ClientSubscription, error) { + if len(subscriptionEpochType) > 0 { + return client.rpcRequester.Subscribe(context.Background(), "cfx", channel, "epochs", subscriptionEpochType[0].String()) + } return client.rpcRequester.Subscribe(context.Background(), "cfx", channel, "epochs") } diff --git a/client_test.go b/client_test.go index 09d3643..c23796b 100644 --- a/client_test.go +++ b/client_test.go @@ -32,7 +32,7 @@ func _TestNewClient(t *testing.T) { }) defer guard.Unpatch() - client, err := newClientWithRetry("", "", 0, 0) + client, err := newClientWithRetry("", ClientOption{}) Convey("Return error", func() { So(err, ShouldNotEqual, nil) So(client, ShouldEqual, nil) @@ -46,7 +46,7 @@ func _TestNewClient(t *testing.T) { }) defer guard.Unpatch() - client, err := newClientWithRetry("", "", 0, 0) + client, err := newClientWithRetry("", ClientOption{}) // fmt.Printf("client:%+v,err:%+v", client, err) Convey("Return client instance", func() { diff --git a/example/context/config.toml b/example/context/config.toml index f14941a..88645ba 100644 --- a/example/context/config.toml +++ b/example/context/config.toml @@ -1,5 +1,5 @@ -NodeURL = "http://127.0.0.1:12539" -BlockHash = "0x33649d83178942524f52c3a3a7b9a86ea161f72ef2e5c40494ca1341c7a86c9d" -TransactionHash = "0xdca75fe1d4c3187702b433a29609c8ef3125d7a2936a80348f9ea6f400110cea" -BlockHashOfNewContract = "0x3e5f823bcb4228e00b8da839667285e551499382e928b43dbebecf82088df5bf" -ERC20Address = "NET4021259308:TYPE.CONTRACT:ACC2PMEWZKPR34N3DEU10S2DM47SSBMXS2RFYWAD9X" +NodeURL = "http://127.0.0.1:22537" +BlockHash = "0xbbbf51dab51c02413413edf0c5d9e03f7c2bfeb584d4154d82f0b38cd63f2e22" +TransactionHash = "0x1498b96d6e3976b8f167a49436addcb907bb8c202df6100aebb2441a55654691" +BlockHashOfNewContract = "0x2e7dd6c1896dd15729fd9f3573b5a6033464385951f557f44906e8a32d3f1307" +ERC20Address = "NET8958:TYPE.CONTRACT:ACC2PMEWZKPR34N3DEU10S2DM47SSBMXS2CUF67X96" diff --git a/example/context/prepare.go b/example/context/prepare.go index 9230577..1437c7c 100644 --- a/example/context/prepare.go +++ b/example/context/prepare.go @@ -2,7 +2,6 @@ package context import ( "fmt" - "log" "os" "path" "runtime" @@ -59,7 +58,7 @@ func getConfig() { config = exampletypes.Config{} _, err := toml.DecodeFile(configPath, &config) if err != nil { - log.Fatal(err) + panic(err) } fmt.Printf("- to get config done: %+v\n", JSONFmt(config)) @@ -74,6 +73,8 @@ func initClient() { KeystorePath: path.Join(currentDir, "keystore"), RetryCount: 10, RetryInterval: time.Second, + // CallRpcLog: types.DefaultCallRPCLog, + // BatchCallRPCLog: types.DefaultBatchCallRPCLog, } client, err = sdk.NewClient(config.NodeURL, option) if err != nil { diff --git a/example/example_client/main.go b/example/example_client/main.go index bcf1773..c119b45 100644 --- a/example/example_client/main.go +++ b/example/example_client/main.go @@ -8,6 +8,7 @@ import ( sdk "github.com/Conflux-Chain/go-conflux-sdk" "github.com/Conflux-Chain/go-conflux-sdk/example/context" exampletypes "github.com/Conflux-Chain/go-conflux-sdk/example/context/types" + "github.com/Conflux-Chain/go-conflux-sdk/rpc" "github.com/Conflux-Chain/go-conflux-sdk/types" "github.com/Conflux-Chain/go-conflux-sdk/types/cfxaddress" address "github.com/Conflux-Chain/go-conflux-sdk/types/cfxaddress" @@ -100,6 +101,8 @@ func run(_client *sdk.Client) { subscribeNewHeads() subscribeEpochs() subscribeLogs() + + batchCall() } func newAddress() { @@ -595,6 +598,18 @@ func subscribeLogs() { sub.Unsubscribe() } +func batchCall() { + elems := make([]rpc.BatchElem, 2) + elems[0] = rpc.BatchElem{Method: "cfx_epochNumber", Result: &hexutil.Big{}, Args: []interface{}{}} + elems[1] = rpc.BatchElem{Method: "cfx_getBalance", Result: &hexutil.Big{}, Args: []interface{}{client.MustNewAddress("cfxtest:aap9kthvctunvf030rbkk9k7zbzyz12dajp1u3sp4g")}} + err := client.BatchCallRPC(elems) + if err != nil { + panic(err) + } else { + fmt.Printf("batch call rpc done:%+v\n", elems) + } +} + func printResult(method string, args []interface{}, result interface{}, err error) { if err != nil { fmt.Printf("- call method %v with args %+v error: %v\n\n", method, args, err.Error()) diff --git a/go.sum b/go.sum index f3ac349..e9ed17a 100644 --- a/go.sum +++ b/go.sum @@ -48,14 +48,17 @@ github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea h1:j4317fAZh7X github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bacAU/eSnV3dAxVpepaghAdhGoQ= github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c h1:JHHhtb9XWJrGNMcrVP6vyzO4dusgi/HnceHTgxSejUM= github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/ethereum/go-ethereum v1.9.25 h1:mMiw/zOOtCLdGLWfcekua0qPrJTe7FVIiHJ4IKNTfR0= github.com/ethereum/go-ethereum v1.9.25/go.mod h1:vMkFiYLHI4tgPw4k2j4MHKoovchFE8plZ0M9VMk4/oM= +github.com/fatih/color v1.3.0 h1:YehCCcyeQ6Km0D6+IapqPinWBK6y+0eB5umvZXK9WPs= github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc h1:jtW8jbpkO4YirRSyepBOH8E+2HEw6/hKkBvFPwhUN8c= github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= @@ -71,6 +74,7 @@ github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2i github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-sourcemap/sourcemap v2.1.2+incompatible h1:0b/xya7BKGhXuqFESKM4oIiRo9WOt2ebz7KxfreD6ug= github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= diff --git a/interface.go b/interface.go index 7e32770..4e7f3c1 100644 --- a/interface.go +++ b/interface.go @@ -120,9 +120,19 @@ type AccountManagerOperator interface { Sign(tx types.UnsignedTransaction, passphrase string) (v byte, r, s []byte, err error) } -type rpcRequester interface { +type RpcRequester interface { Call(resultPtr interface{}, method string, args ...interface{}) error BatchCall(b []rpc.BatchElem) error Subscribe(ctx context.Context, namespace string, channel interface{}, args ...interface{}) (*rpc.ClientSubscription, error) Close() } + +type CallRPCLogger interface { + Info(method string, args []interface{}, result interface{}, duration time.Duration) + Error(method string, args []interface{}, resultError error, duration time.Duration) +} + +type BatchCallRPCLogger interface { + Info(b []rpc.BatchElem, duration time.Duration) + Error(b []rpc.BatchElem, err error, duration time.Duration) +} diff --git a/types/rpc_log.go b/types/rpc_log.go new file mode 100644 index 0000000..48011aa --- /dev/null +++ b/types/rpc_log.go @@ -0,0 +1,25 @@ +package types + +import ( + "fmt" + "time" + + "github.com/Conflux-Chain/go-conflux-sdk/rpc" + "github.com/Conflux-Chain/go-conflux-sdk/utils" +) + +func DefaultCallRPCLog(method string, args []interface{}, result interface{}, resultError error, duration time.Duration) { + if resultError == nil { + fmt.Printf("call rpc %v sucessfully, args %+v, result %+v, use %v\n", method, utils.PrettyJSON(args), utils.PrettyJSON(result), duration) + return + } + fmt.Printf("call rpc %v failed, args %+v, error: %+v, use %v\n", method, args, resultError, duration) +} + +func DefaultBatchCallRPCLog(b []rpc.BatchElem, err error, duration time.Duration) { + if err == nil { + fmt.Printf("batch call %+v sucessfully, use %v\n", b, duration) + return + } + fmt.Printf("batch call %+v failed, error: %+v, use %v\n", b, err, duration) +}