Skip to content

Commit ee0feaa

Browse files
committed
feat(logging)_: enable runtime logs configuration
Add endpoints for log level and namespaces configuration to `status.go` and deprecate equivalents from service. Endpoints are defined in `status.go`, as it has access to `statusBackend`, the only entity capable of manipulating node configuration without requiring a restart.
1 parent 0cf556b commit ee0feaa

File tree

6 files changed

+101
-36
lines changed

6 files changed

+101
-36
lines changed

api/geth_backend.go

+26-6
Original file line numberDiff line numberDiff line change
@@ -2079,12 +2079,6 @@ func (b *GethStatusBackend) loadNodeConfig(inputNodeCfg *params.NodeConfig) erro
20792079
}
20802080
}
20812081

2082-
if len(conf.LogDir) == 0 {
2083-
conf.LogFile = filepath.Join(b.rootDataDir, conf.LogFile)
2084-
} else {
2085-
conf.LogFile = filepath.Join(conf.LogDir, conf.LogFile)
2086-
}
2087-
20882082
b.config = conf
20892083

20902084
if inputNodeCfg != nil && inputNodeCfg.RuntimeLogLevel != "" {
@@ -2859,3 +2853,29 @@ func (b *GethStatusBackend) TogglePanicReporting(enabled bool) error {
28592853
}
28602854
return b.DisablePanicReporting()
28612855
}
2856+
2857+
func (b *GethStatusBackend) SetLogLevel(level string) error {
2858+
b.mu.Lock()
2859+
defer b.mu.Unlock()
2860+
2861+
err := nodecfg.SetLogLevel(b.appDB, level)
2862+
if err != nil {
2863+
return err
2864+
}
2865+
b.config.LogLevel = level
2866+
2867+
return logutils.OverrideRootLoggerWithConfig(b.config.LogSettings())
2868+
}
2869+
2870+
func (b *GethStatusBackend) SetLogNamespaces(namespaces string) error {
2871+
b.mu.Lock()
2872+
defer b.mu.Unlock()
2873+
2874+
err := nodecfg.SetLogNamespaces(b.appDB, namespaces)
2875+
if err != nil {
2876+
return err
2877+
}
2878+
b.config.LogNamespaces = namespaces
2879+
2880+
return logutils.OverrideRootLoggerWithConfig(b.config.LogSettings())
2881+
}

logutils/core.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ func (core *Core) Sync() error {
107107
}
108108

109109
func (core *Core) UpdateSyncer(newSyncer zapcore.WriteSyncer) {
110-
core.syncer.Store(writeSyncerWrapper{WriteSyncer: newSyncer})
110+
oldSyncer := core.syncer.Swap(writeSyncerWrapper{WriteSyncer: newSyncer})
111+
_ = oldSyncer.(zapcore.WriteSyncer).Sync() // may fail but doesn't impact syncer update
111112
}
112113

