Skip to content

Commit

Permalink
Add support to RPC for slog logger, and add some helpers for all defa… (
Browse files Browse the repository at this point in the history
#114)

* Add support to RPC for slog logger, and add some helpers for all default log levels to easily pass to the client option

* Update usages of log. to the slog compatible logger

* Update readme to show how to set up logging (and a bit of reorganization)

* Update pkg/rpc/readme.md

Co-authored-by: StartToaster <[email protected]>

---------

Co-authored-by: StartToaster <[email protected]>
  • Loading branch information
cmmarslender and Starttoaster authored Mar 17, 2024
1 parent 71666e5 commit 79749b0
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 24 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/chia-network/go-chia-libs

go 1.18
go 1.21

require (
github.com/google/go-querystring v1.1.0
Expand Down
8 changes: 8 additions & 0 deletions pkg/httpclient/httpclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"io"
"log/slog"
"net/http"
"net/url"
"time"
Expand All @@ -21,6 +22,7 @@ import (
type HTTPClient struct {
config *config.ChiaConfig
baseURL *url.URL
logger *slog.Logger

// If set > 0, will configure http requests with a cache
cacheValidTime time.Duration
Expand Down Expand Up @@ -61,6 +63,7 @@ type HTTPClient struct {
func NewHTTPClient(cfg *config.ChiaConfig, options ...rpcinterface.ClientOptionFunc) (*HTTPClient, error) {
c := &HTTPClient{
config: cfg,
logger: slog.New(rpcinterface.SlogInfo()),

Timeout: 10 * time.Second, // Default, overridable with client option

Expand Down Expand Up @@ -101,6 +104,11 @@ func (c *HTTPClient) SetBaseURL(url *url.URL) error {
return nil
}

// SetLogHandler sets a slog compatible log handler
func (c *HTTPClient) SetLogHandler(handler slog.Handler) {
c.logger = slog.New(handler)
}

// SetCacheValidTime sets how long cache should be valid for
func (c *HTTPClient) SetCacheValidTime(validTime time.Duration) {
c.cacheValidTime = validTime
Expand Down
9 changes: 9 additions & 0 deletions pkg/rpc/clientoptions.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package rpc

import (
"log/slog"
"net/url"
"time"

Expand Down Expand Up @@ -63,3 +64,11 @@ func WithTimeout(timeout time.Duration) rpcinterface.ClientOptionFunc {
return nil
}
}

// WithLogHandler sets a slog compatible log handler to be used for logging
func WithLogHandler(handler slog.Handler) rpcinterface.ClientOptionFunc {
return func(c rpcinterface.Client) error {
c.SetLogHandler(handler)
return nil
}
}
60 changes: 47 additions & 13 deletions pkg/rpc/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,53 @@ There are two helper functions to subscribe to events that come over the websock

`client.Subscribe(service)` - Calling this method, with an appropriate service, subscribes to any events that chia may generate that are not necessarily in responses to requests made from this client (for instance, `metrics` events fire when relevant updates are available that may impact metrics services)

## Logging

By default, a slog compatible text logger set to INFO level will be used to log any information from the RPC clients.

### Change Log Level

To change the log level of the default logger, you can use a client option like the following example:

```go
package main

import (
"github.com/chia-network/go-chia-libs/pkg/rpc"
"github.com/chia-network/go-chia-libs/pkg/rpcinterface"
)

func main() {
client, err := rpc.NewClient(
rpc.ConnectionModeWebsocket,
rpc.WithAutoConfig(),
rpc.WithLogHandler(rpcinterface.SlogDebug()),
)
if err != nil {
// an error occurred
}
}
```

### Custom Log Handler

The `rpc.WithLogHandler()` method accepts a `slog.Handler` interface. Any logger can be provided as long as it conforms to the interface.

## Request Cache

When using HTTP mode, there is an optional request cache that can be enabled with a configurable cache duration. To use the cache, initialize the client with the `rpc.WithCache()` option like the following example:

```go
client, err := rpc.NewClient(rpc.ConnectionModeHTTP, rpc.WithAutoConfig(), rpc.WithCache(60 * time.Second))
if err != nil {
// error happened
}
```

This example sets the cache time to 60 seconds. Any identical requests within the 60 seconds will be served from the local cache rather than making another RPC call.

## Example RPC Calls

### Get Transactions

#### HTTP Mode
Expand Down Expand Up @@ -287,16 +334,3 @@ if state.BlockchainState.IsPresent() {
log.Println(state.BlockchainState.MustGet().Space)
}
```

### Request Cache

When using HTTP mode, there is an optional request cache that can be enabled with a configurable cache duration. To use the cache, initialize the client with the `rpc.WithCache()` option like the following example:

```go
client, err := rpc.NewClient(rpc.ConnectionModeHTTP, rpc.WithAutoConfig(), rpc.WithCache(60 * time.Second))
if err != nil {
// error happened
}
```

This example sets the cache time to 60 seconds. Any identical requests within the 60 seconds will be served from the local cache rather than making another RPC call.
4 changes: 4 additions & 0 deletions pkg/rpcinterface/client.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package rpcinterface

import (
"log/slog"
"net/http"
"net/url"

Expand All @@ -14,6 +15,9 @@ type Client interface {
Do(req *Request, v interface{}) (*http.Response, error)
SetBaseURL(url *url.URL) error

// SetLogHandler sets a slog compatible log handler
SetLogHandler(handler slog.Handler)

// The following are added for websocket compatibility
// Any implementation that these don't make sense for should just do nothing / return nil as applicable

Expand Down
46 changes: 46 additions & 0 deletions pkg/rpcinterface/loghandler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package rpcinterface

import (
"log/slog"
"os"
)

// SlogError returns a text handler preconfigured to ERROR log level
func SlogError() slog.Handler {
opts := &slog.HandlerOptions{
Level: slog.LevelError,
}

handler := slog.NewTextHandler(os.Stdout, opts)
return handler
}

// SlogWarn returns a text handler preconfigured to WARN log level
func SlogWarn() slog.Handler {
opts := &slog.HandlerOptions{
Level: slog.LevelWarn,
}

handler := slog.NewTextHandler(os.Stdout, opts)
return handler
}

// SlogInfo returns a text handler preconfigured to INFO log level
func SlogInfo() slog.Handler {
opts := &slog.HandlerOptions{
Level: slog.LevelInfo,
}

handler := slog.NewTextHandler(os.Stdout, opts)
return handler
}

// SlogDebug returns a text handler preconfigured to DEBUG log level
func SlogDebug() slog.Handler {
opts := &slog.HandlerOptions{
Level: slog.LevelDebug,
}

handler := slog.NewTextHandler(os.Stdout, opts)
return handler
}
2 changes: 0 additions & 2 deletions pkg/util/bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package util

import (
"fmt"
"log"

"github.com/chia-network/go-chia-libs/pkg/types"
)
Expand All @@ -13,7 +12,6 @@ func FormatBytes(bytes types.Uint128) string {
base := uint64(1024)

value := bytes.Div64(base)
log.Printf("%s %s\n", value.String(), "KiB")
for _, label := range labels {
if value.FitsInUint64() {
valueUint64 := float64(value.Uint64()) / float64(base)
Expand Down
23 changes: 15 additions & 8 deletions pkg/websocketclient/websocketclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"encoding/json"
"fmt"
"io"
"log"
"log/slog"
"net/http"
"net/url"
"sync"
Expand All @@ -28,6 +28,7 @@ const origin string = "go-chia-rpc"
type WebsocketClient struct {
config *config.ChiaConfig
baseURL *url.URL
logger *slog.Logger

// Request timeout
Timeout time.Duration
Expand Down Expand Up @@ -62,6 +63,7 @@ type WebsocketClient struct {
func NewWebsocketClient(cfg *config.ChiaConfig, options ...rpcinterface.ClientOptionFunc) (*WebsocketClient, error) {
c := &WebsocketClient{
config: cfg,
logger: slog.New(rpcinterface.SlogInfo()),

Timeout: 10 * time.Second, // Default, overridable with client option

Expand Down Expand Up @@ -111,6 +113,11 @@ func (c *WebsocketClient) SetBaseURL(url *url.URL) error {
return nil
}

// SetLogHandler sets a slog compatible log handler
func (c *WebsocketClient) SetLogHandler(handler slog.Handler) {
c.logger = slog.New(handler)
}

// NewRequest creates an RPC request for the specified service
func (c *WebsocketClient) NewRequest(service rpcinterface.ServiceType, rpcEndpoint rpcinterface.Endpoint, opt interface{}) (*rpcinterface.Request, error) {
request := &rpcinterface.Request{
Expand Down Expand Up @@ -314,14 +321,14 @@ func (c *WebsocketClient) reconnectLoop() {
handler()
}
for {
log.Println("Trying to reconnect...")
c.logger.Info("Trying to reconnect...")
err := c.ensureConnection()
if err == nil {
log.Println("Reconnected!")
c.logger.Info("Reconnected!")
for topic := range c.subscriptions {
err = c.doSubscribe(topic)
if err != nil {
log.Printf("Error subscribing to topic %s: %s\n", topic, err.Error())
c.logger.Error("Error subscribing to topic", "topic", topic, "error", err.Error())
}
}
for _, handler := range c.reconnectHandlers {
Expand All @@ -330,7 +337,7 @@ func (c *WebsocketClient) reconnectLoop() {
return
}

log.Printf("Unable to reconnect: %s\n", err.Error())
c.logger.Error("Unable to reconnect", "error", err.Error())
time.Sleep(5 * time.Second)
}
}
Expand Down Expand Up @@ -397,12 +404,12 @@ func (c *WebsocketClient) listen() {
for {
_, message, err := c.conn.ReadMessage()
if err != nil {
log.Printf("Error reading message on chia websocket: %s\n", err.Error())
c.logger.Error("Error reading message on chia websocket", "error", err.Error())
if _, isCloseErr := err.(*websocket.CloseError); !isCloseErr {
log.Println("Chia websocket sent close message, attempting to close connection...")
c.logger.Debug("Chia websocket sent close message, attempting to close connection...")
closeConnErr := c.conn.Close()
if closeConnErr != nil {
log.Printf("Error closing chia websocket connection: %s\n", closeConnErr.Error())
c.logger.Error("Error closing chia websocket connection", "error", closeConnErr.Error())
}
}
c.conn = nil
Expand Down

0 comments on commit 79749b0

Please sign in to comment.