Don't record measurements with no subscription (#600)

Suggested by Ramon, immediately dropping the measurements from
the measures with no subscription rather than doing it at a later time
significantly improves the performance on the critical path.

Before:
BenchmarkRecord0-8            	500000000	         3.09 ns/op
BenchmarkRecord1-8            	 5000000	       366 ns/op
BenchmarkRecord8-8            	 3000000	       412 ns/op
BenchmarkRecord8_Parallel-8   	 2000000	       804 ns/op
BenchmarkRecord8_8Tags-8      	 3000000	       415 ns/op

After:
BenchmarkRecord0-8            	1000000000	         2.58 ns/op
BenchmarkRecord1-8            	30000000	        36.9 ns/op
BenchmarkRecord8-8            	20000000	        89.4 ns/op
BenchmarkRecord8_Parallel-8   	30000000	        44.8 ns/op
BenchmarkRecord8_8Tags-8      	20000000	        90.1 ns/op
6 files changed
tree: 6d797e345e165fe573546cfd6f25f637dade4afc
  1. examples/
  2. exporter/
  3. internal/
  4. plugin/
  5. stats/
  6. tag/
  7. trace/
  8. zpages/
  9. .gitignore
  10. .travis.yml
  11. appveyor.yml
  12. AUTHORS
  13. CONTRIBUTING.md
  14. doc.go
  15. LICENSE
  16. README.md
README.md

OpenCensus Libraries for Go

Build Status Windows Build Status GoDoc Gitter chat

OpenCensus Go is a Go implementation of OpenCensus, a toolkit for collecting application performance and behavior monitoring data. Currently it consists of three major components: tags, stats, and tracing.

This project is still at a very early stage of development. The API is changing rapidly, vendoring is recommended.

Installation

$ go get -u go.opencensus.io

Prerequisites

OpenCensus Go libraries require Go 1.8 or later.

Exporters

OpenCensus can export instrumentation data to various backends. Currently, OpenCensus supports:

Overview

OpenCensus Overview

In a microservices environment, a user request may go through multiple services until there is a response. OpenCensus allows you to instrument your services and collect diagnostics data all through your services end-to-end.

Start with instrumenting HTTP and gRPC clients and servers, then add additional custom instrumentation.

Tags

Tags represent propagated key-value pairs. They are propagated using context.Context in the same process or can be encoded to be transmitted on the wire and decoded back to a tag.Map at the destination.

Package tag provides a builder to create tag maps and put it into the current context. To propagate a tag map to downstream methods and RPCs, New will add the produced tag map to the current context. If there is already a tag map in the current context, it will be replaced.

ctx, err = tag.New(ctx,
	tag.Insert(osKey, "macOS-10.12.5"),
	tag.Upsert(userIDKey, "cde36753ed"),
)
if err != nil {
	log.Fatal(err)
}

Stats

OpenCensus is a low-overhead framework even if instrumentation is always enabled. In order to be so, it is optimized to make recording of data points fast and separate from the data aggregation.

OpenCensus stats collection happens in two stages:

  • Definition of measures and recording of data points
  • Definition of views and aggregation of the recorded data

Recording

Measurements are data points associated with a measure. Recording implicitly tags the set of Measurements with the tags from the provided context:

stats.Record(ctx, videoSize.M(102478))

Views

Views are how Measures are aggregated. You can think of them as queries over the set of recorded data points (measurements).

Views have two parts: the tags to group by and the aggregation type used.

Currently four types of aggregations are supported:

  • CountAggregation is used to count the number of times a sample was recorded.
  • DistributionAggregation is used to provide a histogram of the values of the samples.
  • SumAggregation is used to sum up all sample values.
  • MeanAggregation is used to calculate the mean of sample values.
distAgg := view.DistributionAggregation{0, 1 << 32, 2 << 32, 3 << 32}
countAgg := view.CountAggregation{}
sumAgg := view.SumAggregation{}
meanAgg := view.MeanAggregation{}

Here we create a view with the DistributionAggregation over our measure.

if err = view.Subscribe(&view.View{
	Name:        "my.org/video_size_distribution",
	Description: "distribution of processed video size over time",
	Measure:     videoSize,
	Aggregation: view.DistributionAggregation([]float64{0, 1 << 32, 2 << 32, 3 << 32}),
}); err != nil {
	log.Fatalf("Failed to subscribe to view: %v", err)
}

Subscribe begins collecting data for the view. Subscribed views' data will be exported via the registered exporters.

Traces

ctx, span := trace.StartSpan(ctx, "your choice of name")
defer span.End()

Profiles

OpenCensus tags can be applied as profiler labels for users who are on Go 1.9 and above.

ctx, err = tag.New(ctx,
	tag.Insert(osKey, "macOS-10.12.5"),
	tag.Insert(userIDKey, "fff0989878"),
)
if err != nil {
	log.Fatal(err)
}
tag.Do(ctx, func(ctx context.Context) {
	// Do work.
	// When profiling is on, samples will be
	// recorded with the key/values from the tag map.
})

A screenshot of the CPU profile from the program above:

CPU profile