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

WP - dvovk/diagnostics downloader print #10000

Merged
merged 17 commits into from
Apr 22, 2024
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)
}
Loading