Skip to content

Commit b8ae4a4

Browse files
committed
Add CLI to set/get module log levels on peer
Dynamically set or get the log level for the specified module running on a peer. Useful for problem determination / debugging. Use the following format: peer logging setlevel <module-name> <log-level> peer logging getlevel <module-name> Fix Issue FAB-574 Change-Id: I5b16d100a84393bafe052511bb2e6f188214e81e Signed-off-by: Will Lahti <[email protected]>
1 parent 37b1168 commit b8ae4a4

12 files changed

+536
-17
lines changed

core/admin.go

+17
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"golang.org/x/net/context"
2626

2727
"github.com/golang/protobuf/ptypes/empty"
28+
"github.com/hyperledger/fabric/flogging"
2829
pb "github.com/hyperledger/fabric/protos"
2930
)
3031

@@ -78,3 +79,19 @@ func (*ServerAdmin) StopServer(context.Context, *empty.Empty) (*pb.ServerStatus,
7879
defer os.Exit(0)
7980
return status, nil
8081
}
82+
83+
// GetModuleLogLevel gets the current logging level for the specified module
84+
func (*ServerAdmin) GetModuleLogLevel(ctx context.Context, request *pb.LogLevelRequest) (*pb.LogLevelResponse, error) {
85+
logLevelString, err := flogging.GetModuleLogLevel(request.LogModule)
86+
logResponse := &pb.LogLevelResponse{LogModule: request.LogModule, LogLevel: logLevelString}
87+
88+
return logResponse, err
89+
}
90+
91+
// SetModuleLogLevel sets the logging level for the specified module
92+
func (*ServerAdmin) SetModuleLogLevel(ctx context.Context, request *pb.LogLevelRequest) (*pb.LogLevelResponse, error) {
93+
logLevelString, err := flogging.SetModuleLogLevel(request.LogModule, request.LogLevel)
94+
logResponse := &pb.LogLevelResponse{LogModule: request.LogModule, LogLevel: logLevelString}
95+
96+
return logResponse, err
97+
}

flogging/logging.go

+30
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,33 @@ func init() {
9898
backendFormatter := logging.NewBackendFormatter(backend, format)
9999
logging.SetBackend(backendFormatter).SetLevel(loggingDefaultLevel, "")
100100
}
101+
102+
// GetModuleLogLevel gets the current logging level for the specified module
103+
func GetModuleLogLevel(module string) (string, error) {
104+
// logging.GetLevel() returns the logging level for the module, if defined.
105+
// otherwise, it returns the default logging level, as set by
106+
// flogging/logging.go
107+
level := logging.GetLevel(module).String()
108+
109+
loggingLogger.Infof("Module '%s' logger enabled for log level: %s", module, level)
110+
111+
return level, nil
112+
}
113+
114+
// SetModuleLogLevel sets the logging level for the specified module. This is
115+
// currently only called from admin.go but can be called from anywhere in the
116+
// code on a running peer to dynamically change the log level for the module.
117+
func SetModuleLogLevel(module string, logLevel string) (string, error) {
118+
level, err := logging.LogLevel(logLevel)
119+
120+
if err != nil {
121+
loggingLogger.Warningf("Invalid logging level: %s - ignored", logLevel)
122+
} else {
123+
logging.SetLevel(logging.Level(level), module)
124+
loggingLogger.Infof("Module '%s' logger enabled for log level: %s", module, level)
125+
}
126+
127+
logLevelString := level.String()
128+
129+
return logLevelString, err
130+
}

flogging/logging_test.go

+43
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,49 @@ func TestLoggingLevelInvalidModuleSyntax(t *testing.T) {
143143
assertDefaultLoggingLevel(t, flogging.DefaultLoggingLevel())
144144
}
145145

