Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to RPC for slog logger, and add some helpers for all defa… #114

Merged
merged 4 commits into from
Mar 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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