telemetry support
Fixes #832 Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
BIN
telemetry/telemetry
Executable file
BIN
telemetry/telemetry
Executable file
Binary file not shown.
156
telemetry/telemetry.go
Normal file
156
telemetry/telemetry.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package telemetry
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/rs/zerolog/log"
|
||||
"go.dagger.io/dagger/version"
|
||||
)
|
||||
|
||||
const (
|
||||
apiKey = "cb9777c166aefe4b77b31f961508191c"
|
||||
telemetryURL = "https://t.dagger.io/v1"
|
||||
)
|
||||
|
||||
type Property struct {
|
||||
Name string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func TrackAsync(ctx context.Context, eventName string, properties ...*Property) chan struct{} {
|
||||
doneCh := make(chan struct{}, 1)
|
||||
go func() {
|
||||
defer close(doneCh)
|
||||
Track(ctx, eventName, properties...)
|
||||
}()
|
||||
return doneCh
|
||||
}
|
||||
|
||||
func Track(ctx context.Context, eventName string, properties ...*Property) {
|
||||
lg := log.Ctx(ctx).
|
||||
With().
|
||||
Str("event", eventName).
|
||||
Logger()
|
||||
|
||||
if telemetryDisabled() || isCI() {
|
||||
return
|
||||
}
|
||||
|
||||
deviceID, err := getDeviceID()
|
||||
if err != nil {
|
||||
lg.Trace().Err(err).Msg("failed to get device id")
|
||||
return
|
||||
}
|
||||
|
||||
// Base properties
|
||||
props := map[string]interface{}{
|
||||
"dagger_version": version.Version,
|
||||
"dagger_revision": version.Revision,
|
||||
"os": runtime.GOOS,
|
||||
"arch": runtime.GOARCH,
|
||||
}
|
||||
|
||||
// Merge extra properties
|
||||
for _, p := range properties {
|
||||
props[p.Name] = p.Value
|
||||
}
|
||||
lg = lg.With().Fields(props).Logger()
|
||||
|
||||
ev := &event{
|
||||
DeviceID: deviceID,
|
||||
EventType: eventName,
|
||||
Time: time.Now().Unix(),
|
||||
AppVersion: version.Version,
|
||||
OSName: runtime.GOOS,
|
||||
Platform: runtime.GOARCH,
|
||||
IP: "$remote", // Use "$remote" to use the IP address on the upload request
|
||||
EventProperties: props,
|
||||
}
|
||||
|
||||
p := &payload{
|
||||
APIKey: apiKey,
|
||||
Events: []*event{ev},
|
||||
}
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
if err := json.NewEncoder(b).Encode(p); err != nil {
|
||||
lg.Trace().Err(err).Msg("failed to encode payload")
|
||||
return
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", telemetryURL, b)
|
||||
if err != nil {
|
||||
lg.Trace().Err(err).Msg("failed to prepare request")
|
||||
}
|
||||
|
||||
req.Header = map[string][]string{
|
||||
"Content-Type": {"application/json"},
|
||||
"Accept": {"*/*"},
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
lg.Trace().Err(err).Msg("failed to send telemetry event")
|
||||
return
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
lg.Trace().Str("status", resp.Status).Msg("telemetry request failed")
|
||||
return
|
||||
}
|
||||
|
||||
lg.Trace().Msg("telemetry event")
|
||||
}
|
||||
|
||||
type payload struct {
|
||||
APIKey string `json:"api_key,omitempty"`
|
||||
Events []*event `json:"events"`
|
||||
}
|
||||
|
||||
type event struct {
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
DeviceID string `json:"device_id,omitempty"`
|
||||
EventType string `json:"event_type,omitempty"`
|
||||
Time int64 `json:"time,omitempty"`
|
||||
AppVersion string `json:"app_version,omitempty"`
|
||||
Platform string `json:"platform,omitempty"`
|
||||
OSName string `json:"os_name,omitempty"`
|
||||
OSVersion string `json:"os_version,omitempty"`
|
||||
IP string `json:"ip,omitempty"`
|
||||
EventProperties map[string]interface{} `json:"event_properties,omitempty"`
|
||||
}
|
||||
|
||||
func isCI() bool {
|
||||
return os.Getenv("CI") != "" || // GitHub Actions, Travis CI, CircleCI, Cirrus CI, GitLab CI, AppVeyor, CodeShip, dsari
|
||||
os.Getenv("BUILD_NUMBER") != "" || // Jenkins, TeamCity
|
||||
os.Getenv("RUN_ID") != "" // TaskCluster, dsari
|
||||
}
|
||||
|
||||
func telemetryDisabled() bool {
|
||||
return os.Getenv("DAGGER_TELEMETRY_DISABLE") != "" || // dagger specific env
|
||||
os.Getenv("DO_NOT_TRACK") != "" // https://consoledonottrack.com/
|
||||
}
|
||||
|
||||
func getDeviceID() (string, error) {
|
||||
idFile, err := homedir.Expand("~/.config/dagger/cli_id")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
id, err := os.ReadFile(idFile)
|
||||
if err != nil {
|
||||
id = []byte(uuid.New().String())
|
||||
if err := os.WriteFile(idFile, id, 0600); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return string(id), nil
|
||||
}
|
Reference in New Issue
Block a user