Skip to content

Commit

Permalink
Export Prometheus telemetry in daemon mode (arduino#573)
Browse files Browse the repository at this point in the history
* Apply cosmetics

* Implement ugly telemetry POC

* Added prefix and moved Insrumentation inside the command package

* Refactor the telemetry module

* Implement configuration via Viper

* Add stats flush in case of a not daemonized cli daemon proces

* Add repertory to store installation id and secret

* Repertory force write

* Cosmetics

* Use viper config for repertory dir

* Add test for repertory file creation

* Add testing for telemetry deaemon and repertory

* Wait for repertory and kill daemon

* Updated pyinvoke to use async feature to test the daemon

* Updated daemon test timeout

* Cosmetics

* Set getDefaultArduinoDataDir as unexported

* Cosmetics

* Cosmetics

* Cosmetics

* Lint on repertory module

* Set SIGTERM as kill signal in case of win platform to overcome pyinvoke bug

* Import platform as a module

* Reverse platform if for signal value

* Extract pid value

* Remove print leftover

* Add better error handling in repertory creation

* Update docs with old README extract

* Remove telemetry.pattern setting from docs

* Remove serverPattern config option for telemetry

* Upgrade viper to 1.6.2

* Defer stats Increment in compile command and explicit set for success/failure

* Fix board list help message

* Implement stats flush anyway to leverage module no-op in case of no handler configured

* Rename "repertory" module in "inventory" and refactor Sanitize function

* Sanitize ExportFile in command/compile

* Refactor daemon start fixture to include daemon process cleanup

* Use defer function to push success tag correctly updated

* Use named return parameters to handle success tagging for a command stat
  • Loading branch information
Roberto Sora authored Mar 10, 2020
1 parent 8483cb2 commit 2791756
Show file tree
Hide file tree
Showing 18 changed files with 479 additions and 28 deletions.
2 changes: 1 addition & 1 deletion cli/board/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func initListCommand() *cobra.Command {
}

listCommand.Flags().StringVar(&listFlags.timeout, "timeout", "0s",
"The timeout of the search of connected devices, try to increase it if your board is not found (e.g. to 10s).")
"The connected devices search timeout, raise it if your board doesn't show up (e.g. to 10s).")
return listCommand
}

