Newer
Older
package tracing
import (
"encoding/json"
"errors"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
othttp "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
b3 "go.opentelemetry.io/contrib/propagators/b3"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/zipkin"
)
var (
// Enabled reports whether tracing is globally enabled or not.
Enabled bool
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() (*tracingConfig, error) {
// Read and decode configuration.
cfgPath := globalTracingConfigPath
if s := os.Getenv("TRACING_CONFIG"); s != "" {
cfgPath = s
}
data, err := ioutil.ReadFile(cfgPath)
if err != nil {
if err := json.Unmarshal(data, &config); err != nil {
log.Printf("warning: error in tracing configuration: %v, tracing disabled", err)
}
if config.ReportURL == "" {
log.Printf("warning: tracing configuration contains no report_url, tracing disabled")
}
// 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
}
if s, err := os.Executable(); err == nil {
return filepath.Base(s)
}
return "unknown_service"
func defaultResource(serviceName string) *resource.Resource {
r, _ := resource.Merge(
resource.Default(),
resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String(serviceName),
// 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.
// Kill switch from environment.
if s := os.Getenv("TRACING_ENABLE"); s == "0" {
return
}
config, err := readTracingConfig()
if err != nil {
return
}
ze, err := zipkin.New(config.ReportURL)
log.Printf("error creating Zipkin exporter: %v", err)
// The sampling policy only applies to incoming requests for
// which tracing is not already enabled: in this case, we
// always pass-through.
switch config.Sample {
case "", "always":
default:
frac, err := strconv.ParseFloat(config.Sample, 64)
if err != nil {
log.Printf("warning: error in tracing configuration: sample: %v, tracing disabled", err)
return
}
sampler = trace.TraceIDRatioBased(frac)
trace.WithResource(defaultResource(serviceName)),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(
propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
b3.New()))
log.Printf("tracing enabled (report_url %s)", config.ReportURL)
Enabled = true
})
}
// Init tracing support, if not using WrapHandler.
func Init() {
initTracing(getServiceName())
// WrapTransport optionally wraps a http.RoundTripper with OpenCensus
// tracing functionality, if it is globally enabled.
func WrapTransport(t http.RoundTripper) http.RoundTripper {
if Enabled {
}
return t
}
// WrapHandler wraps a http.Handler with OpenCensus tracing
// functionality, if globally enabled. Automatically calls Init().
func WrapHandler(h http.Handler, endpointAddr string) http.Handler {
serviceName := getServiceName()
initTracing(serviceName)
if !Enabled {
return h
// Format span names with the request URL path.
return othttp.NewHandler(
h, serviceName,
othttp.WithSpanNameFormatter(func(op string, r *http.Request) string {
return r.URL.Path
}),
)