github.com/ezbuy/gauge@v0.9.4-0.20171013092048-7ac5bd3931cd/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 "strings" 24 "time" 25 26 "fmt" 27 28 "os" 29 30 "sync" 31 32 "github.com/getgauge/gauge/config" 33 "github.com/getgauge/gauge/logger" 34 "github.com/getgauge/gauge/manifest" 35 "github.com/getgauge/gauge/version" 36 "github.com/jpillora/go-ogle-analytics" 37 ) 38 39 const ( 40 gaTrackingID = "UA-54838477-1" 41 appName = "Gauge Core" 42 consoleMedium = "console" 43 apiMedium = "api" 44 ciMedium = "CI" 45 timeout = 1 46 ) 47 48 var gaHTTPTransport = http.DefaultTransport 49 50 var telemetryEnabled, telemetryLogEnabled bool 51 52 func Init() { 53 telemetryEnabled = config.TelemetryEnabled() 54 telemetryLogEnabled = config.TelemetryLogEnabled() 55 } 56 57 func send(category, action, label, medium string, wg *sync.WaitGroup) bool { 58 if !telemetryEnabled { 59 wg.Done() 60 return false 61 } 62 sendChan := make(chan bool, 1) 63 go func(c chan<- bool) { 64 client, err := ga.NewClient(gaTrackingID) 65 client.HttpClient = &http.Client{} 66 client.ClientID(config.UniqueID()) 67 client.AnonymizeIP(true) 68 client.ApplicationName(appName) 69 client.ApplicationVersion(version.FullVersion()) 70 client.CampaignMedium(medium) 71 client.CampaignSource(appName) 72 client.HttpClient.Transport = gaHTTPTransport 73 74 if telemetryLogEnabled { 75 client.HttpClient.Transport = newlogEnabledHTTPTransport() 76 } 77 78 if err != nil { 79 logger.Debugf("Unable to create ga client, %s", err) 80 } 81 82 ev := ga.NewEvent(category, action) 83 if label != "" { 84 ev.Label(label) 85 } 86 87 err = client.Send(ev) 88 if err != nil { 89 logger.Debugf("Unable to send analytics data, %s", err) 90 } 91 c <- true 92 }(sendChan) 93 94 for { 95 select { 96 case <-sendChan: 97 wg.Done() 98 return true 99 case <-time.After(timeout * time.Second): 100 logger.Debugf("Unable to send analytics data, timed out") 101 wg.Done() 102 return false 103 } 104 } 105 } 106 107 func trackConsole(category, action, label string) { 108 var medium = consoleMedium 109 if isCI() { 110 medium = ciMedium 111 } 112 wg := &sync.WaitGroup{} 113 wg.Add(1) 114 defer wg.Wait() 115 go send(category, action, label, medium, wg) 116 } 117 118 func trackAPI(category, action, label string) { 119 var medium = apiMedium 120 if isCI() { 121 medium = ciMedium 122 } 123 wg := &sync.WaitGroup{} 124 wg.Add(1) 125 go send(category, action, label, medium, wg) 126 } 127 128 func isCI() bool { 129 // Travis, AppVeyor, CircleCI, Wercket, drone.io, gitlab-ci 130 if os.Getenv("CI") == "true" { 131 return true 132 } 133 134 // GoCD 135 if os.Getenv("GO_SERVER_URL") != "" { 136 return true 137 } 138 139 // Teamcity 140 if os.Getenv("TEAMCITY_VERSION") != "" { 141 return true 142 } 143 144 // TFS 145 if os.Getenv("TFS_BUILD") == "true" { 146 return true 147 } 148 return false 149 } 150 151 func trackManifest() { 152 m, err := manifest.ProjectManifest() 153 if err == nil { 154 trackConsole("manifest", "language", fmt.Sprintf("%s,%s", m.Language, strings.Join(m.Plugins, ","))) 155 } 156 } 157 158 func Execution(parallel, tagged, sorted, simpleConsole, verbose, hideSuggestion bool, parallelExecutionStrategy string) { 159 action := "serial" 160 if parallel { 161 action = "parallel" 162 } 163 flags := []string{} 164 165 if tagged { 166 flags = append(flags, "tagged") 167 } 168 169 if sorted { 170 flags = append(flags, "sorted") 171 } 172 173 if simpleConsole { 174 flags = append(flags, "simple-console") 175 } else { 176 flags = append(flags, "rich-console") 177 } 178 179 if verbose { 180 flags = append(flags, "verbose") 181 } 182 if hideSuggestion { 183 flags = append(flags, "hide-suggestion") 184 } 185 186 trackManifest() 187 trackConsole("execution", action, strings.Join(flags, ",")) 188 } 189 190 func Validation(hideSuggestion bool) { 191 if hideSuggestion { 192 trackConsole("validation", "validate", "hide-suggestion") 193 } else { 194 trackConsole("validation", "validate", "") 195 } 196 } 197 198 func Docs(docs string) { 199 trackConsole("docs", "generate", docs) 200 } 201 202 func Format() { 203 trackConsole("formatting", "format", "") 204 } 205 206 func Refactor() { 207 trackConsole("refactoring", "rephrase", "") 208 } 209 210 func ListTemplates() { 211 trackConsole("init", "templates", "") 212 } 213 214 func UninstallPlugin(plugin string) { 215 trackConsole("plugins", "uninstall", plugin) 216 } 217 218 func ProjectInit(lang string) { 219 trackConsole("project", "init", lang) 220 } 221 222 func Install(plugin string, zip bool) { 223 if zip { 224 trackConsole("plugins", "install-zip", plugin) 225 } else { 226 trackConsole("plugins", "install", plugin) 227 } 228 } 229 230 func Update(plugin string) { 231 trackConsole("plugins", "update", plugin) 232 } 233 234 func UpdateAll() { 235 trackConsole("plugins", "update-all", "all") 236 } 237 238 func InstallAll() { 239 trackConsole("plugins", "install-all", "all") 240 } 241 242 func CheckUpdates() { 243 trackConsole("updates", "check", "") 244 } 245 246 func Daemon() { 247 trackConsole("daemon", "api", "") 248 } 249 250 func Lsp() { 251 trackConsole("daemon", "lsp", "") 252 } 253 254 func APIRefactoring() { 255 trackAPI("refactoring", "rephrase", "") 256 } 257 258 func APIExtractConcept() { 259 trackAPI("refactoring", "extract-concept", "") 260 } 261 262 func APIFormat() { 263 trackAPI("formatting", "format", "") 264 } 265 266 func newlogEnabledHTTPTransport() http.RoundTripper { 267 return &logEnabledRoundTripper{} 268 } 269 270 type logEnabledRoundTripper struct { 271 } 272 273 func (r logEnabledRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { 274 dump, err := httputil.DumpRequestOut(req, true) 275 if err != nil { 276 logger.Debugf("Unable to dump analytics request, %s", err) 277 } 278 279 logger.Debugf(fmt.Sprintf("%q", dump)) 280 return http.DefaultTransport.RoundTrip(req) 281 }