146+
func TestGetModuleLoggingLevelDefault(t *testing.T) {
147+
level, _ := flogging.GetModuleLogLevel("peer")
148+
149+
// peer should be using the default log level at this point
150+
if level != "INFO" {
151+
t.FailNow()
152+
}
153+
}
154+
155+
func TestGetModuleLoggingLevelDebug(t *testing.T) {
156+
flogging.SetModuleLogLevel("peer", "DEBUG")
157+
level, _ := flogging.GetModuleLogLevel("peer")
158+
159+
// ensure that the log level has changed to debug
160+
if level != "DEBUG" {
161+
t.FailNow()
162+
}
163+
}
164+
165+
func TestGetModuleLoggingLevelInvalid(t *testing.T) {
166+
flogging.SetModuleLogLevel("peer", "invalid")
167+
level, _ := flogging.GetModuleLogLevel("peer")
168+
169+
// ensure that the log level didn't change after invalid log level specified
170+
if level != "DEBUG" {
171+
t.FailNow()
172+
}
173+
}
174+
175+
func TestSetModuleLoggingLevel(t *testing.T) {
176+
flogging.SetModuleLogLevel("peer", "WARNING")
177+
178+
// ensure that the log level has changed to warning
179+
assertModuleLoggingLevel(t, "peer", logging.WARNING)
180+
}
181+
182+
func TestSetModuleLoggingLevelInvalid(t *testing.T) {
183+
flogging.SetModuleLogLevel("peer", "invalid")
184+
185+
// ensure that the log level didn't change after invalid log level specified
186+
assertModuleLoggingLevel(t, "peer", logging.WARNING)
187+
}
188+
146189
func assertDefaultLoggingLevel(t *testing.T, expectedLevel logging.Level) {
147190
assertModuleLoggingLevel(t, "", expectedLevel)
148191
}

peer/clilogging/common.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package clilogging
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/op/go-logging"
23+
"github.com/spf13/cobra"
24+
)
25+
26+
func checkLoggingCmdParams(cmd *cobra.Command, args []string) error {
27+
var err error
28+
29+
// check that at least one parameter is passed in
30+
if len(args) == 0 {
31+
return fmt.Errorf("no parameters provided")
32+
}
33+
34+
if cmd.Name() == "setlevel" {
35+
if len(args) == 1 {
36+
err = fmt.Errorf("no log level provided")
37+
} else {
38+
// check that log level is valid. if not, err is set
39+
_, err = logging.LogLevel(args[1])
40+
if err != nil {
41+
err = fmt.Errorf("%s - %s", err, args[1])
42+
}
43+
}
44+
}
45+
46+
return err
47+
}

peer/clilogging/getlevel.go

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package clilogging
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/hyperledger/fabric/core/peer"
23+
pb "github.com/hyperledger/fabric/protos"
24+
"github.com/spf13/cobra"
25+
"golang.org/x/net/context"
26+
)
27+
28+
func getLevelCmd() *cobra.Command {
29+
return loggingGetLevelCmd
30+
}
31+
32+
var loggingGetLevelCmd = &cobra.Command{
33+
Use: "getlevel <module>",
34+
Short: "Returns the logging level of the requested module logger.",
35+
Long: `Returns the logging level of the requested module logger`,
36+
Run: func(cmd *cobra.Command, args []string) {
37+
getLevel(cmd, args)
38+
},
39+
}
40+
41+
func getLevel(cmd *cobra.Command, args []string) (err error) {
42+
err = checkLoggingCmdParams(cmd, args)
43+
44+
if err != nil {
45+
logger.Warningf("Error: %s", err)
46+
} else {
47+
clientConn, err := peer.NewPeerClientConnection()
48+
if err != nil {
49+
logger.Infof("Error trying to connect to local peer: %s", err)
50+
err = fmt.Errorf("Error trying to connect to local peer: %s", err)
51+
fmt.Println(&pb.ServerStatus{Status: pb.ServerStatus_UNKNOWN})
52+
return err
53+
}
54+
55+
serverClient := pb.NewAdminClient(clientConn)
56+
57+
logResponse, err := serverClient.GetModuleLogLevel(context.Background(), &pb.LogLevelRequest{LogModule: args[0]})
58+
59+
if err != nil {
60+
logger.Warningf("Error retrieving log level")
61+
return err
62+
}
63+
logger.Infof("Current log level for module '%s': %s", logResponse.LogModule, logResponse.LogLevel)
64+
}
65+
return err
66+
}

