Skip to content

Commit

Permalink
chore: Change to defra errors and handle errors stacktrace (#794)
Browse files Browse the repository at this point in the history
Description
This PR moves our error handling from the standard library errors package to our own errors package. It also removes the logger stacktrace default which logs the stacktrace at the line of logging and introduces stacktrace logging from the creation of the error.
  • Loading branch information
fredcarle authored Sep 16, 2022
1 parent 89c6a8f commit f58062b
Show file tree
Hide file tree
Showing 96 changed files with 493 additions and 417 deletions.
5 changes: 3 additions & 2 deletions api/http/handlerfuncs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/sourcenetwork/defradb/client"
badgerds "github.com/sourcenetwork/defradb/datastore/badger/v3"
"github.com/sourcenetwork/defradb/db"
"github.com/sourcenetwork/defradb/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
Expand Down Expand Up @@ -188,7 +189,7 @@ func TestExecGQLWithMockBody(t *testing.T) {
env = "dev"
mockReadCloser := mockReadCloser{}
// if Read is called, it will return error
mockReadCloser.On("Read", mock.AnythingOfType("[]uint8")).Return(0, fmt.Errorf("error reading"))
mockReadCloser.On("Read", mock.AnythingOfType("[]uint8")).Return(0, errors.New("error reading"))

errResponse := ErrorResponse{}
testRequest(testOptions{
Expand Down Expand Up @@ -478,7 +479,7 @@ func TestLoadSchemaHandlerWithReadBodyError(t *testing.T) {
env = "dev"
mockReadCloser := mockReadCloser{}
// if Read is called, it will return error
mockReadCloser.On("Read", mock.AnythingOfType("[]uint8")).Return(0, fmt.Errorf("error reading"))
mockReadCloser.On("Read", mock.AnythingOfType("[]uint8")).Return(0, errors.New("error reading"))

errResponse := ErrorResponse{}
testRequest(testOptions{
Expand Down
12 changes: 6 additions & 6 deletions cli/addreplicator.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ package cli

import (
"context"
"fmt"

ma "github.com/multiformats/go-multiaddr"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"

"github.com/sourcenetwork/defradb/errors"
"github.com/sourcenetwork/defradb/logging"
netclient "github.com/sourcenetwork/defradb/net/api/client"
)
Expand All @@ -33,12 +33,12 @@ for the p2p data sync system.`,
if err := cmd.Usage(); err != nil {
return err
}
return fmt.Errorf("must specify two arguments: collection and peer")
return errors.New("must specify two arguments: collection and peer")
}
collection := args[0]
peerAddr, err := ma.NewMultiaddr(args[1])
if err != nil {
return fmt.Errorf("could not parse peer address: %w", err)
return errors.Wrap("could not parse peer address", err)
}

log.FeedbackInfo(
Expand All @@ -52,20 +52,20 @@ for the p2p data sync system.`,
cred := insecure.NewCredentials()
client, err := netclient.NewClient(cfg.Net.RPCAddress, grpc.WithTransportCredentials(cred))
if err != nil {
return fmt.Errorf("failed to create RPC client: %w", err)
return errors.Wrap("failed to create RPC client", err)
}

rpcTimeoutDuration, err := cfg.Net.RPCTimeoutDuration()
if err != nil {
return fmt.Errorf("failed to parse RPC timeout duration: %w", err)
return errors.Wrap("failed to parse RPC timeout duration", err)
}

ctx, cancel := context.WithTimeout(cmd.Context(), rpcTimeoutDuration)
defer cancel()

pid, err := client.AddReplicator(ctx, collection, peerAddr)
if err != nil {
return fmt.Errorf("failed to add replicator, request failed: %w", err)
return errors.Wrap("failed to add replicator, request failed", err)
}
log.FeedbackInfo(ctx, "Successfully added replicator", logging.NewKV("PID", pid))
return nil
Expand Down
17 changes: 9 additions & 8 deletions cli/blocks_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"os"

httpapi "github.com/sourcenetwork/defradb/api/http"
"github.com/sourcenetwork/defradb/errors"
"github.com/spf13/cobra"
)

Expand All @@ -28,45 +29,45 @@ var getCmd = &cobra.Command{
if err = cmd.Usage(); err != nil {
return err
}
return fmt.Errorf("get requires a CID argument")
return errors.New("get requires a CID argument")
}
cid := args[0]

endpoint, err := httpapi.JoinPaths(cfg.API.AddressToURL(), httpapi.BlocksPath, cid)
if err != nil {
return fmt.Errorf("failed to join endpoint: %w", err)
return errors.Wrap("failed to join endpoint", err)
}

res, err := http.Get(endpoint.String())
if err != nil {
return fmt.Errorf("failed to send get request: %w", err)
return errors.Wrap("failed to send get request", err)
}

defer func() {
if e := res.Body.Close(); e != nil {
err = fmt.Errorf("failed to read response body: %v: %w", e.Error(), err)
err = errors.Wrap(fmt.Sprintf("failed to read response body: %v", e.Error()), err)
}
}()

response, err := io.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("failed to read response body: %w", err)
return errors.Wrap("failed to read response body", err)
}

stdout, err := os.Stdout.Stat()
if err != nil {
return fmt.Errorf("failed to stat stdout: %w", err)
return errors.Wrap("failed to stat stdout", err)
}
if isFileInfoPipe(stdout) {
cmd.Println(string(response))
} else {
graphlErr, err := hasGraphQLErrors(response)
if err != nil {
return fmt.Errorf("failed to handle GraphQL errors: %w", err)
return errors.Wrap("failed to handle GraphQL errors", err)
}
indentedResult, err := indentJSON(response)
if err != nil {
return fmt.Errorf("failed to pretty print response: %w", err)
return errors.Wrap("failed to pretty print response", err)
}
if graphlErr {
log.FeedbackError(cmd.Context(), indentedResult)
Expand Down
27 changes: 14 additions & 13 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"strings"

"github.com/sourcenetwork/defradb/config"
"github.com/sourcenetwork/defradb/errors"
"github.com/sourcenetwork/defradb/logging"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -57,7 +58,7 @@ func readStdin() (string, error) {
s.Write(scanner.Bytes())
}
if err := scanner.Err(); err != nil {
return "", fmt.Errorf("reading standard input: %w", err)
return "", errors.Wrap("reading standard input", err)
}
return s.String(), nil
}
Expand All @@ -76,7 +77,7 @@ func hasGraphQLErrors(buf []byte) (bool, error) {
errs := graphqlErrors{}
err := json.Unmarshal(buf, &errs)
if err != nil {
return false, fmt.Errorf("couldn't parse GraphQL response %w", err)
return false, errors.Wrap("couldn't parse GraphQL response %w", err)
}
if errs.Errors != nil {
return true, nil
Expand All @@ -100,7 +101,7 @@ func parseAndConfigLog(ctx context.Context, cfg *config.LoggingConfig, cmd *cobr
// handle --logger <name>,<field>=<value>,... --logger <name2>,<field>=<value>,..
loggerKVs, err := cmd.Flags().GetStringArray("logger")
if err != nil {
return fmt.Errorf("can't get logger flag: %w", err)
return errors.Wrap("can't get logger flag", err)
}

for _, kvs := range loggerKVs {
Expand All @@ -111,7 +112,7 @@ func parseAndConfigLog(ctx context.Context, cfg *config.LoggingConfig, cmd *cobr

loggingConfig, err := cfg.ToLoggerConfig()
if err != nil {
return fmt.Errorf("could not get logging config: %w", err)
return errors.Wrap("could not get logging config", err)
}
logging.SetConfig(loggingConfig)

Expand All @@ -125,7 +126,7 @@ func parseAndConfigLogAllParams(ctx context.Context, cfg *config.LoggingConfig,

parsed := strings.Split(kvs, ",")
if len(parsed) <= 1 {
return fmt.Errorf("logger was not provided as comma-separated pairs of <name>=<value>: %s", kvs)
return errors.New(fmt.Sprintf("logger was not provided as comma-separated pairs of <name>=<value>: %s", kvs))
}
name := parsed[0]

Expand All @@ -134,12 +135,12 @@ func parseAndConfigLogAllParams(ctx context.Context, cfg *config.LoggingConfig,
for _, kv := range parsed[1:] {
parsedKV := strings.Split(kv, "=")
if len(parsedKV) != 2 {
return fmt.Errorf("level was not provided as <key>=<value> pair: %s", kv)
return errors.New(fmt.Sprintf("level was not provided as <key>=<value> pair: %s", kv))
}

logcfg, err := cfg.GetOrCreateNamedLogger(name)
if err != nil {
return fmt.Errorf("could not get named logger config: %w", err)
return errors.Wrap("could not get named logger config", err)
}

// handle field
Expand All @@ -153,23 +154,23 @@ func parseAndConfigLogAllParams(ctx context.Context, cfg *config.LoggingConfig,
case "stacktrace": // bool
boolValue, err := strconv.ParseBool(parsedKV[1])
if err != nil {
return fmt.Errorf("couldn't parse kv bool: %w", err)
return errors.Wrap("couldn't parse kv bool", err)
}
logcfg.Stacktrace = boolValue
case "nocolor": // bool
boolValue, err := strconv.ParseBool(parsedKV[1])
if err != nil {
return fmt.Errorf("couldn't parse kv bool: %w", err)
return errors.Wrap("couldn't parse kv bool", err)
}
logcfg.NoColor = boolValue
case "caller": // bool
boolValue, err := strconv.ParseBool(parsedKV[1])
if err != nil {
return fmt.Errorf("couldn't parse kv bool: %w", err)
return errors.Wrap("couldn't parse kv bool", err)
}
logcfg.Caller = boolValue
default:
return fmt.Errorf("unknown parameter for logger: %s", param)
return errors.New(fmt.Sprintf("unknown parameter for logger: %s", param))
}
}
return nil
Expand Down Expand Up @@ -197,12 +198,12 @@ func parseAndConfigLogStringParam(
for _, kv := range parsed[1:] {
parsedKV := strings.Split(kv, "=")
if len(parsedKV) != 2 {
return fmt.Errorf("level was not provided as <key>=<value> pair: %s", kv)
return errors.New(fmt.Sprintf("level was not provided as <key>=<value> pair: %s", kv))
}

logcfg, err := cfg.GetOrCreateNamedLogger(parsedKV[0])
if err != nil {
return fmt.Errorf("could not get named logger config: %w", err)
return errors.Wrap("could not get named logger config", err)
}

paramSetterFn(&logcfg.LoggingConfig, parsedKV[1])
Expand Down
13 changes: 7 additions & 6 deletions cli/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"os"

httpapi "github.com/sourcenetwork/defradb/api/http"
"github.com/sourcenetwork/defradb/errors"
"github.com/spf13/cobra"
)

Expand All @@ -27,31 +28,31 @@ var dumpCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, _ []string) (err error) {
stdout, err := os.Stdout.Stat()
if err != nil {
return fmt.Errorf("failed to stat stdout: %w", err)
return errors.Wrap("failed to stat stdout", err)
}
if !isFileInfoPipe(stdout) {
log.FeedbackInfo(cmd.Context(), "Requesting the database to dump its state, server-side...")
}

endpoint, err := httpapi.JoinPaths(cfg.API.AddressToURL(), httpapi.DumpPath)
if err != nil {
return fmt.Errorf("failed to join endpoint: %w", err)
return errors.Wrap("failed to join endpoint", err)
}

res, err := http.Get(endpoint.String())
if err != nil {
return fmt.Errorf("failed dump request: %w", err)
return errors.Wrap("failed dump request", err)
}

defer func() {
if e := res.Body.Close(); e != nil {
err = fmt.Errorf("failed to read response body: %v: %w", e.Error(), err)
err = errors.Wrap(fmt.Sprintf("failed to read response body: %v", e.Error()), err)
}
}()

response, err := io.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("failed to read response body: %w", err)
return errors.Wrap("failed to read response body", err)
}

if isFileInfoPipe(stdout) {
Expand All @@ -66,7 +67,7 @@ var dumpCmd = &cobra.Command{
r := dumpResponse{}
err = json.Unmarshal(response, &r)
if err != nil {
return fmt.Errorf("failed parsing of response: %w", err)
return errors.Wrap("failed parsing of response", err)
}
log.FeedbackInfo(cmd.Context(), r.Data.Response)
}
Expand Down
15 changes: 8 additions & 7 deletions cli/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"os"

"github.com/sourcenetwork/defradb/config"
"github.com/sourcenetwork/defradb/errors"
"github.com/spf13/cobra"
)

Expand All @@ -36,7 +37,7 @@ var initCmd = &cobra.Command{
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
err := cfg.LoadWithoutRootDir()
if err != nil {
return fmt.Errorf("failed to load configuration: %w", err)
return errors.Wrap("failed to load configuration", err)
}

// parse loglevel overrides.
Expand All @@ -51,11 +52,11 @@ var initCmd = &cobra.Command{
if err := cmd.Usage(); err != nil {
return err
}
return fmt.Errorf("init command requires one rootdir argument, or no argument")
return errors.New("init command requires one rootdir argument, or no argument")
}
rootDir, rootDirExists, err := config.GetRootDir(rootDirPath)
if err != nil {
return fmt.Errorf("failed to get root dir: %w", err)
return errors.Wrap("failed to get root dir", err)
}
if rootDirExists {
// we assume the config file is using its default path in the rootdir
Expand All @@ -66,11 +67,11 @@ var initCmd = &cobra.Command{
if reinitialize {
err = os.Remove(configFilePath)
if err != nil {
return fmt.Errorf("failed to remove configuration file: %w", err)
return errors.Wrap("failed to remove configuration file", err)
}
err = cfg.WriteConfigFileToRootDir(rootDir)
if err != nil {
return fmt.Errorf("failed to create configuration file: %w", err)
return errors.Wrap("failed to create configuration file", err)
}
log.FeedbackInfo(cmd.Context(), fmt.Sprintf("Reinitialized configuration file at %v", configFilePath))
} else {
Expand All @@ -85,14 +86,14 @@ var initCmd = &cobra.Command{
} else {
err = cfg.WriteConfigFileToRootDir(rootDir)
if err != nil {
return fmt.Errorf("failed to create configuration file: %w", err)
return errors.Wrap("failed to create configuration file", err)
}
log.FeedbackInfo(cmd.Context(), fmt.Sprintf("Initialized configuration file at %v", configFilePath))
}
} else {
err = config.CreateRootDirWithDefaultConfig(rootDir)
if err != nil {
return fmt.Errorf("failed to create root dir: %w", err)
return errors.Wrap("failed to create root dir", err)
}
log.FeedbackInfo(cmd.Context(), fmt.Sprintf("Created DefraDB root directory at %v", rootDir))
}
Expand Down
Loading

0 comments on commit f58062b

Please sign in to comment.