113114
func (core *Core) UpdateEncoder(newEncoder zapcore.Encoder) {

mobile/status.go

+39-1
Original file line numberDiff line numberDiff line change
@@ -1234,7 +1234,7 @@ func exportNodeLogs() string {
12341234
if config == nil {
12351235
return makeJSONResponse(errors.New("config and log file are not available"))
12361236
}
1237-
data, err := json.Marshal(exportlogs.ExportFromBaseFile(config.LogFile))
1237+
data, err := json.Marshal(exportlogs.ExportFromBaseFile(config.LogFilePath()))
12381238
if err != nil {
12391239
return makeJSONResponse(fmt.Errorf("error marshalling to json: %v", err))
12401240
}
@@ -2306,6 +2306,44 @@ func addCentralizedMetric(requestJSON string) string {
23062306
return metric.ID
23072307
}
23082308

2309+
func SetLogLevel(requestJSON string) string {
2310+
return callWithResponse(setLogLevel, requestJSON)
2311+
}
2312+
2313+
func setLogLevel(requestJSON string) string {
2314+
var request requests.SetLogLevel
2315+
err := json.Unmarshal([]byte(requestJSON), &request)
2316+
if err != nil {
2317+
return makeJSONResponse(err)
2318+
}
2319+
2320+
err = request.Validate()
2321+
if err != nil {
2322+
return makeJSONResponse(err)
2323+
}
2324+
2325+
return makeJSONResponse(statusBackend.SetLogLevel(request.LogLevel))
2326+
}
2327+
2328+
func SetLogNamespaces(requestJSON string) string {
2329+
return callWithResponse(setLogNamespaces, requestJSON)
2330+
}
2331+
2332+
func setLogNamespaces(requestJSON string) string {
2333+
var request requests.SetLogNamespaces
2334+
err := json.Unmarshal([]byte(requestJSON), &request)
2335+
if err != nil {
2336+
return makeJSONResponse(err)
2337+
}
2338+
2339+
err = request.Validate()
2340+
if err != nil {
2341+
return makeJSONResponse(err)
2342+
}
2343+
2344+
return makeJSONResponse(statusBackend.SetLogNamespaces(request.LogNamespaces))
2345+
}
2346+
23092347
func IntendedPanic(message string) string {
23102348
type intendedPanic struct {
23112349
error

params/config.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -1196,12 +1196,19 @@ func LesTopic(netid int) string {
11961196
}
11971197
}
11981198

1199+
func (c *NodeConfig) LogFilePath() string {
1200+
if c.LogDir == "" {
1201+
return filepath.Join(c.RootDataDir, c.LogFile)
1202+
}
1203+
return filepath.Join(c.LogDir, c.LogFile)
1204+
}
1205+
11991206
func (c *NodeConfig) LogSettings() logutils.LogSettings {
12001207
return logutils.LogSettings{
12011208
Enabled: c.LogEnabled,
12021209
Level: c.LogLevel,
12031210
Namespaces: c.LogNamespaces,
1204-
File: c.LogFile,
1211+
File: c.LogFilePath(),
12051212
MaxSize: c.LogMaxSize,
12061213
MaxBackups: c.LogMaxBackups,
12071214
CompressRotated: c.LogCompressRotated,

protocol/messenger_settings.go

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ func (m *Messenger) SetSyncingOnMobileNetwork(request *requests.SetSyncingOnMobi
3030
return nil
3131
}
3232

33+
// Deprecated: Use SetLogLevel from status.go instead.
3334
func (m *Messenger) SetLogLevel(request *requests.SetLogLevel) error {
3435
if err := request.Validate(); err != nil {
3536
return err
@@ -38,6 +39,7 @@ func (m *Messenger) SetLogLevel(request *requests.SetLogLevel) error {
3839
return nodecfg.SetLogLevel(m.database, request.LogLevel)
3940
}
4041

42+
// Deprecated: Use SetLogNamespaces from status.go instead.
4143
func (m *Messenger) SetLogNamespaces(request *requests.SetLogNamespaces) error {
4244
if err := request.Validate(); err != nil {
4345
return err

tests-functional/tests/test_logging.py

+24-27
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import re
2+
import time
23
from test_cases import StatusBackend
34
import pytest
4-
5+
import os
56

67
@pytest.mark.rpc
7-
@pytest.mark.skip("waiting for status-backend to be executed on the same host/container")
88
class TestLogging:
99

1010
@pytest.mark.init
@@ -25,41 +25,38 @@ def test_logging(self, tmp_path):
2525
key_uid = self.ensure_logged_in(backend_client)
2626

2727
# Configure logging
28-
backend_client.rpc_valid_request("wakuext_setLogLevel", [{"logLevel": "ERROR"}])
29-
backend_client.rpc_valid_request(
30-
"wakuext_setLogNamespaces",
31-
[{"logNamespaces": "test1.test2:debug,test1.test2.test3:info"}],
32-
)
28+
backend_client.api_valid_request("SetLogLevel", {"logLevel": "ERROR"})
29+
backend_client.api_valid_request("SetLogNamespaces", {"logNamespaces": "test1.test2:debug,test1.test2.test3:info"})
30+
31+
log_pattern = [
32+
r"DEBUG\s+test1\.test2\s+",
33+
r"INFO\s+test1\.test2\s+",
34+
r"INFO\s+test1\.test2\.test3\s+",
35+
r"WARN\s+test1\.test2\s+",
36+
r"WARN\s+test1\.test2\.test3\s+",
37+
r"ERROR\s+test1\s+",
38+
r"ERROR\s+test1\.test2\s+",
39+
r"ERROR\s+test1\.test2\.test3\s+",
40+
]
3341

34-
# Re-login (logging settings take effect after re-login)
42+
# Ensure changes take effect at runtime
43+
backend_client.rpc_valid_request("wakuext_logTest")
44+
self.expect_logs(tmp_path / "geth.log", "test message", log_pattern, count=1)
45+
46+
# Ensure changes are persisted after re-login
3547
backend_client.logout()
3648
backend_client.login(str(key_uid))
3749
self.ensure_logged_in(backend_client)
38-
39-
# Test logging
4050
backend_client.rpc_valid_request("wakuext_logTest")
41-
self.expect_logs(
42-
tmp_path / "geth.log",
43-
"test message",
44-
[
45-
r"DEBUG\s+test1\.test2",
46-
r"INFO\s+test1\.test2",
47-
r"INFO\s+test1\.test2\.test3",
48-
r"WARN\s+test1\.test2",
49-
r"WARN\s+test1\.test2\.test3",
50-
r"ERROR\s+test1",
51-
r"ERROR\s+test1\.test2",
52-
r"ERROR\s+test1\.test2\.test3",
53-
],
54-
)
51+
self.expect_logs(tmp_path / "geth.log", "test message", log_pattern, count=2)
5552

56-
def expect_logs(self, log_file, filter_keyword, expected_logs):
57-
with open(log_file, "r") as f:
53+
def expect_logs(self, log_file, filter_keyword, expected_logs, count):
54+
with open(log_file, 'r') as f:
5855
log_content = f.read()
5956

6057
filtered_logs = [line for line in log_content.splitlines() if filter_keyword in line]
6158
for expected_log in expected_logs:
62-
assert any(re.search(expected_log, log) for log in filtered_logs), f"Log entry not found: {expected_log}"
59+
assert sum(1 for log in filtered_logs if re.search(expected_log, log)) == count, f"Log entry not found or count mismatch: {expected_log}"
6360

6461
def ensure_logged_in(self, backend_client):
6562
login_response = backend_client.wait_for_signal("node.login")

0 commit comments

Comments
 (0)