1
2
3
4
5 package upload
6
7 import (
8 "fmt"
9 "io"
10 "log"
11 "os"
12 "path"
13 "path/filepath"
14 "runtime/debug"
15 "strings"
16 "time"
17
18 "golang.org/x/telemetry/internal/configstore"
19 "golang.org/x/telemetry/internal/telemetry"
20 )
21
22
23
24
25 type RunConfig struct {
26 TelemetryDir string
27 UploadURL string
28 LogWriter io.Writer
29 Env []string
30 StartTime time.Time
31 }
32
33
34 func Run(config RunConfig) error {
35 defer func() {
36 if err := recover(); err != nil {
37 log.Printf("upload recover: %v", err)
38 }
39 }()
40 uploader, err := newUploader(config)
41 if err != nil {
42 return err
43 }
44 defer uploader.Close()
45 return uploader.Run()
46 }
47
48
49
50 type uploader struct {
51
52 config *telemetry.UploadConfig
53 configVersion string
54 dir telemetry.Dir
55
56 uploadServerURL string
57 startTime time.Time
58
59 cache parsedCache
60
61 logFile *os.File
62 logger *log.Logger
63 }
64
65
66
67
68
69 func newUploader(rcfg RunConfig) (*uploader, error) {
70
71 var dir telemetry.Dir
72 if rcfg.TelemetryDir != "" {
73 dir = telemetry.NewDir(rcfg.TelemetryDir)
74 } else {
75 dir = telemetry.Default
76 }
77
78
79 uploadURL := rcfg.UploadURL
80 if uploadURL == "" {
81 uploadURL = "https://telemetry.go.dev/upload"
82 }
83
84
85
86
87
88
89
90
91
92 var logWriters []io.Writer
93 logFile, err := debugLogFile(dir.DebugDir())
94 if err != nil {
95 logFile = nil
96 }
97 if logFile != nil {
98 logWriters = append(logWriters, logFile)
99 }
100 if rcfg.LogWriter != nil {
101 logWriters = append(logWriters, rcfg.LogWriter)
102 }
103 var logWriter io.Writer
104 switch len(logWriters) {
105 case 0:
106 logWriter = io.Discard
107 case 1:
108 logWriter = logWriters[0]
109 default:
110 logWriter = io.MultiWriter(logWriters...)
111 }
112 logger := log.New(logWriter, "", log.Ltime|log.Lmicroseconds|log.Lshortfile)
113
114
115 var (
116 config *telemetry.UploadConfig
117 configVersion string
118 )
119
120 if mode, _ := dir.Mode(); mode == "on" {
121
122
123
124
125
126 config, configVersion, err = configstore.Download("latest", rcfg.Env)
127 if err != nil {
128 return nil, err
129 }
130 } else {
131 config = &telemetry.UploadConfig{}
132 configVersion = "v0.0.0-0"
133 }
134
135
136 startTime := time.Now().UTC()
137 if !rcfg.StartTime.IsZero() {
138 startTime = rcfg.StartTime
139 }
140
141 return &uploader{
142 config: config,
143 configVersion: configVersion,
144 dir: dir,
145 uploadServerURL: uploadURL,
146 startTime: startTime,
147
148 logFile: logFile,
149 logger: logger,
150 }, nil
151 }
152
153
154 func (u *uploader) Close() error {
155 if u.logFile == nil {
156 return nil
157 }
158 return u.logFile.Close()
159 }
160
161
162 func (u *uploader) Run() error {
163 if telemetry.DisabledOnPlatform {
164 return nil
165 }
166 todo := u.findWork()
167 ready, err := u.reports(&todo)
168 if err != nil {
169 u.logger.Printf("Error building reports: %v", err)
170 return fmt.Errorf("reports failed: %v", err)
171 }
172 u.logger.Printf("Uploading %d reports", len(ready))
173 for _, f := range ready {
174 u.uploadReport(f)
175 }
176 return nil
177 }
178
179
180
181 func debugLogFile(debugDir string) (*os.File, error) {
182 fd, err := os.Stat(debugDir)
183 if os.IsNotExist(err) {
184 return nil, nil
185 }
186 if err != nil {
187 return nil, err
188 }
189 if !fd.IsDir() {
190 return nil, fmt.Errorf("debug path %q is not a directory", debugDir)
191 }
192 info, ok := debug.ReadBuildInfo()
193 if !ok {
194 return nil, fmt.Errorf("no build info")
195 }
196 year, month, day := time.Now().UTC().Date()
197 goVers := info.GoVersion
198
199 words := strings.Fields(goVers)
200 goVers = words[0]
201 progPkgPath := info.Path
202 if progPkgPath == "" {
203 progPkgPath = strings.TrimSuffix(filepath.Base(os.Args[0]), ".exe")
204 }
205 prog := path.Base(progPkgPath)
206 progVers := info.Main.Version
207 if progVers == "(devel)" {
208 progVers = "devel"
209 }
210 logBase := strings.ReplaceAll(
211 fmt.Sprintf("%s-%s-%s-%4d%02d%02d-%d.log", prog, progVers, goVers, year, month, day, os.Getpid()),
212 " ", "")
213 fname := filepath.Join(debugDir, logBase)
214 if _, err := os.Stat(fname); err == nil {
215
216 return nil, nil
217 }
218 f, err := os.OpenFile(fname, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
219 if err != nil {
220 if os.IsExist(err) {
221 return nil, nil
222 }
223 return nil, err
224 }
225 return f, nil
226 }
227
View as plain text