Skip to content

Commit 41cf496

Browse files
committed
Add runtime log level control via HTTP (Fix #1181)
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.
1 parent 8593fd1 commit 41cf496

File tree

10 files changed

+154
-96
lines changed

10 files changed

+154
-96
lines changed

Diff for: cmd/juno/juno.go

+27-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"math"
77
"math/big"
8+
"net/http"
89
"os"
910
"os/signal"
1011
"path/filepath"
@@ -199,11 +200,35 @@ func main() {
199200
return err
200201
}
201202

202-
n, err := node.New(config, Version)
203+
logLevel := utils.NewLogLevel(utils.INFO)
204+
err = logLevel.Set(config.LogLevel)
203205
if err != nil {
204206
return err
205207
}
206208

209+
n, err := node.New(config, Version, logLevel)
210+
if err != nil {
211+
return err
212+
}
213+
214+
// Create a HTTP server to control the log level.
215+
http.HandleFunc("/log", func(w http.ResponseWriter, r *http.Request) {
216+
utils.HTTPLogSettings(w, r, logLevel)
217+
})
218+
go func() {
219+
server := &http.Server{
220+
Addr: ":6789",
221+
Handler: nil,
222+
ReadTimeout: 5 * time.Second,
223+
WriteTimeout: 10 * time.Second,
224+
IdleTimeout: 15 * time.Second,
225+
}
226+
err := server.ListenAndServe()
227+
if err != nil && err != http.ErrServerClosed {
228+
fmt.Printf("HTTP server ListenAndServe: %v", err)
229+
}
230+
}()
231+
207232
n.Run(cmd.Context())
208233
return nil
209234
})
@@ -304,13 +329,12 @@ func NewCmd(config *node.Config, run func(*cobra.Command, []string) error) *cobr
304329

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

312336
junoCmd.Flags().StringVar(&cfgFile, configF, defaultConfig, configFlagUsage)
313-
junoCmd.Flags().Var(&defaultLogLevel, logLevelF, logLevelFlagUsage)
337+
junoCmd.Flags().String(logLevelF, utils.INFO.String(), logLevelFlagUsage)
314338
junoCmd.Flags().Bool(httpF, defaultHTTP, httpUsage)
315339
junoCmd.Flags().String(httpHostF, defaulHost, httpHostUsage)
316340
junoCmd.Flags().Uint16(httpPortF, defaultHTTPPort, httpPortUsage)

