Skip to content

Commit

Permalink
Add runtime log level control via HTTP (Fix #1181)
Browse files Browse the repository at this point in the history
Introduce HTTP server to dynamically adjust log levels at runtime.
Refactor log level handling to use a new LogLevel struct.
Update tests and documentation to reflect these changes.

Enhances logging flexibility and improves runtime configurability.
  • Loading branch information
derrix060 committed Oct 31, 2024
1 parent 8593fd1 commit 41cf496
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 96 deletions.
30 changes: 27 additions & 3 deletions cmd/juno/juno.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"math"
"math/big"
"net/http"
"os"
"os/signal"
"path/filepath"
Expand Down Expand Up @@ -199,11 +200,35 @@ func main() {
return err
}

n, err := node.New(config, Version)
logLevel := utils.NewLogLevel(utils.INFO)
err = logLevel.Set(config.LogLevel)
if err != nil {
return err
}

n, err := node.New(config, Version, logLevel)
if err != nil {
return err
}

// Create a HTTP server to control the log level.
http.HandleFunc("/log", func(w http.ResponseWriter, r *http.Request) {
utils.HTTPLogSettings(w, r, logLevel)
})
go func() {
server := &http.Server{
Addr: ":6789",
Handler: nil,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 15 * time.Second,
}
err := server.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
fmt.Printf("HTTP server ListenAndServe: %v", err)
}
}()

n.Run(cmd.Context())
return nil
})
Expand Down Expand Up @@ -304,13 +329,12 @@ func NewCmd(config *node.Config, run func(*cobra.Command, []string) error) *cobr

// For testing purposes, these variables cannot be declared outside the function because Cobra
// may mutate their values.
defaultLogLevel := utils.INFO
defaultNetwork := utils.Mainnet
defaultMaxVMs := 3 * runtime.GOMAXPROCS(0)
defaultCNUnverifiableRange := []int{} // Uint64Slice is not supported in Flags()

