Skip to content
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
4 changes: 1 addition & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,7 @@ COMMANDS += evm
COMMANDS += sentinel
COMMANDS += caplin
COMMANDS += snapshots



COMMANDS += diag

# build each command using %.cmd rule
$(COMMANDS): %: %.cmd
Expand Down
77 changes: 77 additions & 0 deletions cmd/diag/downloader/downloader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package downloader

import (
"encoding/json"
"fmt"

"github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/diagnostics"
"github.com/ledgerwatch/erigon/cmd/diag/flags"
"github.com/ledgerwatch/erigon/cmd/diag/util"
"github.com/urfave/cli/v2"
)

var Command = cli.Command{
Action: print,
Name: "downloader",
Aliases: []string{"dl"},
Usage: "print snapshot download stats",
ArgsUsage: "",
Flags: []cli.Flag{
&flags.DebugURLFlag,
&flags.OutputFlag,
},
Description: ``,
}

func print(cliCtx *cli.Context) error {
var data diagnostics.SyncStatistics
url := "http://" + cliCtx.String(flags.DebugURLFlag.Name) + "/debug/snapshot-sync"

err := util.MakeHttpGetCall(cliCtx.Context, url, &data)

if err != nil {
return err
}

switch cliCtx.String(flags.OutputFlag.Name) {
case "json":
bytes, err := json.Marshal(data.SnapshotDownload)

if err != nil {
return err
}

fmt.Println(string(bytes))

case "text":
fmt.Println("-------------------Snapshot Download-------------------")

snapDownload := data.SnapshotDownload
var remainingBytes uint64
percent := 50
if snapDownload.Total > snapDownload.Downloaded {
remainingBytes = snapDownload.Total - snapDownload.Downloaded
percent = int((snapDownload.Downloaded*100)/snapDownload.Total) / 2
}

logstr := "["

for i := 1; i < 50; i++ {
if i < percent {
logstr += "#"
} else {
logstr += "."
}
}

logstr += "]"

fmt.Println("Download:", logstr, common.ByteCount(snapDownload.Downloaded), "/", common.ByteCount(snapDownload.Total))
downloadTimeLeft := util.CalculateTime(remainingBytes, snapDownload.DownloadRate)

fmt.Println("Time left:", downloadTimeLeft)
}

return nil
}
21 changes: 21 additions & 0 deletions cmd/diag/flags/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package flags

import "github.com/urfave/cli/v2"

var (
DebugURLFlag = cli.StringFlag{
Name: "debug.addr",
Aliases: []string{"da"},
Usage: "URL to the debug endpoint",
Required: false,
Value: "localhost:6060",
}

OutputFlag = cli.StringFlag{
Name: "output",
Aliases: []string{"o"},
Usage: "Output format [text|json]",
Required: false,
Value: "text",
}
)
105 changes: 105 additions & 0 deletions cmd/diag/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package main

import (
"context"
"fmt"
"os"
"os/signal"
"path/filepath"
"syscall"

"github.com/ledgerwatch/log/v3"
"github.com/urfave/cli/v2"

"github.com/ledgerwatch/erigon/cmd/diag/downloader"
"github.com/ledgerwatch/erigon/cmd/diag/stages"
"github.com/ledgerwatch/erigon/cmd/snapshots/sync"
"github.com/ledgerwatch/erigon/cmd/utils"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/turbo/logging"
)

