tracing.go 3.23 KB
Newer Older
ale's avatar
ale committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
package tracing

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sync"

	openzipkin "github.com/openzipkin/zipkin-go"
	zipkinHTTP "github.com/openzipkin/zipkin-go/reporter/http"
	"go.opencensus.io/exporter/zipkin"
	"go.opencensus.io/plugin/ochttp"
	"go.opencensus.io/trace"
)

var (
	// Enabled reports whether tracing is globally enabled or not.
	Enabled bool

	// The active tracing configuration, if Enabled is true.
	config tracingConfig

	initOnce sync.Once
)

const globalTracingConfigPath = "/etc/tracing/client.conf"

type tracingConfig struct {
	ReportURL string `json:"report_url"`
}

// Read the global tracing configuration file. Its location is
// hardcoded, but it can be overriden using the TRACING_CONFIG
// environment variable.
func readTracingConfig() error {
	// Read and decode configuration.
	cfgPath := globalTracingConfigPath
	if s := os.Getenv("TRACING_CONFIG"); s != "" {
		cfgPath = s
	}
	data, err := ioutil.ReadFile(cfgPath)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(data, &config); err != nil {
		log.Printf("warning: error in tracing configuration: %v, tracing disabled", err)
		return err
	}

	if config.ReportURL == "" {
		log.Printf("warning: tracing configuration contains no report_url, tracing disabled")
		return errors.New("no report_url")
	}

	return nil
}

// Compute the service name for Zipkin: this is usually the program
// name (without path), but it can be overriden by the TRACING_SERVICE
// environment variable.
func getServiceName() string {
	if s := os.Getenv("TRACING_SERVICE"); s != "" {
		return s
	}
	return filepath.Base(os.Args[0])
}

// Initialize tracing. Tracing will be enabled if the system-wide
// tracing configuration file is present and valid. Explicitly set
// TRACING_ENABLE=0 in the environment to disable tracing.
//
// We need to check the configuration as soon as possible, because
// it's likely that client transports are created before HTTP servers,
// and we need to wrap them with opencensus at creation time.
func init() {
	// Kill switch from environment.
	if s := os.Getenv("TRACING_ENABLE"); s == "0" {
		return
	}

	if err := readTracingConfig(); err != nil {
		return
	}

	Enabled = true
}

func initTracing(endpointAddr string) {
	initOnce.Do(func() {
		localEndpoint, err := openzipkin.NewEndpoint(getServiceName(), endpointAddr)
		if err != nil {
			log.Printf("warning: error creating tracing endpoint: %v, tracing disabled", err)
			return
		}

		reporter := zipkinHTTP.NewReporter(config.ReportURL)
		ze := zipkin.NewExporter(reporter, localEndpoint)

		trace.RegisterExporter(ze)
		trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})

		log.Printf("tracing enabled (report_url %s)", config.ReportURL)

		Enabled = true
	})
}

// WrapTransport optionally wraps a http.RoundTripper with OpenCensus
// tracing functionality, if it is globally enabled.
func WrapTransport(t http.RoundTripper) http.RoundTripper {
	if Enabled {
		t = &ochttp.Transport{Base: t}
	}
	return t
}

// WrapHandler wraps a http.Handler with OpenCensus tracing
// functionality, if globally enabled.
func WrapHandler(h http.Handler, endpointAddr string) http.Handler {
	if Enabled {
		initTracing(endpointAddr)
		h = &ochttp.Handler{Handler: h}
	}
	return h
}