Skip to content

Commit 44502fb

Browse files
committed
internal/telemetry: centralize telemetry settings (directories and mode)
The LocalDir/UploadDir/Enabled in types.go are computed based on internal/telemetry.{LocalDir,UploadDir,ModeFile}. Previously, the init function in types.go created the telemetry directories, which means, simply depending on the package creates directories in user's system default configuration location. For example, by running tests in this repo, users may end up having telemetry directories. Avoid this eager directory creation. The previous behavior allowed a process to completely skip telemetry collection if there is no writable disk space, even when the user didn't turn off the telemetry. However, we guess os.UserConfigDir misconfiguration is rare, so we drop this behavior. We introduce the undocumented EXP_GOTELEMETRYDIR env var here. A process' default telemetry setting uses directories and files under a directory determined by the EXP_GOTELEMETRYDIR env var, or os.UserConfigDir. EXP_GOTELEMETRYDIR env var allows us to test instrumented binary's behavior without polluting the user's default telemetry mode file or data collection directory. This environment variable is intentionally undocumented because this may change or get dropped. And, rename LookupMode to Mode. Updates golang/go#60967 Change-Id: Ie8ad9bf52a9b5d7fe70b92f6c8ccdeda6dbb1a90 Reviewed-on: https://go-review.googlesource.com/c/telemetry/+/506916 Run-TryBot: Hyang-Ah Hana Kim <[email protected]> Reviewed-by: Jamal Carvalho <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 931bd62 commit 44502fb

File tree

4 files changed

+84
-66
lines changed

4 files changed

+84
-66
lines changed

