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  }