From 003cf6f212f92efafc9ef5fe15bb30eed13171a0 Mon Sep 17 00:00:00 2001 From: Makis Christou Date: Thu, 25 Jul 2024 13:03:46 +0300 Subject: [PATCH 01/10] Replace slog.Level with slog.LevelVar --- cmd/disco/utils.go | 9 +++++++-- cmd/thor/utils.go | 8 ++++++-- log/handler.go | 10 ++++++---- log/logger_test.go | 8 ++++++-- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/cmd/disco/utils.go b/cmd/disco/utils.go index 53a2a2e5f..c73d357e4 100644 --- a/cmd/disco/utils.go +++ b/cmd/disco/utils.go @@ -9,6 +9,7 @@ import ( "crypto/ecdsa" "fmt" "io" + "log/slog" "os" "os/user" "path/filepath" @@ -19,15 +20,19 @@ import ( "github.com/vechain/thor/v2/log" ) -func initLogger(lvl int) { +func initLogger(lvl int) *slog.LevelVar { logLevel := log.FromLegacyLevel(lvl) + var level slog.LevelVar + level.Set(logLevel) output := io.Writer(os.Stdout) useColor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" - handler := log.NewTerminalHandlerWithLevel(output, logLevel, useColor) + handler := log.NewTerminalHandlerWithLevel(output, &level, useColor) log.SetDefault(log.NewLogger(handler)) ethlog.Root().SetHandler(ðLogger{ logger: log.WithContext("pkg", "geth"), }) + + return &level } type ethLogger struct { diff --git a/cmd/thor/utils.go b/cmd/thor/utils.go index 620e9c16a..14296844e 100644 --- a/cmd/thor/utils.go +++ b/cmd/thor/utils.go @@ -60,21 +60,25 @@ import ( var devNetGenesisID = genesis.NewDevnet().ID() -func initLogger(lvl int, jsonLogs bool) { +func initLogger(lvl int, jsonLogs bool) *slog.LevelVar { logLevel := log.FromLegacyLevel(lvl) output := io.Writer(os.Stdout) + var level slog.LevelVar + level.Set(logLevel) var handler slog.Handler if jsonLogs { handler = log.JSONHandlerWithLevel(output, logLevel) } else { useColor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" - handler = log.NewTerminalHandlerWithLevel(output, logLevel, useColor) + handler = log.NewTerminalHandlerWithLevel(output, &level, useColor) } log.SetDefault(log.NewLogger(handler)) ethlog.Root().SetHandler(ethlog.LvlFilterHandler(ethlog.LvlWarn, ðLogger{ logger: log.WithContext("pkg", "geth"), })) + + return &level } type ethLogger struct { diff --git a/log/handler.go b/log/handler.go index 17ad6bc89..53125ae48 100644 --- a/log/handler.go +++ b/log/handler.go @@ -55,7 +55,7 @@ func (h *discardHandler) WithAttrs(attrs []slog.Attr) slog.Handler { type TerminalHandler struct { mu sync.Mutex wr io.Writer - lvl slog.Level + lvl *slog.LevelVar useColor bool attrs []slog.Attr // fieldPadding is a map with maximum field value lengths seen until now @@ -75,12 +75,14 @@ type TerminalHandler struct { // // [DBUG] [May 16 20:58:45] remove route ns=haproxy addr=127.0.0.1:50002 func NewTerminalHandler(wr io.Writer, useColor bool) *TerminalHandler { - return NewTerminalHandlerWithLevel(wr, levelMaxVerbosity, useColor) + var level slog.LevelVar + level.Set(levelMaxVerbosity) + return NewTerminalHandlerWithLevel(wr, &level, useColor) } // NewTerminalHandlerWithLevel returns the same handler as NewTerminalHandler but only outputs // records which are less than or equal to the specified verbosity level. -func NewTerminalHandlerWithLevel(wr io.Writer, lvl slog.Level, useColor bool) *TerminalHandler { +func NewTerminalHandlerWithLevel(wr io.Writer, lvl *slog.LevelVar, useColor bool) *TerminalHandler { return &TerminalHandler{ wr: wr, lvl: lvl, @@ -99,7 +101,7 @@ func (h *TerminalHandler) Handle(_ context.Context, r slog.Record) error { } func (h *TerminalHandler) Enabled(_ context.Context, level slog.Level) bool { - return level >= h.lvl + return level.Level() >= h.lvl.Level() } func (h *TerminalHandler) WithGroup(name string) slog.Handler { diff --git a/log/logger_test.go b/log/logger_test.go index 5310362dc..374d729af 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -33,7 +33,9 @@ import ( func TestTerminalHandlerWithAttrs(t *testing.T) { out := new(bytes.Buffer) - handler := NewTerminalHandlerWithLevel(out, LevelTrace, false).WithAttrs([]slog.Attr{slog.String("baz", "bat")}) + var level slog.LevelVar + level.Set(LevelTrace) + handler := NewTerminalHandlerWithLevel(out, &level, false).WithAttrs([]slog.Attr{slog.String("baz", "bat")}) logger := NewLogger(handler) logger.Trace("a message", "foo", "bar") have := out.String() @@ -128,7 +130,9 @@ func TestLoggerOutput(t *testing.T) { ) out := new(bytes.Buffer) - handler := NewTerminalHandlerWithLevel(out, LevelInfo, false) + var level slog.LevelVar + level.Set(LevelInfo) + handler := NewTerminalHandlerWithLevel(out, &level, false) NewLogger(handler).Info("This is a message", "foo", int16(123), "bytes", bb, From f8fc94117c573fa60d1656666b4a0a4382fe93a5 Mon Sep 17 00:00:00 2001 From: Makis Christou Date: Thu, 25 Jul 2024 13:11:30 +0300 Subject: [PATCH 02/10] Add admin server to handle verbosity changes --- admin/admin.go | 43 +++++++++++++++++++++++++++++++++++++++++++ cmd/thor/flags.go | 9 +++++++++ cmd/thor/main.go | 33 +++++++++++++++++++++++++++++---- cmd/thor/utils.go | 42 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 admin/admin.go diff --git a/admin/admin.go b/admin/admin.go new file mode 100644 index 000000000..c2a8ca0b2 --- /dev/null +++ b/admin/admin.go @@ -0,0 +1,43 @@ +// Copyright (c) 2024 The VeChainThor developers + +// Distributed under the GNU Lesser General Public License v3.0 software license, see the accompanying +// file LICENSE or + +package admin + +import ( + "fmt" + "log/slog" + "net/http" + + "github.com/gorilla/handlers" + "github.com/gorilla/mux" + "github.com/vechain/thor/v2/log" +) + +func HTTPHandler(logLevel *slog.LevelVar) http.Handler { + router := mux.NewRouter() + router.PathPrefix("/admin").Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + verbosity := r.URL.Query().Get("verbosity") + switch verbosity { + case "debug": + logLevel.Set(log.LevelDebug) + case "info": + logLevel.Set(log.LevelInfo) + case "warn": + logLevel.Set(log.LevelWarn) + case "error": + logLevel.Set(log.LevelError) + case "trace": + logLevel.Set(log.LevelTrace) + case "crit": + logLevel.Set(log.LevelCrit) + default: + http.Error(w, "Invalid verbosity level", http.StatusBadRequest) + return + } + + fmt.Fprintln(w, "Verbosity changed to ", verbosity) + })) + return handlers.CompressHandler(router) +} diff --git a/cmd/thor/flags.go b/cmd/thor/flags.go index 34eaa3325..f5b158815 100644 --- a/cmd/thor/flags.go +++ b/cmd/thor/flags.go @@ -150,6 +150,15 @@ var ( Value: "localhost:2112", Usage: "metrics service listening address", } + enableAdminFlag = cli.BoolFlag{ + Name: "enable-admin", + Usage: "enables admin service", + } + adminAddrFlag = cli.StringFlag{ + Name: "admin-addr", + Value: "localhost:2113", + Usage: "admin service listening address", + } // solo mode only flags onDemandFlag = cli.BoolFlag{ diff --git a/cmd/thor/main.go b/cmd/thor/main.go index 8c2f00d19..37db92a78 100644 --- a/cmd/thor/main.go +++ b/cmd/thor/main.go @@ -93,6 +93,8 @@ func main() { disablePrunerFlag, enableMetricsFlag, metricsAddrFlag, + adminAddrFlag, + enableAdminFlag, }, Action: defaultAction, Commands: []cli.Command{ @@ -125,6 +127,8 @@ func main() { disablePrunerFlag, enableMetricsFlag, metricsAddrFlag, + adminAddrFlag, + enableAdminFlag, }, Action: soloAction, }, @@ -156,7 +160,7 @@ func defaultAction(ctx *cli.Context) error { if err != nil { return errors.Wrap(err, "parse verbosity flag") } - initLogger(lvl, ctx.Bool(jsonLogsFlag.Name)) + logLevel := initLogger(lvl, ctx.Bool(jsonLogsFlag.Name)) // enable metrics as soon as possible metricsURL := "" @@ -170,6 +174,16 @@ func defaultAction(ctx *cli.Context) error { defer func() { log.Info("stopping metrics server..."); close() }() } + adminURL := "" + if ctx.Bool(enableAdminFlag.Name) { + url, close, err := startAdminServer(ctx.String(adminAddrFlag.Name), logLevel) + if err != nil { + return fmt.Errorf("unable to start admin server - %w", err) + } + adminURL = url + defer func() { log.Info("stopping admin server..."); close() }() + } + gene, forkConfig, err := selectGenesis(ctx) if err != nil { return err @@ -251,7 +265,7 @@ func defaultAction(ctx *cli.Context) error { } defer func() { log.Info("stopping API server..."); srvCloser() }() - printStartupMessage2(gene, apiURL, p2pCommunicator.Enode(), metricsURL) + printStartupMessage2(gene, apiURL, p2pCommunicator.Enode(), metricsURL, adminURL) if err := p2pCommunicator.Start(); err != nil { return err @@ -283,7 +297,8 @@ func soloAction(ctx *cli.Context) error { if err != nil { return errors.Wrap(err, "parse verbosity flag") } - initLogger(lvl, ctx.Bool(jsonLogsFlag.Name)) + + logLevel := initLogger(lvl, ctx.Bool(jsonLogsFlag.Name)) // enable metrics as soon as possible metricsURL := "" @@ -297,6 +312,16 @@ func soloAction(ctx *cli.Context) error { defer func() { log.Info("stopping metrics server..."); close() }() } + adminURL := "" + if ctx.Bool(enableAdminFlag.Name) { + url, close, err := startAdminServer(ctx.String(adminAddrFlag.Name), logLevel) + if err != nil { + return fmt.Errorf("unable to start admin server - %w", err) + } + adminURL = url + defer func() { log.Info("stopping admin server..."); close() }() + } + var ( gene *genesis.Genesis forkConfig thor.ForkConfig @@ -397,7 +422,7 @@ func soloAction(ctx *cli.Context) error { return errors.New("block-interval cannot be zero") } - printSoloStartupMessage(gene, repo, instanceDir, apiURL, forkConfig, metricsURL) + printSoloStartupMessage(gene, repo, instanceDir, apiURL, forkConfig, metricsURL, adminURL) optimizer := optimizer.New(mainDB, repo, !ctx.Bool(disablePrunerFlag.Name)) defer func() { log.Info("stopping optimizer..."); optimizer.Stop() }() diff --git a/cmd/thor/utils.go b/cmd/thor/utils.go index 14296844e..f91ecb1e8 100644 --- a/cmd/thor/utils.go +++ b/cmd/thor/utils.go @@ -39,6 +39,7 @@ import ( "github.com/mattn/go-isatty" "github.com/mattn/go-tty" "github.com/pkg/errors" + "github.com/vechain/thor/v2/admin" "github.com/vechain/thor/v2/api/doc" "github.com/vechain/thor/v2/chain" "github.com/vechain/thor/v2/cmd/thor/node" @@ -591,6 +592,27 @@ func startMetricsServer(addr string) (string, func(), error) { }, nil } +func startAdminServer(addr string, logLevel *slog.LevelVar) (string, func(), error) { + listener, err := net.Listen("tcp", addr) + if err != nil { + return "", nil, errors.Wrapf(err, "listen admin API addr [%v]", addr) + } + + router := mux.NewRouter() + router.PathPrefix("/admin").Handler(admin.HTTPHandler(logLevel)) + handler := handlers.CompressHandler(router) + + srv := &http.Server{Handler: handler, ReadHeaderTimeout: time.Second, ReadTimeout: 5 * time.Second} + var goes co.Goes + goes.Go(func() { + srv.Serve(listener) + }) + return "http://" + listener.Addr().String() + "/admin", func() { + srv.Close() + goes.Wait() + }, nil +} + func printStartupMessage1( gene *genesis.Genesis, repo *chain.Repository, @@ -642,8 +664,9 @@ func printStartupMessage2( apiURL string, nodeID string, metricsURL string, + adminURL string, ) { - fmt.Printf(`%v API portal [ %v ]%v%v`, + fmt.Printf(`%v API portal [ %v ]%v%v%v`, func() string { // node ID if nodeID == "" { return "" @@ -663,6 +686,15 @@ func printStartupMessage2( metricsURL) } }(), + func() string { // admin URL + if adminURL == "" { + return "" + } else { + return fmt.Sprintf(` + Admin [ %v ]`, + adminURL) + } + }(), func() string { // print default dev net's dev accounts info if gene.ID() == devNetGenesisID { @@ -685,6 +717,7 @@ func printSoloStartupMessage( apiURL string, forkConfig thor.ForkConfig, metricsURL string, + adminURL string, ) { bestBlock := repo.BestBlockSummary() @@ -695,6 +728,7 @@ func printSoloStartupMessage( Data dir [ %v ] API portal [ %v ] Metrics [ %v ] + Admin [ %v ] `, common.MakeName("Thor solo", fullVersion()), gene.ID(), gene.Name(), @@ -708,6 +742,12 @@ func printSoloStartupMessage( } return metricsURL }(), + func() string { + if adminURL == "" { + return "Disabled" + } + return adminURL + }(), ) if gene.ID() == devNetGenesisID { From 2675df338766eda873a21e9c5990e2139da19afd Mon Sep 17 00:00:00 2001 From: Makis Christou Date: Thu, 25 Jul 2024 13:37:56 +0300 Subject: [PATCH 03/10] Update Json logger with LevelVar --- cmd/thor/utils.go | 2 +- log/handler.go | 12 +++++++----- log/logger_test.go | 6 +++++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/cmd/thor/utils.go b/cmd/thor/utils.go index f91ecb1e8..cb810364d 100644 --- a/cmd/thor/utils.go +++ b/cmd/thor/utils.go @@ -69,7 +69,7 @@ func initLogger(lvl int, jsonLogs bool) *slog.LevelVar { var handler slog.Handler if jsonLogs { - handler = log.JSONHandlerWithLevel(output, logLevel) + handler = log.JSONHandlerWithLevel(output, &level) } else { useColor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" handler = log.NewTerminalHandlerWithLevel(output, &level, useColor) diff --git a/log/handler.go b/log/handler.go index 53125ae48..2cc150fa4 100644 --- a/log/handler.go +++ b/log/handler.go @@ -125,20 +125,22 @@ func (t *TerminalHandler) ResetFieldPadding() { t.mu.Unlock() } -type leveler struct{ minLevel slog.Level } +type leveler struct{ minLevel *slog.LevelVar } func (l *leveler) Level() slog.Level { - return l.minLevel + return l.minLevel.Level() } // JSONHandler returns a handler which prints records in JSON format. func JSONHandler(wr io.Writer) slog.Handler { - return JSONHandlerWithLevel(wr, levelMaxVerbosity) + var level slog.LevelVar + level.Set(levelMaxVerbosity) + return JSONHandlerWithLevel(wr, &level) } // JSONHandlerWithLevel returns a handler which prints records in JSON format that are less than or equal to // the specified verbosity level. -func JSONHandlerWithLevel(wr io.Writer, level slog.Level) slog.Handler { +func JSONHandlerWithLevel(wr io.Writer, level *slog.LevelVar) slog.Handler { return slog.NewJSONHandler(wr, &slog.HandlerOptions{ ReplaceAttr: builtinReplaceJSON, Level: &leveler{level}, @@ -157,7 +159,7 @@ func LogfmtHandler(wr io.Writer) slog.Handler { // LogfmtHandlerWithLevel returns the same handler as LogfmtHandler but it only outputs // records which are less than or equal to the specified verbosity level. -func LogfmtHandlerWithLevel(wr io.Writer, level slog.Level) slog.Handler { +func LogfmtHandlerWithLevel(wr io.Writer, level *slog.LevelVar) slog.Handler { return slog.NewTextHandler(wr, &slog.HandlerOptions{ ReplaceAttr: builtinReplaceLogfmt, Level: &leveler{level}, diff --git a/log/logger_test.go b/log/logger_test.go index 374d729af..4d82c710e 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -59,7 +59,11 @@ func TestJSONHandler(t *testing.T) { } out.Reset() - handler = JSONHandlerWithLevel(out, slog.LevelInfo) + + var level slog.LevelVar + level.Set(LevelInfo) + + handler = JSONHandlerWithLevel(out, &level) logger = slog.New(handler) logger.Debug("hi there") if len(out.String()) != 0 { From d845b7a7398d03ca92633cbbd2e5219918fe510e Mon Sep 17 00:00:00 2001 From: Makis Christou Date: Mon, 29 Jul 2024 09:14:49 +0300 Subject: [PATCH 04/10] Add a GET and POST route --- admin/admin.go | 57 +++++++++++++++++++++++++++++++++++++++++++------- docs/usage.md | 2 ++ 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/admin/admin.go b/admin/admin.go index c2a8ca0b2..8c715818a 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -6,6 +6,7 @@ package admin import ( + "encoding/json" "fmt" "log/slog" "net/http" @@ -15,11 +16,35 @@ import ( "github.com/vechain/thor/v2/log" ) -func HTTPHandler(logLevel *slog.LevelVar) http.Handler { - router := mux.NewRouter() - router.PathPrefix("/admin").Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - verbosity := r.URL.Query().Get("verbosity") - switch verbosity { +type logLevelRequest struct { + Level string `json:"level"` +} + +type logLevelResponse struct { + CurrentLevel string `json:"currentLevel"` +} + +func getLogLevelHandler(logLevel *slog.LevelVar) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + response := logLevelResponse{ + CurrentLevel: logLevel.Level().String(), + } + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(response); err != nil { + http.Error(w, "Failed to encode response", http.StatusInternalServerError) + } + } +} + +func postLogLevelHandler(logLevel *slog.LevelVar) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req logLevelRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, "Invalid request body", http.StatusBadRequest) + return + } + + switch req.Level { case "debug": logLevel.Set(log.LevelDebug) case "info": @@ -37,7 +62,25 @@ func HTTPHandler(logLevel *slog.LevelVar) http.Handler { return } - fmt.Fprintln(w, "Verbosity changed to ", verbosity) - })) + fmt.Fprintln(w, "Verbosity changed to ", req.Level) + } +} + +func logLevelHandler(logLevel *slog.LevelVar) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + getLogLevelHandler(logLevel).ServeHTTP(w, r) + case http.MethodPost: + postLogLevelHandler(logLevel).ServeHTTP(w, r) + default: + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } + } +} + +func HTTPHandler(logLevel *slog.LevelVar) http.Handler { + router := mux.NewRouter() + router.HandleFunc("/admin/loglevel", logLevelHandler(logLevel)) return handlers.CompressHandler(router) } diff --git a/docs/usage.md b/docs/usage.md index 552bc62b9..b63b16ad3 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -182,6 +182,8 @@ bin/thor -h | `--disable-pruner` | Disable state pruner to keep all history | | `--enable-metrics` | Enables the metrics server | | `--metrics-addr` | Metrics service listening address | +| `--enable-admin` | Enables the admin server | +| `--admin-addr` | Admin service listening address | | `--help, -h` | Show help | | `--version, -v` | Print the version | From 68f93d2c3f4faf08ccdbc612768f0d777ccd68b0 Mon Sep 17 00:00:00 2001 From: Makis Christou Date: Mon, 29 Jul 2024 11:02:28 +0300 Subject: [PATCH 05/10] Fix Metrics and Admin docs --- docs/hosting-a-node.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/hosting-a-node.md b/docs/hosting-a-node.md index ac9e8fa73..aaf57ab0b 100644 --- a/docs/hosting-a-node.md +++ b/docs/hosting-a-node.md @@ -101,11 +101,29 @@ _As of 22nd April 2024, a full node without logs uses **~100 GB** of disk space. Telemetry plays a critical role in monitoring and managing blockchain nodes efficiently. Below is an overview of how metrics is integrated and utilized within our node systems. -Metrics is enabled in nodes by default. It's possible to disable it by setting `--enable-metrics=false`. -By default, a [prometheus](https://prometheus.io/docs/introduction/overview/) server is available at `localhost:2112/metrics` with the metrics. +Metrics is not enabled in nodes by default. It's possible to enable it by setting `--enable-metrics`. +Once enabled, a [prometheus](https://prometheus.io/docs/introduction/overview/) server is available at `localhost:2112/metrics` with the metrics. ```shell curl localhost:2112/metrics ``` -Instrumentation is in a beta phase at this stage. You can read more about the metric types [here](https://prometheus.io/docs/concepts/metric_types/). \ No newline at end of file +Instrumentation is in a beta phase at this stage. You can read more about the metric types [here](https://prometheus.io/docs/concepts/metric_types/). + +### Admin + +Admin is used to allow privilidged actions to the node by the administrator. Currently is supports changing the logger's verbosity at runtime. + +Admin is not enabled in nodes by default. It's possible to enable it by setting `--enable-admin`. Once enabled, an Admin server is available at `localhost:2113/admin` with the following capabilities: + +Retrieve the current log level via a GET request to /admin/loglevel. + +```shell +curl http://localhost:2113/admin/loglevel +``` + +Change the log level via a POST request to /admin/loglevel. + +```shell +curl -X POST -H "Content-Type: application/json" -d '{"level": "trace"}' http://localhost:2113/admin/loglevel +``` \ No newline at end of file From 624a7e25056048075f9414b36ce4b11ec4d45ef4 Mon Sep 17 00:00:00 2001 From: Makis Christou Date: Tue, 30 Jul 2024 12:34:16 +0300 Subject: [PATCH 06/10] Fix grammar --- docs/hosting-a-node.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/hosting-a-node.md b/docs/hosting-a-node.md index aaf57ab0b..f64f961bb 100644 --- a/docs/hosting-a-node.md +++ b/docs/hosting-a-node.md @@ -101,7 +101,7 @@ _As of 22nd April 2024, a full node without logs uses **~100 GB** of disk space. Telemetry plays a critical role in monitoring and managing blockchain nodes efficiently. Below is an overview of how metrics is integrated and utilized within our node systems. -Metrics is not enabled in nodes by default. It's possible to enable it by setting `--enable-metrics`. +Metrics are not enabled in nodes by default. It's possible to enable them by setting `--enable-metrics`. Once enabled, a [prometheus](https://prometheus.io/docs/introduction/overview/) server is available at `localhost:2112/metrics` with the metrics. ```shell From a188d6332292f0dd65a9702b99ec5b7789e8e72a Mon Sep 17 00:00:00 2001 From: Makis Christou Date: Mon, 12 Aug 2024 11:33:33 +0300 Subject: [PATCH 07/10] Always use JSON responses --- admin/admin.go | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/admin/admin.go b/admin/admin.go index 8c715818a..17e873293 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -7,7 +7,6 @@ package admin import ( "encoding/json" - "fmt" "log/slog" "net/http" @@ -24,14 +23,27 @@ type logLevelResponse struct { CurrentLevel string `json:"currentLevel"` } +type errorResponse struct { + ErrorCode int `json:"errorCode"` + ErrorMessage string `json:"errorMessage"` +} + +func writeError(w http.ResponseWriter, errCode int, errMsg string) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(errCode) + json.NewEncoder(w).Encode(errorResponse{ + ErrorCode: errCode, + ErrorMessage: errMsg, + }) +} + func getLogLevelHandler(logLevel *slog.LevelVar) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { response := logLevelResponse{ CurrentLevel: logLevel.Level().String(), } - w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(response); err != nil { - http.Error(w, "Failed to encode response", http.StatusInternalServerError) + writeError(w, http.StatusInternalServerError, "Failed to encode response") } } } @@ -40,7 +52,7 @@ func postLogLevelHandler(logLevel *slog.LevelVar) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req logLevelRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - http.Error(w, "Invalid request body", http.StatusBadRequest) + writeError(w, http.StatusBadRequest, "Invalid request body") return } @@ -58,11 +70,16 @@ func postLogLevelHandler(logLevel *slog.LevelVar) http.HandlerFunc { case "crit": logLevel.Set(log.LevelCrit) default: - http.Error(w, "Invalid verbosity level", http.StatusBadRequest) + writeError(w, http.StatusBadRequest, "Invalid verbosity level") return } - fmt.Fprintln(w, "Verbosity changed to ", req.Level) + w.Header().Set("Content-Type", "application/json") + response := logLevelResponse{ + CurrentLevel: logLevel.Level().String(), + } + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(response) } } @@ -74,7 +91,7 @@ func logLevelHandler(logLevel *slog.LevelVar) http.HandlerFunc { case http.MethodPost: postLogLevelHandler(logLevel).ServeHTTP(w, r) default: - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + writeError(w, http.StatusMethodNotAllowed, "method not allowed") } } } From de824bab1ae79e84939a936399300b03743ef56b Mon Sep 17 00:00:00 2001 From: Makis Christou Date: Mon, 12 Aug 2024 11:38:09 +0300 Subject: [PATCH 08/10] Revert Metrics docs changes --- docs/hosting-a-node.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/hosting-a-node.md b/docs/hosting-a-node.md index f64f961bb..d05e774af 100644 --- a/docs/hosting-a-node.md +++ b/docs/hosting-a-node.md @@ -101,8 +101,8 @@ _As of 22nd April 2024, a full node without logs uses **~100 GB** of disk space. Telemetry plays a critical role in monitoring and managing blockchain nodes efficiently. Below is an overview of how metrics is integrated and utilized within our node systems. -Metrics are not enabled in nodes by default. It's possible to enable them by setting `--enable-metrics`. -Once enabled, a [prometheus](https://prometheus.io/docs/introduction/overview/) server is available at `localhost:2112/metrics` with the metrics. +Metrics is enabled in nodes by default. It's possible to disable it by setting `--enable-metrics=false`. +By default, a [prometheus](https://prometheus.io/docs/introduction/overview/) server is available at `localhost:2112/metrics` with the metrics. ```shell curl localhost:2112/metrics From 8edfdd1f86482b3b6a27b3a722fce0df1bd27b56 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 14 Aug 2024 14:45:46 +0100 Subject: [PATCH 09/10] Update docs/hosting-a-node.md Co-authored-by: Darren Kelly <107671032+darrenvechain@users.noreply.github.com> --- docs/hosting-a-node.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/hosting-a-node.md b/docs/hosting-a-node.md index d05e774af..8defd58dc 100644 --- a/docs/hosting-a-node.md +++ b/docs/hosting-a-node.md @@ -112,7 +112,7 @@ Instrumentation is in a beta phase at this stage. You can read more about the me ### Admin -Admin is used to allow privilidged actions to the node by the administrator. Currently is supports changing the logger's verbosity at runtime. +Admin is used to allow privileged actions to the node by the administrator. Currently it supports changing the logger's verbosity at runtime. Admin is not enabled in nodes by default. It's possible to enable it by setting `--enable-admin`. Once enabled, an Admin server is available at `localhost:2113/admin` with the following capabilities: From 1aae362bd5f2800bea93c00cc3b45181ee3fcd15 Mon Sep 17 00:00:00 2001 From: Makis Christou Date: Mon, 19 Aug 2024 14:03:29 +0300 Subject: [PATCH 10/10] Fix linter errors --- cmd/thor/flags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/thor/flags.go b/cmd/thor/flags.go index be76850e1..b93167e21 100644 --- a/cmd/thor/flags.go +++ b/cmd/thor/flags.go @@ -159,7 +159,7 @@ var ( Name: "admin-addr", Value: "localhost:2113", Usage: "admin service listening address", - + } txPoolLimitPerAccountFlag = cli.Uint64Flag{ Name: "txpool-limit-per-account", Value: 16,