Copyright 2019 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
Package dcensus provides functionality for debug instrumentation.
package dcensus

import (
	
	
	
	
	

	
	
	
	
	
	
	
	
	
	
	
	mrpb 
)
KeyStatus is a tag key named "status".
var KeyStatus = tag.MustNewKey("status")
RouteTagger is a func that can be used to derive a dynamic route tag for an incoming request.
type RouteTagger func(route string, r *http.Request) string
Router is an http multiplexer that instruments per-handler debugging information and census instrumentation.
NewRouter creates a new Router, using tagger to tag incoming requests in monitoring. If tagger is nil, a default route tagger is used.
func ( RouteTagger) *Router {
	if  == nil {
		 = func( string,  *http.Request) string {
			return strings.Trim(, "/")
		}
	}
	 := http.NewServeMux()
	return &Router{
		mux:     ,
		Handler: &ochttp.Handler{Handler: },
		tagger:  ,
	}
}
Handle registers handler with the given route. It has the same routing semantics as http.ServeMux.
func ( *Router) ( string,  http.Handler) {
	.mux.HandleFunc(, func( http.ResponseWriter,  *http.Request) {
		 := .tagger(, )
		ochttp.WithRouteTag(, ).ServeHTTP(, )
	})
}
HandleFunc is a wrapper around Handle for http.HandlerFuncs.
func ( *Router) ( string,  http.HandlerFunc) {
	.Handle(, )
}

const debugPage = `
<html>
<p><a href="/tracez">/tracez</a> - trace spans</p>
<p><a href="/statsz">/statz</a> - prometheus metrics page</p>
`
Init configures tracing and aggregation according to the given Views. If running on GCP, Init also configures exporting to StackDriver.
The default trace sampler samples with probability 1e-4. That's too infrequent for our traffic levels. In the future we may want to decrease this sampling rate.
	trace.ApplyConfig(trace.Config{DefaultSampler: trace.ProbabilitySampler(0.01)})
	if  := view.Register(...);  != nil {
		return fmt.Errorf("dcensus.Init(views): view.Register: %v", )
	}
	exportToStackdriver(context.Background(), )
	return nil
}
NewServer creates a new http.Handler for serving debug information.
func () (http.Handler, error) {
	,  := prometheus.NewExporter(prometheus.Options{})
	if  != nil {
		return nil, fmt.Errorf("dcensus.NewServer: prometheus.NewExporter: %v", )
	}
	 := http.NewServeMux()
	zpages.Handle(, "/")
	.Handle("/statsz", )
	.HandleFunc("/", func( http.ResponseWriter,  *http.Request) {
		fmt.Fprint(, debugPage)
	})

	return , nil
}
monitoredResource wraps a *mrpb.MonitoredResource to implement the monitoredresource.MonitoredResource interface.
ExportToStackdriver checks to see if the process is running in a GCP environment, and if so configures exporting to stackdriver.
func ( context.Context,  *config.Config) {
	if .ProjectID == "" {
		log.Infof(, "Not exporting to StackDriver: GOOGLE_CLOUD_PROJECT is unset.")
		return
	}
Report statistics every minutes, due to stackdriver limitations described at https://cloud.google.com/monitoring/custom-metrics/creating-metrics#writing-ts
	view.SetReportingPeriod(time.Minute)

	,  := NewViewExporter()
	if  != nil {
		log.Fatalf(, "error creating view exporter: %v", )
	}
	view.RegisterExporter()

We want traces to be associated with the *app*, not the instance. TraceSpansBufferMaxBytes is increased from the default of 8MiB, though we can't increase *too* much because this is still running in GAE, which is relatively memory-constrained.
	,  := stackdriver.NewExporter(stackdriver.Options{
		ProjectID:                .ProjectID,
		MonitoredResource:        (*monitoredResource)(.MonitoredResource),
		TraceSpansBufferMaxBytes: 32 * 1024 * 1024, // 32 MiB
		DefaultMonitoringLabels:  stackdriverLabels(),
		OnError:                  .onError,
	})
	if  != nil {
		log.Fatalf(, "error creating trace exporter: %v", )
	}
	.exp = 
	trace.RegisterExporter()
}
NewViewExporter creates a StackDriver exporter for stats.
func ( *config.Config) ( *stackdriver.Exporter,  error) {
	defer derrors.Wrap(&, "NewViewExporter()")
Views must be associated with the instance, else we run into overlapping timeseries problems. Note that generic_task is used because the gae_instance resource type is not supported for metrics: https://cloud.google.com/monitoring/custom-metrics/creating-metrics#which-resource
	 := &monitoredResource{
		Type: "generic_task",
		Labels: map[string]string{
			"project_id": .ProjectID,
			"location":   .LocationID,
			"job":        .ServiceID,
			"namespace":  "go-discovery",
			"task_id":    .InstanceID,
		},
	}
	if .OnGKE() {
		 = (*monitoredResource)(.MonitoredResource)
	}
	return stackdriver.NewExporter(stackdriver.Options{
		ProjectID:               .ProjectID,
		MonitoredResource:       ,
		DefaultMonitoringLabels: stackdriverLabels(),
		OnError: func( error) {
			log.Warningf(context.Background(), "Stackdriver view exporter: %v", )
		},
	})
}

func ( *config.Config) *stackdriver.Labels {
	 := &stackdriver.Labels{}
	.Set("version", .AppVersionLabel(), "Version label of the running binary")
	.Set("env", .DeploymentEnvironment(), "deployment environment")
	.Set("app", .Application(), "application name")
	return 
}
Customizations of ochttp views. Views are updated as follows: + ClientHost and ServerRoute are added to resp. client and server metrics. Since these are bounded cardinality in our metrics, they are useful to add additional context. + Method tags are removed. We don't have any routes that accept more than one HTTP method.
var (
	ServerRequestCount = &view.View{
		Name:        "go-discovery/http/server/request_count",
		Description: "Count of HTTP requests started by Method",
		TagKeys:     []tag.Key{ochttp.Method},
		Measure:     ochttp.ServerRequestCount,
		Aggregation: view.Count(),
	}
	ServerResponseCount = &view.View{
		Name:        "go-discovery/http/server/response_count",
		Description: "Server response count by status code and route",
		TagKeys:     []tag.Key{ochttp.StatusCode, ochttp.KeyServerRoute},
		Measure:     ochttp.ServerLatency,
		Aggregation: view.Count(),
	}
	ServerLatency = &view.View{
		Name:        "go-discovery/http/server/response_latency",
		Description: "Server response distribution by status code and route",
		TagKeys:     []tag.Key{ochttp.KeyServerRoute},
		Measure:     ochttp.ServerLatency,
		Aggregation: ochttp.DefaultLatencyDistribution,
	}
	ServerResponseBytes = &view.View{
		Name:        "go-discovery/http/server/response_bytes",
		Description: "Size distribution of HTTP response body",
		TagKeys:     []tag.Key{ochttp.KeyServerRoute},
		Measure:     ochttp.ServerResponseBytes,
		Aggregation: ochttp.DefaultSizeDistribution,
	}
	ServerViews = []*view.View{
		ServerRequestCount,
		ServerResponseCount,
		ServerLatency,
		ServerResponseBytes,
	}
)
RecordWithTag is a convenience function for recording a single measurement with a single tag.