Skip to content

Commit e1c3065

Browse files
committed
Trying a span to view converter
1 parent fd2eca9 commit e1c3065

File tree

2 files changed

+132
-8
lines changed

2 files changed

+132
-8
lines changed

api/server/server.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import (
2828
"github.com/fnproject/fn/api/models"
2929
"github.com/fnproject/fn/api/mqs"
3030
pool "github.com/fnproject/fn/api/runnerpool"
31-
"github.com/fnproject/fn/api/server/spanexporter"
3231
"github.com/fnproject/fn/api/version"
3332
"github.com/fnproject/fn/fnext"
3433
"github.com/gin-gonic/gin"
@@ -739,13 +738,8 @@ func WithPrometheus() Option {
739738
}
740739
s.promExporter = exporter
741740
view.RegisterExporter(exporter)
742-
743-
spanExporter, err := spanexporter.NewExporter(spanexporter.Options{
744-
Namespace: "fn",
745-
Registry: reg,
746-
OnError: func(err error) { logrus.WithError(err).Error("opencensus prometheus span exporter err") },
747-
})
748-
trace.RegisterExporter(spanExporter)
741+
converter, _ := NewConverter(Options{Namespace: "fn", Exporter: exporter})
742+
trace.RegisterExporter(converter)
749743
trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
750744

751745
return nil

api/server/spanconverter.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package server
2+
3+
import (
4+
"strings"
5+
"sync"
6+
"time"
7+
"unicode"
8+
9+
"github.com/sirupsen/logrus"
10+
"go.opencensus.io/stats"
11+
view "go.opencensus.io/stats/view"
12+
"go.opencensus.io/trace"
13+
)
14+
15+
// Exporter exports stats to Prometheus, users need
16+
// to register the exporter as an http.Handler to be
17+
// able to export.
18+
type Converter struct {
19+
opts Options
20+
measures map[string]*stats.Float64Measure
21+
viewsMu sync.Mutex
22+
e view.Exporter
23+
}
24+
25+
// Options contains options for configuring the exporter.
26+
type Options struct {
27+
Namespace string
28+
Exporter view.Exporter
29+
}
30+
31+
func NewConverter(o Options) (*Converter, error) {
32+
e := &Converter{
33+
opts: o,
34+
e: o.Exporter,
35+
measures: make(map[string]*stats.Float64Measure),
36+
}
37+
return e, nil
38+
}
39+
40+
// ExportView exports to the Prometheus if view data has one or more rows.
41+
// Each OpenCensus AggregationData will be converted to
42+
// corresponding Prometheus Metric: SumData will be converted
43+
// to Untyped Metric, CountData will be a Counter Metric,
44+
// DistributionData will be a Histogram Metric.
45+
func (c *Converter) ExportSpan(sd *trace.SpanData) {
46+
m := c.getMeasure(sd)
47+
48+
spanTimeNanos := sd.EndTime.Sub(sd.StartTime)
49+
spanTimeMillis := float64(int64(spanTimeNanos / time.Millisecond))
50+
51+
m.M(spanTimeMillis)
52+
}
53+
54+
func (c *Converter) getMeasure(span *trace.SpanData) *stats.Float64Measure {
55+
sig := spanName(c.opts.Namespace, span)
56+
c.viewsMu.Lock()
57+
m, ok := c.measures[sig]
58+
c.viewsMu.Unlock()
59+
60+
if !ok {
61+
logrus.Info("Creating Measure: %s", sig)
62+
m = stats.Float64("span length", "The span length in milliseconds", "ms")
63+
v := &view.View{
64+
Name: spanName(c.opts.Namespace, span),
65+
Description: spanName(c.opts.Namespace, span),
66+
Measure: m,
67+
Aggregation: view.Distribution(0, 1<<32, 2<<32, 3<<32),
68+
}
69+
// Buckets: []float64{1,
70+
// 10,
71+
// 50,
72+
// 100,
73+
// 250,
74+
// 500,
75+
// 1000,
76+
// 10000,
77+
// 60000,
78+
// 120000},
79+
c.viewsMu.Lock()
80+
c.measures[sig] = m
81+
view.Register(v)
82+
c.viewsMu.Unlock()
83+
}
84+
85+
return m
86+
}
87+
88+
func spanName(namespace string, s *trace.SpanData) string {
89+
var name string
90+
if namespace != "" {
91+
name = namespace + "_"
92+
}
93+
return name + sanitize(s.Name)
94+
}
95+
96+
const labelKeySizeLimit = 100
97+
98+
// sanitize returns a string that is trunacated to 100 characters if it's too
99+
// long, and replaces non-alphanumeric characters to underscores.
100+
func sanitize(s string) string {
101+
if len(s) == 0 {
102+
return s
103+
}
104+
if len(s) > labelKeySizeLimit {
105+
s = s[:labelKeySizeLimit]
106+
}
107+
s = strings.Map(sanitizeRune, s)
108+
if unicode.IsDigit(rune(s[0])) {
109+
s = "key_" + s
110+
}
111+
if s[0] == '_' {
112+
s = "key" + s
113+
}
114+
return s
115+
}
116+
117+
// converts anything that is not a letter or digit to an underscore
118+
func sanitizeRune(r rune) rune {
119+
if unicode.IsLetter(r) || unicode.IsDigit(r) {
120+
return r
121+
}
122+
// Everything else turns into an underscore
123+
return '_'
124+
}
125+
126+
//Gin creates spans for all paths, containing ID values.
127+
//We can safely discard these, as other histograms are being created for them.
128+
func urlName(s string) bool {
129+
return strings.HasPrefix(s, "/")
130+
}

0 commit comments

Comments
 (0)