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  }