github.com/dbernstein1/tyk@v2.9.0-beta9-dl-apic+incompatible/gateway/instrumentation_handlers.go (about)

     1  package gateway
     2  
     3  import (
     4  	"net/http"
     5  	"os"
     6  	"runtime/debug"
     7  	"strconv"
     8  	"time"
     9  
    10  	"github.com/gocraft/health"
    11  
    12  	"github.com/TykTechnologies/tyk/cli"
    13  	"github.com/TykTechnologies/tyk/request"
    14  
    15  	"github.com/TykTechnologies/tyk/config"
    16  )
    17  
    18  var applicationGCStats = debug.GCStats{}
    19  var instrument = health.NewStream()
    20  var instrumentationEnabled bool
    21  
    22  // setupInstrumentation handles all the intialisation of the instrumentation handler
    23  func setupInstrumentation() {
    24  	switch {
    25  	case *cli.LogInstrumentation:
    26  	case os.Getenv("TYK_INSTRUMENTATION") == "1":
    27  	default:
    28  		return
    29  	}
    30  
    31  	if config.Global().StatsdConnectionString == "" {
    32  		log.Error("Instrumentation is enabled, but no connectionstring set for statsd")
    33  		return
    34  	}
    35  
    36  	instrumentationEnabled = true
    37  
    38  	log.Info("Sending stats to: ", config.Global().StatsdConnectionString, " with prefix: ", config.Global().StatsdPrefix)
    39  	statsdSink, err := NewStatsDSink(config.Global().StatsdConnectionString,
    40  		&StatsDSinkOptions{Prefix: config.Global().StatsdPrefix})
    41  
    42  	if err != nil {
    43  		log.Fatal("Failed to start StatsD check: ", err)
    44  	}
    45  
    46  	log.Info("StatsD instrumentation sink started")
    47  	instrument.AddSink(statsdSink)
    48  
    49  	MonitorApplicationInstrumentation()
    50  }
    51  
    52  // InstrumentationMW will set basic instrumentation events, variables and timers on API jobs
    53  func InstrumentationMW(next http.Handler) http.Handler {
    54  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    55  		job := instrument.NewJob("SystemAPICall")
    56  
    57  		next.ServeHTTP(w, r)
    58  		job.EventKv("called", health.Kvs{
    59  			"from_ip":  request.RealIP(r),
    60  			"method":   r.Method,
    61  			"endpoint": r.URL.Path,
    62  			"raw_url":  r.URL.String(),
    63  			"size":     strconv.Itoa(int(r.ContentLength)),
    64  		})
    65  		job.Complete(health.Success)
    66  	})
    67  }
    68  
    69  func MonitorApplicationInstrumentation() {
    70  	log.Info("Starting application monitoring...")
    71  	go func() {
    72  		job := instrument.NewJob("GCActivity")
    73  		job_rl := instrument.NewJob("Load")
    74  		metadata := health.Kvs{"host": hostDetails.Hostname}
    75  		applicationGCStats.PauseQuantiles = make([]time.Duration, 5)
    76  
    77  		for {
    78  			debug.ReadGCStats(&applicationGCStats)
    79  			job.GaugeKv("pauses_quantile_min", float64(applicationGCStats.PauseQuantiles[0].Nanoseconds()), metadata)
    80  			job.GaugeKv("pauses_quantile_25", float64(applicationGCStats.PauseQuantiles[1].Nanoseconds()), metadata)
    81  			job.GaugeKv("pauses_quantile_50", float64(applicationGCStats.PauseQuantiles[2].Nanoseconds()), metadata)
    82  			job.GaugeKv("pauses_quantile_75", float64(applicationGCStats.PauseQuantiles[3].Nanoseconds()), metadata)
    83  			job.GaugeKv("pauses_quantile_max", float64(applicationGCStats.PauseQuantiles[4].Nanoseconds()), metadata)
    84  
    85  			job_rl.GaugeKv("rps", float64(GlobalRate.Rate()), metadata)
    86  			time.Sleep(5 * time.Second)
    87  		}
    88  	}()
    89  }