cmd/gotelemetry/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func main() {
2020

2121
args := flag.Args()
2222
if len(args) == 0 {
23-
fmt.Println(telemetry.LookupMode())
23+
fmt.Println(telemetry.Mode())
2424
return
2525
}
2626
switch cmd := args[0]; cmd {

internal/telemetry/mode.go

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,60 @@ import (
1313
"strings"
1414
)
1515

16-
var userConfigDir = os.UserConfigDir
16+
// The followings are the process' default Settings.
17+
// The values are subdirectories and a file under
18+
// os.UserConfigDir()/go/telemetry.
19+
// For convenience, each field is made to global
20+
// and they are not supposed to be changed.
21+
var (
22+
// Default directory containing count files and local reports (not yet uploaded)
23+
LocalDir string
24+
// Default directory containing uploaded reports.
25+
UploadDir string
26+
// Default file path that holds the telemetry mode info.
27+
ModeFile ModeFilePath
28+
)
1729

18-
// filename returns the default telemetry mode file name.
19-
func filename() (string, error) {
20-
cfgDir, err := userConfigDir()
21-
if err != nil {
22-
return "", err
23-
}
24-
// TODO(hyangah): should we consider GOTELEMETRYDIR?
30+
// ModeFilePath is the telemetry mode file path with methods to manipulate the file contents.
31+
type ModeFilePath string
2532

26-
return filepath.Join(cfgDir, "go", "telemetry", "mode"), nil
33+
func init() {
34+
// First check if the undocumented EXP_GOTELEMETRYDIR
35+
// env var is set. This variable name may change or
36+
// get abandoned in the future.
37+
// This knob helps running instrumented binaries in test
38+
// without affecting or being affected by the system
39+
// default telemetry settings.
40+
gotelemetrydir := os.Getenv("EXP_GOTELEMETRYDIR")
41+
if gotelemetrydir == "" {
42+
cfgDir, err := os.UserConfigDir()
43+
if err != nil {
44+
return
45+
}
46+
gotelemetrydir = filepath.Join(cfgDir, "go", "telemetry")
47+
}
48+
LocalDir = filepath.Join(gotelemetrydir, "local")
49+
UploadDir = filepath.Join(gotelemetrydir, "upload")
50+
ModeFile = ModeFilePath(filepath.Join(gotelemetrydir, "mode"))
2751
}
2852

2953
// SetMode updates the telemetry mode with the given mode.
3054
// Acceptable values for mode are "on", "off", "local", or https:// urls.
3155
func SetMode(mode string) error {
56+
return ModeFile.SetMode(mode)
57+
}
58+
59+
func (m ModeFilePath) SetMode(mode string) error {
3260
switch mode {
3361
case "on", "off", "local":
3462
default:
3563
if !strings.HasPrefix(mode, "https://") {
3664
return errors.New("invalid telemetry mode value")
3765
}
3866
}
39-
fname, err := filename()
40-
if err != nil {
41-
return fmt.Errorf("cannot create a telemetry mode file: %w", err)
67+
fname := string(m)
68+
if fname == "" {
69+
return fmt.Errorf("cannot determine telemetry mode file name")
4270
}
4371
if err := os.MkdirAll(filepath.Dir(fname), 0755); err != nil {
4472
return fmt.Errorf("cannot create a telemetry mode file: %w", err)
@@ -47,11 +75,15 @@ func SetMode(mode string) error {
4775
return os.WriteFile(fname, data, 0666)
4876
}
4977

50-
// LookupMode returns the current telemetry mode.
51-
func LookupMode() string {
52-
fname, err := filename()
53-
if err != nil {
54-
return "local" // default
78+
// Mode returns the current telemetry mode.
79+
func Mode() string {
80+
return ModeFile.Mode()
81+
}
82+
83+
func (m ModeFilePath) Mode() string {
84+
fname := string(m)
85+
if fname == "" {
86+
return "off" // it's likely LocalDir/UploadDir are empty too. Turn off telemetry.
5587
}
5688
data, err := os.ReadFile(fname)
5789
if err != nil {

internal/telemetry/mode_test.go

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,42 @@
66
package telemetry
77

88
import (
9+
"fmt"
910
"os"
11+
"path/filepath"
1012
"testing"
1113
)
1214

15+
func TestTelemetryDefault(t *testing.T) {
16+
gotelemetrydir := os.Getenv("GOTELEMETRYDIR")
17+
if _, err := os.UserConfigDir(); gotelemetrydir == "" && err != nil {
18+
if LocalDir != "" || UploadDir != "" || ModeFile != "" {
19+
t.Errorf("DefaultSetting: (%q, %q, %q), want non-empty LocalDir/UploadDir/ModeFile", LocalDir, UploadDir, ModeFile)
20+
}
21+
} else {
22+
if LocalDir == "" || UploadDir == "" || ModeFile == "" {
23+
t.Errorf("DefaultSetting: (%q, %q, %q), want non-empty LocalDir/UploadDir/ModeFile", LocalDir, UploadDir, ModeFile)
24+
}
25+
}
26+
}
1327
func TestTelemetryModeWithNoModeConfig(t *testing.T) {
14-
defer func() { userConfigDir = os.UserConfigDir }()
15-
1628
tmp := t.TempDir()
17-
userConfigDir = func() (string, error) { return tmp, nil }
18-
19-
got := LookupMode()
20-
if got != "local" {
21-
t.Fatalf("LookupMode() = %q, want local", got)
29+
tests := []struct {
30+
modefile ModeFilePath
31+
want string
32+
}{
33+
{ModeFilePath(filepath.Join(tmp, "mode")), "local"},
34+
{"", "off"},
35+
}
36+
for _, tt := range tests {
37+
if got := tt.modefile.Mode(); got != tt.want {
38+
t.Logf("Mode file: %q", tt.modefile)
39+
t.Errorf("Mode() = %v, want %v", got, tt.want)
40+
}
2241
}
2342
}
2443

2544
func TestTelemetryMode(t *testing.T) {
26-
defer func() { userConfigDir = os.UserConfigDir }()
27-
28-
tmp := t.TempDir()
29-
userConfigDir = func() (string, error) { return tmp, nil }
30-
3145
tests := []struct {
3246
in string
3347
wantErr bool // want error when setting.
@@ -40,16 +54,18 @@ func TestTelemetryMode(t *testing.T) {
4054
{"bogus", true},
4155
{"", true},
4256
}
43-
for _, tt := range tests {
57+
tmp := t.TempDir()
58+
for i, tt := range tests {
4459
t.Run("mode="+tt.in, func(t *testing.T) {
45-
setErr := SetMode(tt.in)
60+
modefile := ModeFilePath(filepath.Join(tmp, fmt.Sprintf("modefile%d", i)))
61+
setErr := modefile.SetMode(tt.in)
4662
if (setErr != nil) != tt.wantErr {
4763
t.Fatalf("Set() error = %v, wantErr %v", setErr, tt.wantErr)
4864
}
4965
if setErr != nil {
5066
return
5167
}
52-
if got := LookupMode(); got != tt.in {
68+
if got := modefile.Mode(); got != tt.in {
5369
t.Errorf("LookupMode() = %q, want %q", got, tt.in)
5470
}
5571
})

types.go

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
package telemetry
66

77
import (
8-
"os"
9-
"path/filepath"
10-
118
"golang.org/x/telemetry/internal/telemetry"
129
)
1310

@@ -59,36 +56,9 @@ type ProgramReport struct {
5956

6057
var (
6158
// directory containing count files and local (not to be uploaded) reports
62-
LocalDir string
59+
LocalDir = telemetry.LocalDir
6360
// directory containing uploaded reports
64-
UploadDir string
61+
UploadDir = telemetry.UploadDir
6562
// whether telemetry is enabled
66-
Enabled bool
63+
Enabled bool = telemetry.Mode() != "off"
6764
)
68-
69-
// init() sets LocalDir and UploadDir. Users must not change these.
70-
// If the directories cannot be found or set, telemetry is disabled.
71-
func init() {
72-
mode := telemetry.LookupMode()
73-
if mode == "off" {
74-
return
75-
}
76-
77-
env, err := os.UserConfigDir()
78-
if err != nil {
79-
return
80-
}
81-
env = filepath.Join(env, "go", "telemetry")
82-
83-
l := filepath.Join(env, "local")
84-
u := filepath.Join(env, "upload")
85-
if err := os.MkdirAll(l, 0755); err != nil {
86-
return
87-
}
88-
if err := os.MkdirAll(u, 0755); err != nil {
89-
return
90-
}
91-
LocalDir = l
92-
UploadDir = u
93-
Enabled = true
94-
}

0 commit comments

Comments
 (0)