Expand Down
4 changes: 4 additions & 0 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"github.com/arduino/arduino-cli/cli/upload"
"github.com/arduino/arduino-cli/cli/version"
"github.com/arduino/arduino-cli/configuration"
"github.com/arduino/arduino-cli/inventory"
"github.com/mattn/go-colorable"
"github.com/rifflock/lfshook"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -167,6 +168,9 @@ func preRun(cmd *cobra.Command, args []string) {
configuration.Init(configPath)
configFile := viper.ConfigFileUsed()

// initialize inventory
inventory.Init(viper.GetString("directories.Data"))

//
// Prepare logging
//
Expand Down
31 changes: 24 additions & 7 deletions cli/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import (
srv_debug "github.com/arduino/arduino-cli/rpc/debug"
srv_monitor "github.com/arduino/arduino-cli/rpc/monitor"
srv_settings "github.com/arduino/arduino-cli/rpc/settings"
"github.com/arduino/arduino-cli/telemetry"
"github.com/segmentio/stats/v4"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand All @@ -59,16 +61,29 @@ func NewCommand() *cobra.Command {
var daemonize bool

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

if viper.GetBool("telemetry.enabled") {
telemetry.Activate("daemon")
stats.Incr("daemon", stats.T("success", "true"))
defer stats.Flush()
}

port := viper.GetString("daemon.port")
s := grpc.NewServer()

// register the commands service
headers := http.Header{"User-Agent": []string{
fmt.Sprintf("%s/%s daemon (%s; %s; %s) Commit:%s",
globals.VersionInfo.Application,
globals.VersionInfo.VersionString,
runtime.GOARCH, runtime.GOOS,
runtime.Version(), globals.VersionInfo.Commit)}}
// Compose user agent header
headers := http.Header{
"User-Agent": []string{
fmt.Sprintf("%s/%s daemon (%s; %s; %s) Commit:%s",
globals.VersionInfo.Application,
globals.VersionInfo.VersionString,
runtime.GOARCH,
runtime.GOOS,
runtime.Version(),
globals.VersionInfo.Commit),
},
}
// Register the commands service
srv_commands.RegisterArduinoCoreServer(s, &daemon.ArduinoCoreServerImpl{
DownloaderHeaders: headers,
VersionString: globals.VersionInfo.VersionString,
Expand All @@ -88,6 +103,8 @@ func runDaemonCommand(cmd *cobra.Command, args []string) {
go func() {
// Stdin is closed when the controlling parent process ends
_, _ = io.Copy(ioutil.Discard, os.Stdin)
// Flush telemetry stats (this is a no-op if telemetry is disabled)
stats.Flush()
os.Exit(0)
}()
}
Expand Down
5 changes: 2 additions & 3 deletions commands/board/attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,12 @@ import (
"github.com/arduino/arduino-cli/arduino/sketches"
"github.com/arduino/arduino-cli/commands"
rpc "github.com/arduino/arduino-cli/rpc/commands"
discovery "github.com/arduino/board-discovery"
paths "github.com/arduino/go-paths-helper"
"github.com/arduino/board-discovery"
"github.com/arduino/go-paths-helper"
)

// Attach FIXMEDOC
func Attach(ctx context.Context, req *rpc.BoardAttachReq, taskCB commands.TaskProgressCB) (*rpc.BoardAttachResp, error) {

pm := commands.GetPackageManager(req.GetInstance().GetId())
if pm == nil {
return nil, errors.New("invalid instance")
Expand Down
14 changes: 13 additions & 1 deletion commands/board/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/arduino/arduino-cli/commands"
rpc "github.com/arduino/arduino-cli/rpc/commands"
"github.com/pkg/errors"
"github.com/segmentio/stats/v4"
"github.com/sirupsen/logrus"
)

Expand Down Expand Up @@ -100,10 +101,21 @@ func identifyViaCloudAPI(port *commands.BoardPort) ([]*rpc.BoardListItem, error)
}

// List FIXMEDOC
func List(instanceID int32) ([]*rpc.DetectedPort, error) {
func List(instanceID int32) (r []*rpc.DetectedPort, e error) {
m.Lock()
defer m.Unlock()

tags := map[string]string{}
// Use defer func() to evaluate tags map when function returns
// and set success flag inspecting the error named return parameter
defer func() {
tags["success"] = "true"
if e != nil {
tags["success"] = "false"
}
stats.Incr("compile", stats.M(tags)...)
}()

pm := commands.GetPackageManager(instanceID)
if pm == nil {
return nil, errors.New("invalid instance")
Expand Down
32 changes: 30 additions & 2 deletions commands/compile/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"io"
"path/filepath"
"sort"
"strconv"
"strings"

"github.com/arduino/arduino-cli/arduino/cores"
Expand All @@ -33,14 +34,42 @@ import (
"github.com/arduino/arduino-cli/legacy/builder/i18n"
"github.com/arduino/arduino-cli/legacy/builder/types"
rpc "github.com/arduino/arduino-cli/rpc/commands"
"github.com/arduino/arduino-cli/telemetry"
paths "github.com/arduino/go-paths-helper"
properties "github.com/arduino/go-properties-orderedmap"
"github.com/segmentio/stats/v4"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
)

// Compile FIXMEDOC
func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.Writer, debug bool) (*rpc.CompileResp, error) {
func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.Writer, debug bool) (r *rpc.CompileResp, e error) {

tags := map[string]string{
"fqbn": req.Fqbn,
"sketchPath": telemetry.Sanitize(req.SketchPath),
"showProperties": strconv.FormatBool(req.ShowProperties),
"preprocess": strconv.FormatBool(req.Preprocess),
"buildProperties": strings.Join(req.BuildProperties, ","),
"warnings": req.Warnings,
"verbose": strconv.FormatBool(req.Verbose),
"quiet": strconv.FormatBool(req.Quiet),
"vidPid": req.VidPid,
"exportFile": telemetry.Sanitize(req.ExportFile),
"jobs": strconv.FormatInt(int64(req.Jobs), 10),
"libraries": strings.Join(req.Libraries, ","),
}

// Use defer func() to evaluate tags map when function returns
// and set success flag inspecting the error named return parameter
defer func() {
tags["success"] = "true"
if e != nil {
tags["success"] = "false"
}
stats.Incr("compile", stats.M(tags)...)
}()

pm := commands.GetPackageManager(req.GetInstance().GetId())
if pm == nil {
return nil, errors.New("invalid instance")
Expand Down Expand Up @@ -224,6 +253,5 @@ func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.W
}

logrus.Tracef("Compile %s for %s successful", sketch.Name, fqbnIn)

return &rpc.CompileResp{}, nil
}
4 changes: 4 additions & 0 deletions configuration/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@ func setDefaults(dataDir, userDir string) {

// daemon settings
viper.SetDefault("daemon.port", "50051")

//telemetry settings
viper.SetDefault("telemetry.enabled", true)
viper.SetDefault("telemetry.addr", ":9090")
}
37 changes: 37 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,40 @@ FTDebouncer@1.3.0 downloaded
Installing FTDebouncer@1.3.0...
Installed FTDebouncer@1.3.0
```


Using the ``daemon`` mode and the gRPC interface
------------------------------------------------

Arduino CLI can be launched as a gRPC server via the `daemon` command.

The [client_example] folder contains a sample client code that shows how to
interact with the gRPC server. Available services and messages are detailed
in the [gRPC reference] pages.


To provide observability for the gRPC server activities besides logs,
the `daemon` mode activates and exposes by default a [Prometheus](https://prometheus.io/)
endpoint (http://localhost:9090/metrics) that can be fetched for
telemetry data like:

```text
# TYPE daemon_compile counter
daemon_compile{buildProperties="",exportFile="",fqbn="arduino:samd:mkr1000",installationID="ed6f1f22-1fbe-4b1f-84be-84d035b6369c",jobs="0",libraries="",preprocess="false",quiet="false",showProperties="false",sketchPath="5ff767c6fa5a91230f5cb4e267c889aa61489ab2c4f70f35f921f934c1462cb6",success="true",verbose="true",vidPid="",warnings=""} 1 1580385724726
# TYPE daemon_board_list counter
daemon_board_list{installationID="ed6f1f22-1fbe-4b1f-84be-84d035b6369c",success="true"} 1 1580385724833
```

The telemetry settings are exposed via the ``telemetry`` section
in the CLI configuration:

```yaml
telemetry:
enabled: true
addr: :9090
```
[client_example]: https://github.com/arduino/arduino-cli/blob/master/client_example
[gRPC reference]: /rpc/commands
[Prometheus]: https://prometheus.io/
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ require (
github.com/fluxio/multierror v0.0.0-20160419044231-9c68d39025e5 // indirect
github.com/fsnotify/fsnotify v1.4.7
github.com/go-errors/errors v1.0.1
github.com/gofrs/uuid v3.2.0+incompatible
github.com/golang/protobuf v1.3.3
github.com/h2non/filetype v1.0.8 // indirect
github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 // indirect
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect
github.com/juju/testing v0.0.0-20190429233213-dfc56b8c09fc // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.2
github.com/mattn/go-runewidth v0.0.2 // indirect
github.com/miekg/dns v1.0.5 // indirect
Expand All @@ -31,11 +31,11 @@ require (
github.com/pmylund/sortutil v0.0.0-20120526081524-abeda66eb583
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
github.com/schollz/closestmatch v2.1.0+incompatible
github.com/segmentio/stats/v4 v4.5.3
github.com/sirupsen/logrus v1.4.2
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
github.com/spf13/cobra v0.0.5
github.com/spf13/jwalterweatherman v1.0.0
github.com/spf13/viper v1.3.2
github.com/spf13/viper v1.6.2
github.com/stretchr/testify v1.4.0
go.bug.st/cleanup v1.0.0
go.bug.st/downloader v1.1.0
Expand All @@ -49,5 +49,5 @@ require (
google.golang.org/grpc v1.27.0
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce // indirect
gopkg.in/yaml.v2 v2.2.2
gopkg.in/yaml.v2 v2.2.4
)
Loading

0 comments on commit 2791756

Please sign in to comment.