Skip to content

Commit 73713ac

Browse files
authored
RSDK-5972: Have modular resources obey log configuration levels. (#3307)
1 parent 309f5aa commit 73713ac

File tree

3 files changed

+103
-1
lines changed

3 files changed

+103
-1
lines changed

module/module.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ type Module struct {
154154
activeBackgroundWorkers sync.WaitGroup
155155
handlers HandlerMap
156156
collections map[resource.API]resource.APIResourceCollection[resource.Resource]
157+
resLoggers map[resource.Resource]logging.Logger
157158
closeOnce sync.Once
158159
pb.UnimplementedModuleServiceServer
159160
}
@@ -176,6 +177,7 @@ func NewModule(ctx context.Context, address string, logger logging.Logger) (*Mod
176177
ready: true,
177178
handlers: HandlerMap{},
178179
collections: map[resource.API]resource.APIResourceCollection[resource.Resource]{},
180+
resLoggers: map[resource.Resource]logging.Logger{},
179181
}
180182
if err := m.server.RegisterServiceServer(ctx, &pb.ModuleService_ServiceDesc, m); err != nil {
181183
return nil, err
@@ -332,7 +334,8 @@ func (m *Module) AddResource(ctx context.Context, req *pb.AddResourceRequest) (*
332334
if resInfo.Constructor == nil {
333335
return nil, errors.Errorf("invariant: no constructor for %q", conf.API)
334336
}
335-
res, err := resInfo.Constructor(ctx, deps, *conf, m.logger)
337+
resLogger := m.logger.Sublogger(conf.ResourceName().String())
338+
res, err := resInfo.Constructor(ctx, deps, *conf, resLogger)
336339
if err != nil {
337340
return nil, err
338341
}
@@ -343,6 +346,7 @@ func (m *Module) AddResource(ctx context.Context, req *pb.AddResourceRequest) (*
343346
if !ok {
344347
return nil, errors.Errorf("module cannot service api: %s", conf.API)
345348
}
349+
m.resLoggers[res] = resLogger
346350

347351
return &pb.AddResourceResponse{}, coll.Add(conf.ResourceName(), res)
348352
}
@@ -385,6 +389,15 @@ func (m *Module) ReconfigureResource(ctx context.Context, req *pb.ReconfigureRes
385389
return nil, err
386390
}
387391

392+
if logger, ok := m.resLoggers[res]; ok {
393+
levelStr := req.GetConfig().GetLogConfiguration().GetLevel()
394+
if level, err := logging.LevelFromString(levelStr); err == nil {
395+
logger.SetLevel(level)
396+
} else {
397+
m.logger.Warnw("LogConfiguration does not contain a valid level.", "resource", res.Name().Name, "level", levelStr)
398+
}
399+
}
400+
388401
reconfErr := res.Reconfigure(ctx, deps, *conf)
389402
if reconfErr == nil {
390403
return &pb.ReconfigureResourceResponse{}, nil

module/testmodule/main.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,25 @@ func (h *helper) DoCommand(ctx context.Context, req map[string]interface{}) (map
133133
return map[string]interface{}{}, err
134134
}
135135
return map[string]interface{}{"path": workingDir}, nil
136+
case "log":
137+
level, err := logging.LevelFromString(req["level"].(string))
138+
if err != nil {
139+
return nil, err
140+
}
141+
142+
msg := req["msg"].(string)
143+
switch level {
144+
case logging.DEBUG:
145+
h.logger.Debug(msg)
146+
case logging.INFO:
147+
h.logger.Info(msg)
148+
case logging.WARN:
149+
h.logger.Warn(msg)
150+
case logging.ERROR:
151+
h.logger.Error(msg)
152+
}
153+
154+
return map[string]any{}, nil
136155
default:
137156
return nil, fmt.Errorf("unknown command string %s", cmd)
138157
}

robot/impl/resource_manager_modular_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/pkg/errors"
1010
"go.viam.com/test"
1111

12+
"go.viam.com/rdk/components/generic"
1213
"go.viam.com/rdk/components/motor"
1314
"go.viam.com/rdk/components/motor/fake"
1415
"go.viam.com/rdk/config"
@@ -19,6 +20,7 @@ import (
1920
"go.viam.com/rdk/robot/framesystem"
2021
"go.viam.com/rdk/services/motion"
2122
motionBuiltin "go.viam.com/rdk/services/motion/builtin"
23+
rtestutils "go.viam.com/rdk/testutils"
2224
"go.viam.com/rdk/utils"
2325
)
2426

@@ -471,3 +473,71 @@ func (m *dummyModMan) Close(ctx context.Context) error {
471473
}
472474
return nil
473475
}
476+
477+
func TestDynamicModuleLogging(t *testing.T) {
478+
modPath, err := rtestutils.BuildTempModule(t, "module/testmodule")
479+
test.That(t, err, test.ShouldBeNil)
480+
481+
ctx := context.Background()
482+
logger, observer := logging.NewObservedTestLogger(t)
483+
484+
helperConf := resource.Config{
485+
Name: "helper",
486+
API: generic.API,
487+
Model: resource.NewModel("rdk", "test", "helper"),
488+
LogConfiguration: resource.LogConfig{
489+
Level: logging.INFO,
490+
},
491+
}
492+
cfg := &config.Config{
493+
Components: []resource.Config{helperConf},
494+
Modules: []config.Module{{
495+
Name: "helperModule",
496+
ExePath: modPath,
497+
LogLevel: "info",
498+
Type: "local",
499+
}},
500+
}
501+
502+
myRobot, err := RobotFromConfig(ctx, cfg, logger)
503+
test.That(t, err, test.ShouldBeNil)
504+
defer myRobot.Close(ctx)
505+
506+
client, err := generic.FromRobot(myRobot, "helper")
507+
test.That(t, err, test.ShouldBeNil)
508+
defer client.Close(ctx)
509+
510+
//nolint:lll
511+
// Have the module log a line at info. It should appear as:
512+
// 2023-12-06T15:55:32.590-0500 INFO process.helperModule_/tmp/TestDynamicModuleLogging3790223620/001/testmodule.StdOut pexec/managed_process.go:244
513+
// \_ 2023-12-06T15:55:32.590-0500 INFO TestModule.rdk:component:generic/helper testmodule/main.go:147 special rare log line
514+
logLine := "special rare log line"
515+
testCmd := map[string]interface{}{"command": "log", "msg": logLine, "level": "info"}
516+
_, err = client.DoCommand(ctx, testCmd)
517+
test.That(t, err, test.ShouldBeNil)
518+
519+
// Our log observer should find one occurrence of the log line.
520+
test.That(t, observer.FilterMessageSnippet(logLine).Len(), test.ShouldEqual, 1)
521+
522+
// The module is currently configured to log at info. If the module tries to log at debug,
523+
// nothing new should be observed.
524+
testCmd = map[string]interface{}{"command": "log", "msg": logLine, "level": "debug"}
525+
_, err = client.DoCommand(ctx, testCmd)
526+
test.That(t, err, test.ShouldBeNil)
527+
528+
test.That(t, observer.FilterMessageSnippet(logLine).Len(), test.ShouldEqual, 1)
529+
test.That(t, observer.FilterMessageSnippet(logLine).FilterMessageSnippet("DEBUG").Len(), test.ShouldEqual, 0)
530+
531+
// Change the modular component to log at DEBUG instead of INFO.
532+
cfg.Components[0].LogConfiguration.Level = logging.DEBUG
533+
myRobot.Reconfigure(ctx, cfg)
534+
535+
// Trying to log again at DEBUG should see our log line pattern show up a second time. Now with
536+
// DEBUG in the output string.
537+
testCmd = map[string]interface{}{"command": "log", "msg": logLine, "level": "debug"}
538+
_, err = client.DoCommand(ctx, testCmd)
539+
test.That(t, err, test.ShouldBeNil)
540+
541+
test.That(t, observer.FilterMessageSnippet(logLine).Len(), test.ShouldEqual, 2)
542+
test.That(t, observer.FilterMessageSnippet(logLine).FilterMessageSnippet("DEBUG").Len(), test.ShouldEqual, 1)
543+
}

0 commit comments

Comments
 (0)