gitee.com/mirrors/gauge@v1.0.6/track/track.go (about) 1 // Copyright 2015 ThoughtWorks, Inc. 2 3 // This file is part of Gauge. 4 5 // Gauge is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 10 // Gauge is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 15 // You should have received a copy of the GNU General Public License 16 // along with Gauge. If not, see <http://www.gnu.org/licenses/>. 17 18 package track 19 20 import ( 21 "net/http" 22 "net/http/httputil" 23 "runtime" 24 "runtime/debug" 25 "strconv" 26 "strings" 27 "time" 28 29 "github.com/getgauge/gauge/env" 30 31 "fmt" 32 33 "os" 34 35 "sync" 36 37 "github.com/getgauge/gauge/config" 38 "github.com/getgauge/gauge/logger" 39 "github.com/getgauge/gauge/version" 40 ga "github.com/jpillora/go-ogle-analytics" 41 ) 42 43 const ( 44 gaTrackingID = "UA-54838477-1" 45 gaTestTrackingID = "UA-100778536-1" 46 appName = "Gauge Core" 47 consoleMedium = "console" 48 apiMedium = "api" 49 ciMedium = "CI" 50 timeout = 1 51 // GaugeTelemetryMessageHeading is the header printed for telemetry warning 52 GaugeTelemetryMessageHeading = ` 53 Telemetry 54 --------- 55 ` 56 // GaugeTelemetryMessage is the message printed when user has not explicitly opted in/out 57 // of telemetry. Printed only in CLI. 58 GaugeTelemetryMessage = `This installation of Gauge collects usage data in order to help us improve your experience. 59 The data is anonymous and doesn't include command-line arguments. 60 To turn this message off opt in or out by running 'gauge telemetry on' or 'gauge telemetry off'. 61 62 Read more about Gauge telemetry at https://gauge.org/telemetry 63 ` 64 // GaugeTelemetryMachineRedableMessage is the message printed when user has not explicitly opted in/out 65 // of telemetry. Printed only in CLI. 66 GaugeTelemetryMachineRedableMessage = `This installation of Gauge collects usage data in order to help us improve your experience. 67 <a href="https://gauge.org/telemetry">Read more here</a> about Gauge telemetry.` 68 69 // GaugeTelemetryLSPMessage is the message printed when user has not explicitly opted in/out 70 // of telemetry. Displayed only in LSP Client. 71 GaugeTelemetryLSPMessage = `This installation of Gauge collects usage data in order to help us improve your experience. 72 [Read more here](https://gauge.org/telemetry) about Gauge telemetry. 73 Would you like to participate?` 74 ) 75 76 var gaHTTPTransport = http.DefaultTransport 77 78 var telemetryEnabled, telemetryLogEnabled bool 79 80 // Init sets flags used by the package methods. 81 func Init() { 82 telemetryEnabled = config.TelemetryEnabled() 83 telemetryLogEnabled = config.TelemetryLogEnabled() 84 } 85 86 func send(category, action, label, medium string, wg *sync.WaitGroup) bool { 87 if !telemetryEnabled { 88 wg.Done() 89 return false 90 } 91 label = strings.Trim(fmt.Sprintf("%s,%s", label, runtime.GOOS), ",") 92 sendChan := make(chan bool, 1) 93 go func(c chan<- bool) { 94 defer recoverPanic() 95 t := gaTrackingID 96 if env.UseTestGA() { 97 t = gaTestTrackingID 98 } 99 client, err := ga.NewClient(t) 100 if err != nil { 101 logger.Debugf(true, "Unable to create ga client, %s", err) 102 } 103 client.HttpClient = &http.Client{} 104 client.ClientID(config.UniqueID()) 105 client.AnonymizeIP(true) 106 client.ApplicationName(appName) 107 client.ApplicationVersion(version.FullVersion()) 108 client.CampaignMedium(medium) 109 client.CampaignSource(appName) 110 client.HttpClient.Transport = gaHTTPTransport 111 if telemetryLogEnabled { 112 client.HttpClient.Transport = newlogEnabledHTTPTransport() 113 } 114 ev := ga.NewEvent(category, action) 115 if label != "" { 116 ev.Label(label) 117 } 118 err = client.Send(ev) 119 if err != nil { 120 logger.Debugf(true, "Unable to send analytics data, %s", err) 121 } 122 c <- true 123 }(sendChan) 124 125 for { 126 select { 127 case <-sendChan: 128 wg.Done() 129 return true 130 case <-time.After(timeout * time.Second): 131 logger.Debugf(true, "Unable to send analytics data, timed out") 132 wg.Done() 133 return false 134 } 135 } 136 } 137 138 func recoverPanic() { 139 if r := recover(); r != nil { 140 logger.Errorf(true, "%v\n%s", r, string(debug.Stack())) 141 } 142 } 143 144 func trackConsole(category, action, label string) { 145 var medium = consoleMedium 146 if isCI() { 147 medium = ciMedium 148 } 149 wg := &sync.WaitGroup{} 150 wg.Add(1) 151 defer wg.Wait() 152 go send(category, action, label, medium, wg) 153 } 154 155 func isCI() bool { 156 // Travis, AppVeyor, CircleCI, Wercket, drone.io, gitlab-ci 157 if ci, _ := strconv.ParseBool(os.Getenv("CI")); ci { 158 return true 159 } 160 161 // GoCD 162 if os.Getenv("GO_SERVER_URL") != "" { 163 return true 164 } 165 166 // Jenkins 167 if os.Getenv("JENKINS_URL") != "" { 168 return true 169 } 170 171 // Teamcity 172 if os.Getenv("TEAMCITY_VERSION") != "" { 173 return true 174 } 175 176 // TFS 177 if ci, _ := strconv.ParseBool(os.Getenv("TFS_BUILD")); ci { 178 return true 179 } 180 return false 181 } 182 183 func daemon(mode, lang string) { 184 trackConsole("daemon", mode, lang) 185 } 186 187 // ScheduleDaemonTracking sends pings to GA at regular intervals. This is used to flag active usage. 188 func ScheduleDaemonTracking(mode, lang string) { 189 daemon(mode, lang) 190 ticker := time.NewTicker(28 * time.Minute) 191 if env.UseTestGA() && env.TelemetryInterval() != "" { 192 duration, _ := strconv.Atoi(env.TelemetryInterval()) 193 ticker = time.NewTicker(time.Duration(duration) * time.Minute) 194 } 195 for { 196 select { 197 case <-ticker.C: 198 daemon(mode, lang) 199 } 200 } 201 } 202 203 func newlogEnabledHTTPTransport() http.RoundTripper { 204 return &logEnabledRoundTripper{} 205 } 206 207 type logEnabledRoundTripper struct { 208 } 209 210 func (r logEnabledRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { 211 dump, err := httputil.DumpRequestOut(req, true) 212 if err != nil { 213 logger.Debugf(true, "Unable to dump analytics request, %s", err) 214 } 215 216 logger.Debugf(true, fmt.Sprintf("%q", dump)) 217 return http.DefaultTransport.RoundTrip(req) 218 }