Diff for: cmd/juno/juno_test.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func TestConfigPrecedence(t *testing.T) {
3030
// tested for sanity. These tests are not intended to perform semantics
3131
// checks on the config, those will be checked by the node implementation.
3232
defaultHost := "localhost"
33-
defaultLogLevel := utils.INFO
33+
defaultLogLevel := "info"
3434
defaultHTTP := false
3535
defaultHTTPPort := uint16(6060)
3636
defaultWS := false
@@ -83,7 +83,7 @@ func TestConfigPrecedence(t *testing.T) {
8383
"--cn-core-contract-address", "0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4",
8484
},
8585
expectedConfig: &node.Config{
86-
LogLevel: utils.DEBUG,
86+
LogLevel: "debug",
8787
HTTP: defaultHTTP,
8888
HTTPHost: "0.0.0.0",
8989
HTTPPort: 4576,
@@ -128,7 +128,7 @@ cn-core-contract-address: 0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4
128128
cn-unverifiable-range: [0,10]
129129
`,
130130
expectedConfig: &node.Config{
131-
LogLevel: utils.DEBUG,
131+
LogLevel: "debug",
132132
HTTP: defaultHTTP,
133133
HTTPHost: "0.0.0.0",
134134
HTTPPort: 4576,
@@ -268,7 +268,7 @@ network: sepolia
268268
pprof: true
269269
`,
270270
expectedConfig: &node.Config{
271-
LogLevel: utils.DEBUG,
271+
LogLevel: "debug",
272272
HTTP: defaultHTTP,
273273
HTTPHost: "0.0.0.0",
274274
HTTPPort: 4576,
@@ -304,7 +304,7 @@ http-host: 0.0.0.0
304304
http-port: 4576
305305
`,
306306
expectedConfig: &node.Config{
307-
LogLevel: utils.DEBUG,
307+
LogLevel: "debug",
308308
HTTP: defaultHTTP,
309309
HTTPHost: "0.0.0.0",
310310
HTTPPort: 4576,
@@ -339,7 +339,7 @@ http-port: 4576
339339
"--db-path", "/home/.juno", "--network", "sepolia-integration", "--pprof", "--db-cache-size", "1024",
340340
},
341341
expectedConfig: &node.Config{
342-
LogLevel: utils.DEBUG,
342+
LogLevel: "debug",
343343
HTTP: defaultHTTP,
344344
HTTPHost: "0.0.0.0",
345345
HTTPPort: 4576,
@@ -374,7 +374,7 @@ http-port: 4576
374374
"--network", "sepolia",
375375
},
376376
expectedConfig: &node.Config{
377-
LogLevel: utils.DEBUG,
377+
LogLevel: "debug",
378378
HTTP: defaultHTTP,
379379
HTTPHost: "0.0.0.0",
380380
HTTPPort: 4576,
@@ -433,7 +433,7 @@ db-cache-size: 1024
433433
"--db-cache-size", "9",
434434
},
435435
expectedConfig: &node.Config{
436-
LogLevel: utils.ERROR,
436+
LogLevel: "error",
437437
HTTP: true,
438438
HTTPHost: "127.0.0.1",
439439
HTTPPort: 4577,
@@ -471,7 +471,7 @@ network: sepolia
471471
`,
472472
inputArgs: []string{"--db-path", "/home/flag/.juno"},
473473
expectedConfig: &node.Config{
474-
LogLevel: utils.WARN,
474+
LogLevel: "warn",
475475
HTTP: defaultHTTP,
476476
HTTPHost: "0.0.0.0",
477477
HTTPPort: 4576,

Diff for: db/pebble/db.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ func New(path string) (db.DB, error) {
3939
func NewWithOptions(path string, cacheSizeMB uint, maxOpenFiles int, colouredLogger bool) (db.DB, error) {
4040
// Ensure that the specified cache size meets a minimum threshold.
4141
cacheSizeMB = max(cacheSizeMB, minCacheSizeMB)
42-
43-
dbLog, err := utils.NewZapLogger(utils.ERROR, colouredLogger)
42+
log := utils.NewLogLevel(utils.ERROR)
43+
dbLog, err := utils.NewZapLogger(log, colouredLogger)
4444
if err != nil {
4545
return nil, fmt.Errorf("create DB logger: %w", err)
4646
}

Diff for: docs/docs/monitoring.md

+12
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,15 @@ To have Juno write logs to a file, use the command:
8585
![Grafana dashboard](/img/grafana-1.png)
8686

8787
![Grafana dashboard](/img/grafana-2.png)
88+
89+
## Change log level in runtime
90+
91+
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.
92+
93+
Examples:
94+
95+
```console
96+
curl -X PUT 'localhost:6789/log?level=trace'
97+
curl -X PUT 'localhost:6789/log?level=info'
98+
curl -X GET 'localhost:6789/log'
99+
```

Diff for: node/node.go

+23-23
Original file line numberDiff line numberDiff line change
@@ -46,27 +46,27 @@ const (
4646

4747
// Config is the top-level juno configuration.
4848
type Config struct {
49-
LogLevel utils.LogLevel `mapstructure:"log-level"`
50-
HTTP bool `mapstructure:"http"`
51-
HTTPHost string `mapstructure:"http-host"`
52-
HTTPPort uint16 `mapstructure:"http-port"`
53-
RPCCorsEnable bool `mapstructure:"rpc-cors-enable"`
54-
Websocket bool `mapstructure:"ws"`
55-
WebsocketHost string `mapstructure:"ws-host"`
56-
WebsocketPort uint16 `mapstructure:"ws-port"`
57-
GRPC bool `mapstructure:"grpc"`
58-
GRPCHost string `mapstructure:"grpc-host"`
59-
GRPCPort uint16 `mapstructure:"grpc-port"`
60-
DatabasePath string `mapstructure:"db-path"`
61-
Network utils.Network `mapstructure:"network"`
62-
EthNode string `mapstructure:"eth-node"`
63-
Pprof bool `mapstructure:"pprof"`
64-
PprofHost string `mapstructure:"pprof-host"`
65-
PprofPort uint16 `mapstructure:"pprof-port"`
66-
Colour bool `mapstructure:"colour"`
67-
PendingPollInterval time.Duration `mapstructure:"pending-poll-interval"`
68-
RemoteDB string `mapstructure:"remote-db"`
69-
VersionedConstantsFile string `mapstructure:"versioned-constants-file"`
49+
LogLevel string `mapstructure:"log-level"`
50+
HTTP bool `mapstructure:"http"`
51+
HTTPHost string `mapstructure:"http-host"`
52+
HTTPPort uint16 `mapstructure:"http-port"`
53+
RPCCorsEnable bool `mapstructure:"rpc-cors-enable"`
54+
Websocket bool `mapstructure:"ws"`
55+
WebsocketHost string `mapstructure:"ws-host"`
56+
WebsocketPort uint16 `mapstructure:"ws-port"`
57+
GRPC bool `mapstructure:"grpc"`
58+
GRPCHost string `mapstructure:"grpc-host"`
59+
GRPCPort uint16 `mapstructure:"grpc-port"`
60+
DatabasePath string `mapstructure:"db-path"`
61+
Network utils.Network `mapstructure:"network"`
62+
EthNode string `mapstructure:"eth-node"`
63+
Pprof bool `mapstructure:"pprof"`
64+
PprofHost string `mapstructure:"pprof-host"`
65+
PprofPort uint16 `mapstructure:"pprof-port"`
66+
Colour bool `mapstructure:"colour"`
67+
PendingPollInterval time.Duration `mapstructure:"pending-poll-interval"`
68+
RemoteDB string `mapstructure:"remote-db"`
69+
VersionedConstantsFile string `mapstructure:"versioned-constants-file"`
7070

7171
Metrics bool `mapstructure:"metrics"`
7272
MetricsHost string `mapstructure:"metrics-host"`
@@ -107,8 +107,8 @@ type Node struct {
107107

108108
// New sets the config and logger to the StarknetNode.
109109
// Any errors while parsing the config on creating logger will be returned.
110-
func New(cfg *Config, version string) (*Node, error) { //nolint:gocyclo,funlen
111-
log, err := utils.NewZapLogger(cfg.LogLevel, cfg.Colour)
110+
func New(cfg *Config, version string, logLevel *utils.LogLevel) (*Node, error) { //nolint:gocyclo,funlen
111+
log, err := utils.NewZapLogger(logLevel, cfg.Colour)
112112
if err != nil {
113113
return nil, err
114114
}

Diff for: node/node_test.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
// Create a new node with all services enabled.
1919
func TestNewNode(t *testing.T) {
2020
config := &node.Config{
21-
LogLevel: utils.INFO,
21+
LogLevel: "info",
2222
HTTP: true,
2323
HTTPPort: 0,
2424
Websocket: true,
@@ -39,7 +39,8 @@ func TestNewNode(t *testing.T) {
3939
P2PPeers: "",
4040
}
4141

42-
n, err := node.New(config, "v0.3")
42+
logLevel := utils.NewLogLevel(utils.INFO)
43+
n, err := node.New(config, "v0.3", logLevel)
4344
require.NoError(t, err)
4445

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

80+
logLevel := utils.NewLogLevel(utils.INFO)
7981
_, err = node.New(&node.Config{
8082
DatabasePath: dbPath,
8183
Network: test.network,
82-
}, "v0.1")
84+
}, "v0.1", logLevel)
8385
if test.errString == "" {
8486
require.NoError(t, err)
8587
} else {

0 commit comments

Comments
 (0)