Skip to content

Commit 58ddae7

Browse files
Added gRPC debugger (#1443)
* Added flags to enable logging of gRPC calls * Added gRPC debug filter * updated i18n * Update cli/daemon/daemon.go Co-authored-by: Silvano Cerza <3314350+silvanocerza@users.noreply.github.com> Co-authored-by: Silvano Cerza <3314350+silvanocerza@users.noreply.github.com>
1 parent b6c9edb commit 58ddae7

File tree

4 files changed

+126
-10
lines changed

4 files changed

+126
-10
lines changed

cli/daemon/daemon.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,14 @@ func NewCommand() *cobra.Command {
5656
cmd.PersistentFlags().String("port", "", tr("The TCP port the daemon will listen to"))
5757
configuration.Settings.BindPFlag("daemon.port", cmd.PersistentFlags().Lookup("port"))
5858
cmd.Flags().BoolVar(&daemonize, "daemonize", false, tr("Do not terminate daemon process if the parent process dies"))
59+
cmd.Flags().BoolVar(&debug, "debug", false, tr("Enable debug logging of gRPC calls"))
60+
cmd.Flags().StringSliceVar(&debugFilters, "debug-filter", []string{}, tr("Display only the provided gRPC calls"))
5961
return cmd
6062
}
6163

6264
var daemonize bool
65+
var debug bool
66+
var debugFilters []string
6367

6468
func runDaemonCommand(cmd *cobra.Command, args []string) {
6569

@@ -69,8 +73,14 @@ func runDaemonCommand(cmd *cobra.Command, args []string) {
6973
defer stats.Flush()
7074
}
7175
port := configuration.Settings.GetString("daemon.port")
72-
s := grpc.NewServer()
73-
76+
gRPCOptions := []grpc.ServerOption{}
77+
if debug {
78+
gRPCOptions = append(gRPCOptions,
79+
grpc.UnaryInterceptor(unaryLoggerInterceptor),
80+
grpc.StreamInterceptor(streamLoggerInterceptor),
81+
)
82+
}
83+
s := grpc.NewServer(gRPCOptions...)
7484
// Set specific user-agent for the daemon
7585
configuration.Settings.Set("network.user_agent_ext", "daemon")
7686

cli/daemon/interceptors.go

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to license@arduino.cc.
15+
16+
package daemon
17+
18+
import (
19+
"context"
20+
"encoding/json"
21+
"fmt"
22+
"strings"
23+
24+
"google.golang.org/grpc"
25+
)
26+
27+
func log(isRequest bool, msg interface{}) {
28+
j, _ := json.MarshalIndent(msg, "| ", " ")
29+
inOut := map[bool]string{true: "REQ: ", false: "RESP: "}
30+
fmt.Println("| " + inOut[isRequest] + string(j))
31+
}
32+
33+
func logError(err error) {
34+
if err != nil {
35+
fmt.Println("| ERROR: ", err)
36+
}
37+
}
38+
39+
func logSelector(method string) bool {
40+
if len(debugFilters) == 0 {
41+
return true
42+
}
43+
for _, filter := range debugFilters {
44+
if strings.Contains(method, filter) {
45+
return true
46+
}
47+
}
48+
return false
49+
}
50+
51+
func unaryLoggerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
52+
if !logSelector(info.FullMethod) {
53+
return handler(ctx, req)
54+
}
55+
fmt.Println("CALLED:", info.FullMethod)
56+
log(true, req)
57+
resp, err := handler(ctx, req)
58+
logError(err)
59+
log(false, resp)
60+
fmt.Println()
61+
return resp, err
62+
}
63+
64+
func streamLoggerInterceptor(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
65+
if !logSelector(info.FullMethod) {
66+
return handler(srv, stream)
67+
}
68+
streamReq := ""
69+
if info.IsClientStream {
70+
streamReq = "STREAM_REQ "
71+
}
72+
if info.IsServerStream {
73+
streamReq += "STREAM_RESP"
74+
}
75+
fmt.Println("CALLED:", info.FullMethod, streamReq)
76+
err := handler(srv, &loggingServerStream{ServerStream: stream})
77+
logError(err)
78+
fmt.Println()
79+
return err
80+
}
81+
82+
type loggingServerStream struct {
83+
grpc.ServerStream
84+
}
85+
86+
func (l *loggingServerStream) RecvMsg(m interface{}) error {
87+
err := l.ServerStream.RecvMsg(m)
88+
logError(err)
89+
log(true, m)
90+
return err
91+
}
92+
93+
func (l *loggingServerStream) SendMsg(m interface{}) error {
94+
err := l.ServerStream.SendMsg(m)
95+
logError(err)
96+
log(false, m)
97+
return err
98+
}

i18n/data/en.po

+12-4
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,10 @@ msgstr "Disable completion description for shells that support it"
549549
msgid "Disconnected"
550550
msgstr "Disconnected"
551551

552+
#: cli/daemon/daemon.go:60
553+
msgid "Display only the provided gRPC calls"
554+
msgstr "Display only the provided gRPC calls"
555+
552556
#: cli/lib/install.go:49
553557
msgid "Do not install dependencies."
554558
msgstr "Do not install dependencies."
@@ -586,6 +590,10 @@ msgstr "Downloads one or more cores and corresponding tool dependencies."
586590
msgid "Downloads one or more libraries without installing them."
587591
msgstr "Downloads one or more libraries without installing them."
588592

593+
#: cli/daemon/daemon.go:59
594+
msgid "Enable debug logging of gRPC calls"
595+
msgstr "Enable debug logging of gRPC calls"
596+
589597
#: cli/lib/install.go:51
590598
msgid "Enter a path to zip file"
591599
msgstr "Enter a path to zip file"
@@ -1019,19 +1027,19 @@ msgstr "Failed to create data directory"
10191027
msgid "Failed to create downloads directory"
10201028
msgstr "Failed to create downloads directory"
10211029

1022-
#: cli/daemon/daemon.go:114
1030+
#: cli/daemon/daemon.go:124
10231031
msgid "Failed to listen on TCP port: %[1]s. %[2]s is an invalid port."
10241032
msgstr "Failed to listen on TCP port: %[1]s. %[2]s is an invalid port."
10251033

1026-
#: cli/daemon/daemon.go:108
1034+
#: cli/daemon/daemon.go:118
10271035
msgid "Failed to listen on TCP port: %[1]s. %[2]s is unknown name."
10281036
msgstr "Failed to listen on TCP port: %[1]s. %[2]s is unknown name."
10291037

1030-
#: cli/daemon/daemon.go:123
1038+
#: cli/daemon/daemon.go:133
10311039
msgid "Failed to listen on TCP port: %[1]s. Unexpected error: %[2]v"
10321040
msgstr "Failed to listen on TCP port: %[1]s. Unexpected error: %[2]v"
10331041

1034-
#: cli/daemon/daemon.go:120
1042+
#: cli/daemon/daemon.go:130
10351043
msgid "Failed to listen on TCP port: %s. Address already in use."
10361044
msgstr "Failed to listen on TCP port: %s. Address already in use."
10371045

i18n/rice-box.go

+4-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)