func main() {
logging.LogVerbosityFlag.Value = log.LvlError.String()
logging.LogConsoleVerbosityFlag.Value = log.LvlError.String()

app := cli.NewApp()
app.Name = "diagnostics"
app.Version = params.VersionWithCommit(params.GitCommit)
app.EnableBashCompletion = true

app.Commands = []*cli.Command{
&downloader.Command,
&stages.Command,
}

app.Flags = []cli.Flag{}

app.HelpName = `Erigon Diagnostics`
app.Usage = "Display diagnostic output for a running erigon node"
app.UsageText = `diag [command] [flags]`

app.Action = func(context *cli.Context) error {
var goodNames []string
for _, c := range app.VisibleCommands() {
goodNames = append(goodNames, c.Name)
}
_, _ = fmt.Fprintf(os.Stderr, "Command '%s' not found. Available commands: %s\n", context.Args().First(), goodNames)
cli.ShowAppHelpAndExit(context, 1)

return nil
}

for _, command := range app.Commands {
command.Before = func(ctx *cli.Context) error {
logger, err := setupLogger(ctx)

if err != nil {
return err
}

var cancel context.CancelFunc

ctx.Context, cancel = context.WithCancel(sync.WithLogger(ctx.Context, logger))

go handleTerminationSignals(cancel, logger)

return nil
}
}

if err := app.Run(os.Args); err != nil {
_, _ = fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

func setupLogger(ctx *cli.Context) (log.Logger, error) {
dataDir := ctx.String(utils.DataDirFlag.Name)

if len(dataDir) > 0 {
logsDir := filepath.Join(dataDir, "logs")

if err := os.MkdirAll(logsDir, 0755); err != nil {
return nil, err
}
}

logger := logging.SetupLoggerCtx("diagnostics-"+ctx.Command.Name, ctx, log.LvlError, log.LvlInfo, false)

return logger, nil
}

func handleTerminationSignals(stopFunc func(), logger log.Logger) {
signalCh := make(chan os.Signal, 1)
signal.Notify(signalCh, syscall.SIGTERM, syscall.SIGINT)

switch s := <-signalCh; s {
case syscall.SIGTERM:
logger.Info("Stopping")
stopFunc()
case syscall.SIGINT:
logger.Info("Terminating")
os.Exit(-int(syscall.SIGINT))
}
}
66 changes: 66 additions & 0 deletions cmd/diag/stages/stages.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package stages

import (
"encoding/json"
"fmt"

"github.com/ledgerwatch/erigon-lib/diagnostics"
"github.com/ledgerwatch/erigon/cmd/diag/flags"
"github.com/ledgerwatch/erigon/cmd/diag/util"
"github.com/urfave/cli/v2"
)

var Command = cli.Command{
Name: "stages",
Aliases: []string{"st"},
ArgsUsage: "",
Subcommands: []*cli.Command{
{
Name: "current",
Aliases: []string{"c"},
Action: printCurentStage,
Usage: "print current stage",
ArgsUsage: "",
Flags: []cli.Flag{
&flags.DebugURLFlag,
&flags.OutputFlag,
},
},
},
Description: ``,
}

func printCurentStage(cliCtx *cli.Context) error {
var data diagnostics.SyncStatistics
url := "http://" + cliCtx.String(flags.DebugURLFlag.Name) + "/debug/snapshot-sync"

err := util.MakeHttpGetCall(cliCtx.Context, url, &data)
if err != nil {
return err
}

switch cliCtx.String(flags.OutputFlag.Name) {
case "json":
bytes, err := json.Marshal(data.SyncStages.StagesList)
if err != nil {
return err
}

fmt.Println(string(bytes))

case "text":
fmt.Println("-------------------Stages-------------------")

for idx, stage := range data.SyncStages.StagesList {
if idx == int(data.SyncStages.CurrentStage) {
fmt.Println("[" + stage + "]" + " - Running")
} else if idx < int(data.SyncStages.CurrentStage) {
fmt.Println("[" + stage + "]" + " - Completed")
} else {
fmt.Println("[" + stage + "]" + " - Queued")
}
}
}

return nil
}
51 changes: 51 additions & 0 deletions cmd/diag/util/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package util

import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)

func MakeHttpGetCall(ctx context.Context, url string, data interface{}) error {
var client = &http.Client{
Timeout: time.Second * 20,
}

req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return err
}

resp, err := client.Do(req)
if err != nil {
return err
}

defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}

err = json.Unmarshal(body, &data)
if err != nil {
return err
}

return nil
}

func CalculateTime(amountLeft, rate uint64) string {
if rate == 0 {
return "999hrs:99m"
}
timeLeftInSeconds := amountLeft / rate

hours := timeLeftInSeconds / 3600
minutes := (timeLeftInSeconds / 60) % 60

return fmt.Sprintf("%dhrs:%dm", hours, minutes)
}