junoCmd.Flags().StringVar(&cfgFile, configF, defaultConfig, configFlagUsage)
junoCmd.Flags().Var(&defaultLogLevel, logLevelF, logLevelFlagUsage)
junoCmd.Flags().String(logLevelF, utils.INFO.String(), logLevelFlagUsage)
junoCmd.Flags().Bool(httpF, defaultHTTP, httpUsage)
junoCmd.Flags().String(httpHostF, defaulHost, httpHostUsage)
junoCmd.Flags().Uint16(httpPortF, defaultHTTPPort, httpPortUsage)
Expand Down
18 changes: 9 additions & 9 deletions cmd/juno/juno_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func TestConfigPrecedence(t *testing.T) {
// tested for sanity. These tests are not intended to perform semantics
// checks on the config, those will be checked by the node implementation.
defaultHost := "localhost"
defaultLogLevel := utils.INFO
defaultLogLevel := "info"
defaultHTTP := false
defaultHTTPPort := uint16(6060)
defaultWS := false
Expand Down Expand Up @@ -83,7 +83,7 @@ func TestConfigPrecedence(t *testing.T) {
"--cn-core-contract-address", "0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4",
},
expectedConfig: &node.Config{
LogLevel: utils.DEBUG,
LogLevel: "debug",
HTTP: defaultHTTP,
HTTPHost: "0.0.0.0",
HTTPPort: 4576,
Expand Down Expand Up @@ -128,7 +128,7 @@ cn-core-contract-address: 0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4
cn-unverifiable-range: [0,10]
`,
expectedConfig: &node.Config{
LogLevel: utils.DEBUG,
LogLevel: "debug",
HTTP: defaultHTTP,
HTTPHost: "0.0.0.0",
HTTPPort: 4576,
Expand Down Expand Up @@ -268,7 +268,7 @@ network: sepolia
pprof: true
`,
expectedConfig: &node.Config{
LogLevel: utils.DEBUG,
LogLevel: "debug",
HTTP: defaultHTTP,
HTTPHost: "0.0.0.0",
HTTPPort: 4576,
Expand Down Expand Up @@ -304,7 +304,7 @@ http-host: 0.0.0.0
http-port: 4576
`,
expectedConfig: &node.Config{
LogLevel: utils.DEBUG,
LogLevel: "debug",
HTTP: defaultHTTP,
HTTPHost: "0.0.0.0",
HTTPPort: 4576,
Expand Down Expand Up @@ -339,7 +339,7 @@ http-port: 4576
"--db-path", "/home/.juno", "--network", "sepolia-integration", "--pprof", "--db-cache-size", "1024",
},
expectedConfig: &node.Config{
LogLevel: utils.DEBUG,
LogLevel: "debug",
HTTP: defaultHTTP,
HTTPHost: "0.0.0.0",
HTTPPort: 4576,
Expand Down Expand Up @@ -374,7 +374,7 @@ http-port: 4576
"--network", "sepolia",
},
expectedConfig: &node.Config{
LogLevel: utils.DEBUG,
LogLevel: "debug",
HTTP: defaultHTTP,
HTTPHost: "0.0.0.0",
HTTPPort: 4576,
Expand Down Expand Up @@ -433,7 +433,7 @@ db-cache-size: 1024
"--db-cache-size", "9",
},
expectedConfig: &node.Config{
LogLevel: utils.ERROR,
LogLevel: "error",
HTTP: true,
HTTPHost: "127.0.0.1",
HTTPPort: 4577,
Expand Down Expand Up @@ -471,7 +471,7 @@ network: sepolia
`,
inputArgs: []string{"--db-path", "/home/flag/.juno"},
expectedConfig: &node.Config{
LogLevel: utils.WARN,
LogLevel: "warn",
HTTP: defaultHTTP,
HTTPHost: "0.0.0.0",
HTTPPort: 4576,
Expand Down
4 changes: 2 additions & 2 deletions db/pebble/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ func New(path string) (db.DB, error) {
func NewWithOptions(path string, cacheSizeMB uint, maxOpenFiles int, colouredLogger bool) (db.DB, error) {
// Ensure that the specified cache size meets a minimum threshold.
cacheSizeMB = max(cacheSizeMB, minCacheSizeMB)

dbLog, err := utils.NewZapLogger(utils.ERROR, colouredLogger)
log := utils.NewLogLevel(utils.ERROR)
dbLog, err := utils.NewZapLogger(log, colouredLogger)
if err != nil {
return nil, fmt.Errorf("create DB logger: %w", err)
}
Expand Down
12 changes: 12 additions & 0 deletions docs/docs/monitoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,15 @@ To have Juno write logs to a file, use the command:
![Grafana dashboard](/img/grafana-1.png)

![Grafana dashboard](/img/grafana-2.png)

## Change log level in runtime

In case you want to change the log level in runtime without the need to restart the juno process, you can do it via HTTP calls.

Examples:

```console
curl -X PUT 'localhost:6789/log?level=trace'
curl -X PUT 'localhost:6789/log?level=info'
curl -X GET 'localhost:6789/log'
```
46 changes: 23 additions & 23 deletions node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,27 @@ const (

// Config is the top-level juno configuration.
type Config struct {
LogLevel utils.LogLevel `mapstructure:"log-level"`
HTTP bool `mapstructure:"http"`
HTTPHost string `mapstructure:"http-host"`
HTTPPort uint16 `mapstructure:"http-port"`
RPCCorsEnable bool `mapstructure:"rpc-cors-enable"`
Websocket bool `mapstructure:"ws"`
WebsocketHost string `mapstructure:"ws-host"`
WebsocketPort uint16 `mapstructure:"ws-port"`
GRPC bool `mapstructure:"grpc"`
GRPCHost string `mapstructure:"grpc-host"`
GRPCPort uint16 `mapstructure:"grpc-port"`
DatabasePath string `mapstructure:"db-path"`
Network utils.Network `mapstructure:"network"`
EthNode string `mapstructure:"eth-node"`
Pprof bool `mapstructure:"pprof"`
PprofHost string `mapstructure:"pprof-host"`
PprofPort uint16 `mapstructure:"pprof-port"`
Colour bool `mapstructure:"colour"`
PendingPollInterval time.Duration `mapstructure:"pending-poll-interval"`
RemoteDB string `mapstructure:"remote-db"`
VersionedConstantsFile string `mapstructure:"versioned-constants-file"`
LogLevel string `mapstructure:"log-level"`
HTTP bool `mapstructure:"http"`
HTTPHost string `mapstructure:"http-host"`
HTTPPort uint16 `mapstructure:"http-port"`
RPCCorsEnable bool `mapstructure:"rpc-cors-enable"`
Websocket bool `mapstructure:"ws"`
WebsocketHost string `mapstructure:"ws-host"`
WebsocketPort uint16 `mapstructure:"ws-port"`
GRPC bool `mapstructure:"grpc"`
GRPCHost string `mapstructure:"grpc-host"`
GRPCPort uint16 `mapstructure:"grpc-port"`
DatabasePath string `mapstructure:"db-path"`
Network utils.Network `mapstructure:"network"`
EthNode string `mapstructure:"eth-node"`
Pprof bool `mapstructure:"pprof"`
PprofHost string `mapstructure:"pprof-host"`
PprofPort uint16 `mapstructure:"pprof-port"`
Colour bool `mapstructure:"colour"`
PendingPollInterval time.Duration `mapstructure:"pending-poll-interval"`
RemoteDB string `mapstructure:"remote-db"`
VersionedConstantsFile string `mapstructure:"versioned-constants-file"`

Metrics bool `mapstructure:"metrics"`
MetricsHost string `mapstructure:"metrics-host"`
Expand Down Expand Up @@ -107,8 +107,8 @@ type Node struct {

// New sets the config and logger to the StarknetNode.
// Any errors while parsing the config on creating logger will be returned.
func New(cfg *Config, version string) (*Node, error) { //nolint:gocyclo,funlen
log, err := utils.NewZapLogger(cfg.LogLevel, cfg.Colour)
func New(cfg *Config, version string, logLevel *utils.LogLevel) (*Node, error) { //nolint:gocyclo,funlen
log, err := utils.NewZapLogger(logLevel, cfg.Colour)
if err != nil {
return nil, err
}
Expand Down
8 changes: 5 additions & 3 deletions node/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
// Create a new node with all services enabled.
func TestNewNode(t *testing.T) {
config := &node.Config{
LogLevel: utils.INFO,
LogLevel: "info",
HTTP: true,
HTTPPort: 0,
Websocket: true,
Expand All @@ -39,7 +39,8 @@ func TestNewNode(t *testing.T) {
P2PPeers: "",
}

n, err := node.New(config, "v0.3")
logLevel := utils.NewLogLevel(utils.INFO)
n, err := node.New(config, "v0.3", logLevel)
require.NoError(t, err)

ctx, cancel := context.WithCancel(context.Background())
Expand Down Expand Up @@ -76,10 +77,11 @@ func TestNetworkVerificationOnNonEmptyDB(t *testing.T) {
cancel()
require.NoError(t, database.Close())

logLevel := utils.NewLogLevel(utils.INFO)
_, err = node.New(&node.Config{
DatabasePath: dbPath,
Network: test.network,
}, "v0.1")
}, "v0.1", logLevel)
if test.errString == "" {
require.NoError(t, err)
} else {
Expand Down
Loading

0 comments on commit 41cf496

Please sign in to comment.