Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/event/export/ocagent #89

Merged
merged 1 commit into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions event/export/ocagent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Exporting Metrics and Traces with OpenCensus, Zipkin, and Prometheus

This tutorial provides a minimum example to verify that metrics and traces
can be exported to OpenCensus from Go tools.

## Setting up oragent

1. Ensure you have [docker](https://www.docker.com/get-started) and [docker-compose](https://docs.docker.com/compose/install/).
2. Clone [oragent](https://github.com/orijtech/oragent).
3. In the oragent directory, start the services:
```bash
docker-compose up
```
If everything goes well, you should see output resembling the following:
```
Starting oragent_zipkin_1 ... done
Starting oragent_oragent_1 ... done
Starting oragent_prometheus_1 ... done
...
```
* You can check the status of the OpenCensus agent using zPages at http://localhost:55679/debug/tracez.
* You can now access the Prometheus UI at http://localhost:9445.
* You can now access the Zipkin UI at http://localhost:9444.
4. To shut down oragent, hit Ctrl+C in the terminal.
5. You can also start oragent in detached mode by running `docker-compose up -d`. To stop oragent while detached, run `docker-compose down`.

## Exporting Metrics and Traces
1. Clone the [tools](https://golang.org/x/tools) subrepository.
1. Inside `internal`, create a file named `main.go` with the following contents:
```go
package main

import (
"context"
"fmt"
"math/rand"
"net/http"
"time"

"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/event/export"
"golang.org/x/tools/internal/event/export/metric"
"golang.org/x/tools/internal/event/export/ocagent"
)

type testExporter struct {
metrics metric.Exporter
ocagent *ocagent.Exporter
}

func (e *testExporter) ProcessEvent(ctx context.Context, ev event.Event) (context.Context, event.Event) {
ctx, ev = export.Tag(ctx, ev)
ctx, ev = export.ContextSpan(ctx, ev)
ctx, ev = e.metrics.ProcessEvent(ctx, ev)
ctx, ev = e.ocagent.ProcessEvent(ctx, ev)
return ctx, ev
}

func main() {
exporter := &testExporter{}

exporter.ocagent = ocagent.Connect(&ocagent.Config{
Start: time.Now(),
Address: "http://127.0.0.1:55678",
Service: "go-tools-test",
Rate: 5 * time.Second,
Client: &http.Client{},
})
event.SetExporter(exporter)

ctx := context.TODO()
mLatency := event.NewFloat64Key("latency", "the latency in milliseconds")
distribution := metric.HistogramFloat64Data{
Info: &metric.HistogramFloat64{
Name: "latencyDistribution",
Description: "the various latencies",
Buckets: []float64{0, 10, 50, 100, 200, 400, 800, 1000, 1400, 2000, 5000, 10000, 15000},
},
}

distribution.Info.Record(&exporter.metrics, mLatency)

for {
sleep := randomSleep()
_, end := event.StartSpan(ctx, "main.randomSleep()")
time.Sleep(time.Duration(sleep) * time.Millisecond)
end()
event.Record(ctx, mLatency.Of(float64(sleep)))

fmt.Println("Latency: ", float64(sleep))
}
}

func randomSleep() int64 {
var max int64
switch modulus := time.Now().Unix() % 5; modulus {
case 0:
max = 17001
case 1:
max = 8007
case 2:
max = 917
case 3:
max = 87
case 4:
max = 1173
}
return rand.Int63n(max)
}

```
3. Run the new file from within the tools repository:
```bash
go run internal/main.go
```
4. After about 5 seconds, OpenCensus should start receiving your new metrics, which you can see at http://localhost:8844/metrics. This page will look similar to the following:
```
# HELP promdemo_latencyDistribution the various latencies
# TYPE promdemo_latencyDistribution histogram
promdemo_latencyDistribution_bucket{vendor="otc",le="0"} 0
promdemo_latencyDistribution_bucket{vendor="otc",le="10"} 2
promdemo_latencyDistribution_bucket{vendor="otc",le="50"} 9
promdemo_latencyDistribution_bucket{vendor="otc",le="100"} 22
promdemo_latencyDistribution_bucket{vendor="otc",le="200"} 35
promdemo_latencyDistribution_bucket{vendor="otc",le="400"} 49
promdemo_latencyDistribution_bucket{vendor="otc",le="800"} 63
promdemo_latencyDistribution_bucket{vendor="otc",le="1000"} 78
promdemo_latencyDistribution_bucket{vendor="otc",le="1400"} 93
promdemo_latencyDistribution_bucket{vendor="otc",le="2000"} 108
promdemo_latencyDistribution_bucket{vendor="otc",le="5000"} 123
promdemo_latencyDistribution_bucket{vendor="otc",le="10000"} 138
promdemo_latencyDistribution_bucket{vendor="otc",le="15000"} 153
promdemo_latencyDistribution_bucket{vendor="otc",le="+Inf"} 15
promdemo_latencyDistribution_sum{vendor="otc"} 1641
promdemo_latencyDistribution_count{vendor="otc"} 15
```
5. After a few more seconds, Prometheus should start displaying your new metrics. You can view the distribution at http://localhost:9445/graph?g0.range_input=5m&g0.stacked=1&g0.expr=rate(oragent_latencyDistribution_bucket%5B5m%5D)&g0.tab=0.

6. Zipkin should also start displaying traces. You can view them at http://localhost:9444/zipkin/?limit=10&lookback=300000&serviceName=go-tools-test.
213 changes: 213 additions & 0 deletions event/export/ocagent/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
// 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 ocagent

import (
"time"

"github.com/qiniu/x/event/export/metric"
"github.com/qiniu/x/event/export/ocagent/wire"
"github.com/qiniu/x/event/label"
)

// dataToMetricDescriptor return a *wire.MetricDescriptor based on data.
func dataToMetricDescriptor(data metric.Data) *wire.MetricDescriptor {
if data == nil {
return nil
}

Check warning on line 19 in event/export/ocagent/metrics.go

View check run for this annotation

Codecov / codecov/patch

event/export/ocagent/metrics.go#L18-L19

Added lines #L18 - L19 were not covered by tests
descriptor := &wire.MetricDescriptor{
Name: data.Handle(),
Description: getDescription(data),
// TODO: Unit?
Type: dataToMetricDescriptorType(data),
LabelKeys: getLabelKeys(data),
}

return descriptor
}

// getDescription returns the description of data.
func getDescription(data metric.Data) string {
switch d := data.(type) {
case *metric.Int64Data:
return d.Info.Description

Check warning on line 35 in event/export/ocagent/metrics.go

View check run for this annotation

Codecov / codecov/patch

event/export/ocagent/metrics.go#L34-L35

Added lines #L34 - L35 were not covered by tests

case *metric.Float64Data:
return d.Info.Description

Check warning on line 38 in event/export/ocagent/metrics.go

View check run for this annotation

Codecov / codecov/patch

event/export/ocagent/metrics.go#L37-L38

Added lines #L37 - L38 were not covered by tests

case *metric.HistogramInt64Data:
return d.Info.Description

case *metric.HistogramFloat64Data:
return d.Info.Description
}

return ""

Check warning on line 47 in event/export/ocagent/metrics.go

View check run for this annotation

Codecov / codecov/patch

event/export/ocagent/metrics.go#L47

Added line #L47 was not covered by tests
}

// getLabelKeys returns a slice of *wire.LabelKeys based on the keys
// in data.
func getLabelKeys(data metric.Data) []*wire.LabelKey {
switch d := data.(type) {
case *metric.Int64Data:
return infoKeysToLabelKeys(d.Info.Keys)

Check warning on line 55 in event/export/ocagent/metrics.go

View check run for this annotation

Codecov / codecov/patch

event/export/ocagent/metrics.go#L54-L55

Added lines #L54 - L55 were not covered by tests

case *metric.Float64Data:
return infoKeysToLabelKeys(d.Info.Keys)

Check warning on line 58 in event/export/ocagent/metrics.go

View check run for this annotation

Codecov / codecov/patch

event/export/ocagent/metrics.go#L57-L58

Added lines #L57 - L58 were not covered by tests

case *metric.HistogramInt64Data:
return infoKeysToLabelKeys(d.Info.Keys)

case *metric.HistogramFloat64Data:
return infoKeysToLabelKeys(d.Info.Keys)
}

return nil

Check warning on line 67 in event/export/ocagent/metrics.go

View check run for this annotation

Codecov / codecov/patch

event/export/ocagent/metrics.go#L67

Added line #L67 was not covered by tests
}

// dataToMetricDescriptorType returns a wire.MetricDescriptor_Type based on the
// underlying type of data.
func dataToMetricDescriptorType(data metric.Data) wire.MetricDescriptor_Type {
switch d := data.(type) {
case *metric.Int64Data:
if d.IsGauge {
return wire.MetricDescriptor_GAUGE_INT64
}
return wire.MetricDescriptor_CUMULATIVE_INT64

Check warning on line 78 in event/export/ocagent/metrics.go

View check run for this annotation

Codecov / codecov/patch

event/export/ocagent/metrics.go#L74-L78

Added lines #L74 - L78 were not covered by tests

case *metric.Float64Data:
if d.IsGauge {
return wire.MetricDescriptor_GAUGE_DOUBLE
}
return wire.MetricDescriptor_CUMULATIVE_DOUBLE

Check warning on line 84 in event/export/ocagent/metrics.go

View check run for this annotation

Codecov / codecov/patch

event/export/ocagent/metrics.go#L80-L84

Added lines #L80 - L84 were not covered by tests

case *metric.HistogramInt64Data:
return wire.MetricDescriptor_CUMULATIVE_DISTRIBUTION

case *metric.HistogramFloat64Data:
return wire.MetricDescriptor_CUMULATIVE_DISTRIBUTION
}

return wire.MetricDescriptor_UNSPECIFIED

Check warning on line 93 in event/export/ocagent/metrics.go

View check run for this annotation

Codecov / codecov/patch

event/export/ocagent/metrics.go#L93

Added line #L93 was not covered by tests
}

// dataToTimeseries returns a slice of *wire.TimeSeries based on the
// points in data.
func dataToTimeseries(data metric.Data, start time.Time) []*wire.TimeSeries {
if data == nil {
return nil
}

Check warning on line 101 in event/export/ocagent/metrics.go

View check run for this annotation

Codecov / codecov/patch

event/export/ocagent/metrics.go#L100-L101

Added lines #L100 - L101 were not covered by tests

numRows := numRows(data)
startTimestamp := convertTimestamp(start)
timeseries := make([]*wire.TimeSeries, 0, numRows)

for i := 0; i < numRows; i++ {
timeseries = append(timeseries, &wire.TimeSeries{
StartTimestamp: &startTimestamp,
// TODO: labels?
Points: dataToPoints(data, i),
})
}

return timeseries
}

// numRows returns the number of rows in data.
func numRows(data metric.Data) int {
switch d := data.(type) {
case *metric.Int64Data:
return len(d.Rows)
case *metric.Float64Data:
return len(d.Rows)

Check warning on line 124 in event/export/ocagent/metrics.go

View check run for this annotation

Codecov / codecov/patch

event/export/ocagent/metrics.go#L121-L124

Added lines #L121 - L124 were not covered by tests
case *metric.HistogramInt64Data:
return len(d.Rows)
case *metric.HistogramFloat64Data:
return len(d.Rows)
}

return 0

Check warning on line 131 in event/export/ocagent/metrics.go

View check run for this annotation

Codecov / codecov/patch

event/export/ocagent/metrics.go#L131

Added line #L131 was not covered by tests
}

// dataToPoints returns an array of *wire.Points based on the point(s)
// in data at index i.
func dataToPoints(data metric.Data, i int) []*wire.Point {
switch d := data.(type) {
case *metric.Int64Data:
timestamp := convertTimestamp(d.EndTime)
return []*wire.Point{
{
Value: wire.PointInt64Value{
Int64Value: d.Rows[i],
},
Timestamp: &timestamp,
},
}
case *metric.Float64Data:
timestamp := convertTimestamp(d.EndTime)
return []*wire.Point{
{
Value: wire.PointDoubleValue{
DoubleValue: d.Rows[i],
},
Timestamp: &timestamp,
},
}

Check warning on line 157 in event/export/ocagent/metrics.go

View check run for this annotation

Codecov / codecov/patch

event/export/ocagent/metrics.go#L138-L157

Added lines #L138 - L157 were not covered by tests
case *metric.HistogramInt64Data:
row := d.Rows[i]
bucketBounds := make([]float64, len(d.Info.Buckets))
for i, val := range d.Info.Buckets {
bucketBounds[i] = float64(val)
}
return distributionToPoints(row.Values, row.Count, float64(row.Sum), bucketBounds, d.EndTime)
case *metric.HistogramFloat64Data:
row := d.Rows[i]
return distributionToPoints(row.Values, row.Count, row.Sum, d.Info.Buckets, d.EndTime)
}

return nil

Check warning on line 170 in event/export/ocagent/metrics.go

View check run for this annotation

Codecov / codecov/patch

event/export/ocagent/metrics.go#L170

Added line #L170 was not covered by tests
}

// distributionToPoints returns an array of *wire.Points containing a
// wire.PointDistributionValue representing a distribution with the
// supplied counts, count, and sum.
func distributionToPoints(counts []int64, count int64, sum float64, bucketBounds []float64, end time.Time) []*wire.Point {
buckets := make([]*wire.Bucket, len(counts))
for i := 0; i < len(counts); i++ {
buckets[i] = &wire.Bucket{
Count: counts[i],
}
}
timestamp := convertTimestamp(end)
return []*wire.Point{
{
Value: wire.PointDistributionValue{
DistributionValue: &wire.DistributionValue{
Count: count,
Sum: sum,
// TODO: SumOfSquaredDeviation?
Buckets: buckets,
BucketOptions: &wire.BucketOptionsExplicit{
Bounds: bucketBounds,
},
},
},
Timestamp: &timestamp,
},
}
}

// infoKeysToLabelKeys returns an array of *wire.LabelKeys containing the
// string values of the elements of labelKeys.
func infoKeysToLabelKeys(infoKeys []label.Key) []*wire.LabelKey {
labelKeys := make([]*wire.LabelKey, 0, len(infoKeys))
for _, key := range infoKeys {
labelKeys = append(labelKeys, &wire.LabelKey{
Key: key.Name(),
})
}

return labelKeys
}
Loading