Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/local-app/pkg/telemetry/telemetry.go
2500 views
1
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License-AGPL.txt in the project root for license information.
4
5
package telemetry
6
7
import (
8
"crypto/sha256"
9
"errors"
10
"fmt"
11
"io"
12
"log"
13
"log/slog"
14
"math/rand"
15
"net"
16
"os"
17
"runtime"
18
"strings"
19
"time"
20
21
"github.com/gitpod-io/local-app/pkg/prettyprint"
22
segment "github.com/segmentio/analytics-go/v3"
23
"github.com/spf13/cobra"
24
"golang.org/x/exp/slices"
25
)
26
27
var opts struct {
28
Enabled bool
29
Identity string
30
Version string
31
32
client segment.Client
33
}
34
35
// Init initializes the telemetry
36
func Init(enabled bool, identity, version string, logLevel slog.Level, host string) {
37
opts.Enabled = enabled
38
if !enabled {
39
return
40
}
41
42
opts.Version = version
43
opts.Identity = identity
44
45
var logger segment.Logger
46
if logLevel == slog.LevelDebug {
47
logger = segment.StdLogger(log.New(os.Stderr, "telemetry ", log.LstdFlags))
48
} else {
49
// we don't want to log anything
50
log := log.New(os.Stderr, "telemetry ", log.LstdFlags)
51
log.SetOutput(io.Discard)
52
logger = segment.StdLogger(log)
53
}
54
opts.client, _ = segment.NewWithConfig("untrusted-dummy-key", segment.Config{
55
Logger: logger,
56
Endpoint: host + "/analytics",
57
})
58
}
59
60
// DoNotTrack returns true if the user opted out of telemetry
61
// Implements the https://consoledonottrack.com/ proposal.
62
func DoNotTrack() bool {
63
return os.Getenv("DO_NOT_TRACK") == "1"
64
}
65
66
// GenerateIdentity generates an identity using the machine's MAC address or a random value
67
func GenerateIdentity() string {
68
var addr string
69
interfaces, err := net.Interfaces()
70
if err == nil {
71
for _, i := range interfaces {
72
addr = i.HardwareAddr.String()
73
if i.Flags&net.FlagUp != 0 && addr != "" {
74
break
75
}
76
}
77
}
78
if addr == "" {
79
letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
80
b := make([]rune, 32)
81
for i := range b {
82
b[i] = letters[rand.Intn(len(letters))]
83
}
84
addr = string(b)
85
}
86
return fmt.Sprintf("%x", sha256.Sum256([]byte(addr)))
87
}
88
89
func Close() {
90
if opts.client != nil {
91
opts.client.Close()
92
}
93
}
94
95
// Identity returns the identity
96
func Identity() string {
97
return opts.Identity
98
}
99
100
// Enabled returns true if the telemetry is enabled
101
func Enabled() bool {
102
return opts.Enabled && opts.Identity != "" && opts.client != nil
103
}
104
105
func track(event string, props segment.Properties) {
106
if !Enabled() {
107
return
108
}
109
slog.Debug("tracking telemetry", "props", props, "event", event, "identity", opts.Identity)
110
111
err := opts.client.Enqueue(segment.Track{
112
AnonymousId: opts.Identity,
113
Event: event,
114
Timestamp: time.Now(),
115
Properties: props,
116
})
117
if err != nil {
118
slog.Debug("failed to track telemetry", "err", err)
119
}
120
}
121
122
// RecordCommand records the execution of a CLI command
123
func RecordCommand(cmd *cobra.Command) {
124
var command []string
125
for c := cmd; c != nil; c = c.Parent() {
126
command = append(command, c.Name())
127
}
128
slices.Reverse(command)
129
130
track("gitpodcli_command", defaultProperties().
131
Set("command", strings.Join(command, " ")))
132
}
133
134
// RecordError records an exception that occurred
135
func RecordError(err error) {
136
var exception *prettyprint.ErrSystemException
137
if !errors.As(err, &exception) {
138
return
139
}
140
141
track("gitpodcli_exception", defaultProperties().
142
Set("context", exception.Context).
143
Set("error", exception.Err.Error()))
144
}
145
146
func defaultProperties() segment.Properties {
147
return segment.NewProperties().
148
Set("goos", runtime.GOOS).
149
Set("goarch", runtime.GOARCH).
150
Set("version", opts.Version)
151
}
152
153