peer/clilogging/logging.go

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
Copyright IBM Corp. 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package clilogging
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/op/go-logging"
23+
"github.com/spf13/cobra"
24+
)
25+
26+
const loggingFuncName = "logging"
27+
28+
var logger = logging.MustGetLogger("loggingCmd")
29+
30+
// Cmd returns the cobra command for Logging
31+
func Cmd() *cobra.Command {
32+
loggingCmd.AddCommand(getLevelCmd())
33+
loggingCmd.AddCommand(setLevelCmd())
34+
35+
return loggingCmd
36+
}
37+
38+
var loggingCmd = &cobra.Command{
39+
Use: loggingFuncName,
40+
Short: fmt.Sprintf("%s specific commands.", loggingFuncName),
41+
Long: fmt.Sprintf("%s specific commands.", loggingFuncName),
42+
}

peer/clilogging/logging_test.go

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
Copyright Digital Asset Holdings, LLC 2016 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package clilogging
18+
19+
import "testing"
20+
21+
// TestGetLevelEmptyParams tests the parameter checking for getlevel, which
22+
// should return an error when no parameters are provided
23+
func TestGetLevelEmptyParams(t *testing.T) {
24+
var args []string
25+
26+
err := checkLoggingCmdParams(getLevelCmd(), args)
27+
28+
if err == nil {
29+
t.FailNow()
30+
}
31+
}
32+
33+
// TestGetLevel tests the parameter checking for getlevel, which should
34+
// should return a nil error when one (or more) parameters are provided
35+
func TestGetLevel(t *testing.T) {
36+
args := make([]string, 1)
37+
args[0] = "peer"
38+
39+
err := checkLoggingCmdParams(getLevelCmd(), args)
40+
41+
if err != nil {
42+
t.FailNow()
43+
}
44+
}
45+
46+
// TestSetLevelEmptyParams tests the parameter checking for setlevel, which
47+
// should return an error when no parameters are provided
48+
func TestSetLevelEmptyParams(t *testing.T) {
49+
var args []string
50+
51+
err := checkLoggingCmdParams(setLevelCmd(), args)
52+
53+
if err == nil {
54+
t.FailNow()
55+
}
56+
}
57+
58+
// TestSetLevelEmptyParams tests the parameter checking for setlevel, which
59+
// should return an error when only one parameter is provided
60+
func TestSetLevelOneParam(t *testing.T) {
61+
args := make([]string, 1)
62+
args[0] = "peer"
63+
64+
err := checkLoggingCmdParams(setLevelCmd(), args)
65+
66+
if err == nil {
67+
t.FailNow()
68+
}
69+
}
70+
71+
// TestSetLevelEmptyParams tests the parameter checking for setlevel, which
72+
// should return an error when an invalid log level is provided
73+
func TestSetLevelInvalid(t *testing.T) {
74+
args := make([]string, 2)
75+
args[0] = "peer"
76+
args[1] = "invalidlevel"
77+
78+
err := checkLoggingCmdParams(setLevelCmd(), args)
79+
80+
if err == nil {
81+
t.FailNow()
82+
}
83+
}
84+
85+
// TestSetLevelEmptyParams tests the parameter checking for setlevel, which
86+
// should return a nil error when two parameters, the second of which is a
87+
// valid log level, are provided
88+
func TestSetLevel(t *testing.T) {
89+
args := make([]string, 2)
90+
args[0] = "peer"
91+
args[1] = "debug"
92+
93+
err := checkLoggingCmdParams(setLevelCmd(), args)
94+
95+
if err != nil {
96+
t.FailNow()
97+
}
98+
}

0 commit comments

Comments
 (0)