Skip to content
This repository has been archived by the owner on Jul 31, 2023. It is now read-only.

Commit

Permalink
Exemplar: revert wrong implementation. (#1067)
Browse files Browse the repository at this point in the history
* Exemplar: move to metricdata.

* Remove AttachmentExtractor.

* More cleanup.

* Add a TODO for next PR.
  • Loading branch information
songy23 committed Mar 18, 2019
1 parent 3b8e272 commit 604812a
Show file tree
Hide file tree
Showing 13 changed files with 65 additions and 387 deletions.
79 changes: 0 additions & 79 deletions exemplar/exemplar.go

This file was deleted.

36 changes: 13 additions & 23 deletions trace/exemplar.go → metric/metricdata/exemplar.go
Expand Up @@ -12,32 +12,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package trace
package metricdata

import (
"context"
"encoding/hex"

"go.opencensus.io/exemplar"
"time"
)

func init() {
exemplar.RegisterAttachmentExtractor(attachSpanContext)
// Exemplar is an example data point associated with each bucket of a
// distribution type aggregation.
//
// Their purpose is to provide an example of the kind of thing
// (request, RPC, trace span, etc.) that resulted in that measurement.
type Exemplar struct {
Value float64 // the value that was recorded
Timestamp time.Time // the time the value was recorded
Attachments Attachments // attachments (if any)
}

func attachSpanContext(ctx context.Context, a exemplar.Attachments) exemplar.Attachments {
span := FromContext(ctx)
if span == nil {
return a
}
sc := span.SpanContext()
if !sc.IsSampled() {
return a
}
if a == nil {
a = make(exemplar.Attachments)
}
a[exemplar.KeyTraceID] = hex.EncodeToString(sc.TraceID[:])
a[exemplar.KeySpanID] = hex.EncodeToString(sc.SpanID[:])
return a
}
// Attachments is a map of extra values associated with a recorded data point.
type Attachments map[string]string
4 changes: 1 addition & 3 deletions metric/metricdata/point.go
Expand Up @@ -16,8 +16,6 @@ package metricdata

import (
"time"

"go.opencensus.io/exemplar"
)

// Point is a single data point of a time series.
Expand Down Expand Up @@ -144,7 +142,7 @@ type Bucket struct {
// bucket_bounds.
Count int64
// Exemplar associated with this bucket (if any).
Exemplar *exemplar.Exemplar
Exemplar *Exemplar
}

// Summary is a representation of percentiles.
Expand Down
4 changes: 2 additions & 2 deletions stats/record.go
Expand Up @@ -18,7 +18,6 @@ package stats
import (
"context"

"go.opencensus.io/exemplar"
"go.opencensus.io/stats/internal"
"go.opencensus.io/tag"
)
Expand Down Expand Up @@ -51,7 +50,8 @@ func Record(ctx context.Context, ms ...Measurement) {
if !record {
return
}
recorder(tag.FromContext(ctx), ms, exemplar.AttachmentsFromContext(ctx))
// TODO(songy23): fix attachments.
recorder(tag.FromContext(ctx), ms, map[string]string{})
}

// RecordWithTags records one or multiple measurements at once.
Expand Down
67 changes: 24 additions & 43 deletions stats/view/aggregation_data.go
Expand Up @@ -18,15 +18,15 @@ package view
import (
"math"

"go.opencensus.io/exemplar"
"go.opencensus.io/metric/metricdata"
)

// AggregationData represents an aggregated value from a collection.
// They are reported on the view data during exporting.
// Mosts users won't directly access aggregration data.
type AggregationData interface {
isAggregationData() bool
addSample(e *exemplar.Exemplar)
addSample(v float64)
clone() AggregationData
equal(other AggregationData) bool
}
Expand All @@ -43,7 +43,7 @@ type CountData struct {

func (a *CountData) isAggregationData() bool { return true }

func (a *CountData) addSample(_ *exemplar.Exemplar) {
func (a *CountData) addSample(_ float64) {
a.Value = a.Value + 1
}

Expand All @@ -70,8 +70,8 @@ type SumData struct {

func (a *SumData) isAggregationData() bool { return true }

func (a *SumData) addSample(e *exemplar.Exemplar) {
a.Value += e.Value
func (a *SumData) addSample(v float64) {
a.Value += v
}

func (a *SumData) clone() AggregationData {
Expand Down Expand Up @@ -101,16 +101,16 @@ type DistributionData struct {
SumOfSquaredDev float64 // sum of the squared deviation from the mean
CountPerBucket []int64 // number of occurrences per bucket
// ExemplarsPerBucket is slice the same length as CountPerBucket containing
// an exemplar for the associated bucket, or nil.
ExemplarsPerBucket []*exemplar.Exemplar
// an metricdata for the associated bucket, or nil.
ExemplarsPerBucket []*metricdata.Exemplar
bounds []float64 // histogram distribution of the values
}

func newDistributionData(bounds []float64) *DistributionData {
bucketCount := len(bounds) + 1
return &DistributionData{
CountPerBucket: make([]int64, bucketCount),
ExemplarsPerBucket: make([]*exemplar.Exemplar, bucketCount),
ExemplarsPerBucket: make([]*metricdata.Exemplar, bucketCount),
bounds: bounds,
Min: math.MaxFloat64,
Max: math.SmallestNonzeroFloat64,
Expand All @@ -129,64 +129,45 @@ func (a *DistributionData) variance() float64 {

func (a *DistributionData) isAggregationData() bool { return true }

func (a *DistributionData) addSample(e *exemplar.Exemplar) {
f := e.Value
if f < a.Min {
a.Min = f
// TODO(songy23): support exemplar attachments.
func (a *DistributionData) addSample(v float64) {
if v < a.Min {
a.Min = v
}
if f > a.Max {
a.Max = f
if v > a.Max {
a.Max = v
}
a.Count++
a.addToBucket(e)
a.addToBucket(v)

if a.Count == 1 {
a.Mean = f
a.Mean = v
return
}

oldMean := a.Mean
a.Mean = a.Mean + (f-a.Mean)/float64(a.Count)
a.SumOfSquaredDev = a.SumOfSquaredDev + (f-oldMean)*(f-a.Mean)
a.Mean = a.Mean + (v-a.Mean)/float64(a.Count)
a.SumOfSquaredDev = a.SumOfSquaredDev + (v-oldMean)*(v-a.Mean)
}

func (a *DistributionData) addToBucket(e *exemplar.Exemplar) {
func (a *DistributionData) addToBucket(v float64) {
var count *int64
var ex **exemplar.Exemplar
for i, b := range a.bounds {
if e.Value < b {
if v < b {
count = &a.CountPerBucket[i]
ex = &a.ExemplarsPerBucket[i]
break
}
}
if count == nil {
if count == nil { // Last bucket.
count = &a.CountPerBucket[len(a.bounds)]
ex = &a.ExemplarsPerBucket[len(a.bounds)]
}
*count++
*ex = maybeRetainExemplar(*ex, e)
}

func maybeRetainExemplar(old, cur *exemplar.Exemplar) *exemplar.Exemplar {
if old == nil {
return cur
}

// Heuristic to pick the "better" exemplar: first keep the one with a
// sampled trace attachment, if neither have a trace attachment, pick the
// one with more attachments.
_, haveTraceID := cur.Attachments[exemplar.KeyTraceID]
if haveTraceID || len(cur.Attachments) >= len(old.Attachments) {
return cur
}
return old
}

func (a *DistributionData) clone() AggregationData {
c := *a
c.CountPerBucket = append([]int64(nil), a.CountPerBucket...)
c.ExemplarsPerBucket = append([]*exemplar.Exemplar(nil), a.ExemplarsPerBucket...)
c.ExemplarsPerBucket = append([]*metricdata.Exemplar(nil), a.ExemplarsPerBucket...)
return &c
}

Expand Down Expand Up @@ -218,8 +199,8 @@ func (l *LastValueData) isAggregationData() bool {
return true
}

func (l *LastValueData) addSample(e *exemplar.Exemplar) {
l.Value = e.Value
func (l *LastValueData) addSample(v float64) {
l.Value = v
}

func (l *LastValueData) clone() AggregationData {
Expand Down
40 changes: 7 additions & 33 deletions stats/view/aggregation_data_test.go
Expand Up @@ -18,11 +18,10 @@ package view
import (
"reflect"
"testing"
"time"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"go.opencensus.io/exemplar"
"go.opencensus.io/metric/metricdata"
)

func TestDataClone(t *testing.T) {
Expand Down Expand Up @@ -67,21 +66,12 @@ func TestDataClone(t *testing.T) {

func TestDistributionData_addSample(t *testing.T) {
dd := newDistributionData([]float64{1, 2})
t1, _ := time.Parse("Mon Jan 2 15:04:05 -0700 MST 2006", "Mon Jan 2 15:04:05 -0700 MST 2006")
e1 := &exemplar.Exemplar{
Attachments: exemplar.Attachments{
"tag:X": "Y",
"tag:A": "B",
},
Timestamp: t1,
Value: 0.5,
}
dd.addSample(e1)
dd.addSample(0.5)

want := &DistributionData{
Count: 1,
CountPerBucket: []int64{1, 0, 0},
ExemplarsPerBucket: []*exemplar.Exemplar{e1, nil, nil},
ExemplarsPerBucket: []*metricdata.Exemplar{nil, nil, nil},
Max: 0.5,
Min: 0.5,
Mean: 0.5,
Expand All @@ -91,21 +81,13 @@ func TestDistributionData_addSample(t *testing.T) {
t.Fatalf("Unexpected DistributionData -got +want: %s", diff)
}

t2 := t1.Add(time.Microsecond)
e2 := &exemplar.Exemplar{
Attachments: exemplar.Attachments{
"tag:X": "Y",
},
Timestamp: t2,
Value: 0.7,
}
dd.addSample(e2)
dd.addSample(0.7)

// Previous exemplar should be preserved, since it has more annotations.
want = &DistributionData{
Count: 2,
CountPerBucket: []int64{2, 0, 0},
ExemplarsPerBucket: []*exemplar.Exemplar{e1, nil, nil},
ExemplarsPerBucket: []*metricdata.Exemplar{nil, nil, nil},
Max: 0.7,
Min: 0.5,
Mean: 0.6,
Expand All @@ -115,21 +97,13 @@ func TestDistributionData_addSample(t *testing.T) {
t.Fatalf("Unexpected DistributionData -got +want: %s", diff)
}

t3 := t2.Add(time.Microsecond)
e3 := &exemplar.Exemplar{
Attachments: exemplar.Attachments{
exemplar.KeyTraceID: "abcd",
},
Timestamp: t3,
Value: 0.2,
}
dd.addSample(e3)
dd.addSample(0.2)

// Exemplar should be replaced since it has a trace_id.
want = &DistributionData{
Count: 3,
CountPerBucket: []int64{3, 0, 0},
ExemplarsPerBucket: []*exemplar.Exemplar{e3, nil, nil},
ExemplarsPerBucket: []*metricdata.Exemplar{nil, nil, nil},
Max: 0.7,
Min: 0.2,
Mean: 0.4666666666666667,
Expand Down

0 comments on commit 604812a

Please sign in to comment.