Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
ymtdzzz committed Jul 14, 2024
1 parent ec99980 commit aab7ebd
Show file tree
Hide file tree
Showing 7 changed files with 259 additions and 6 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mostynb/go-grpc-compression v1.2.3 // indirect
github.com/navidys/tvxwidgets v0.6.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/client_golang v1.19.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mostynb/go-grpc-compression v1.2.3 h1:42/BKWMy0KEJGSdWvzqIyOZ95YcR9mLPqKctH7Uo//I=
github.com/mostynb/go-grpc-compression v1.2.3/go.mod h1:AghIxF3P57umzqM9yz795+y1Vjs47Km/Y2FE6ouQ7Lg=
github.com/navidys/tvxwidgets v0.6.0 h1:ARIXGfx4aURHMhq+LW5vIoCCDx1X/PdTF8AcUq+nWZ0=
github.com/navidys/tvxwidgets v0.6.0/go.mod h1:wd6aS2OzjZczFbg8GCaVuwkFcY1eixlT/y7Lc/YIwlg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
Expand Down
4 changes: 4 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -132,17 +132,21 @@ github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBj
github.com/go-latex/latex v0.0.0-20231108140139-5c1ce85aa4ea/go.mod h1:Y7Vld91/HRbTBm7JwoI7HejdDB0u+e9AUBO9MB7yuZk=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-pdf/fpdf v0.9.0/go.mod h1:oO8N111TkmKb9D7VvWGLvLJlaZUQVPM+6V42pp3iV4Y=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/goccmack/gocc v0.0.0-20230228185258-2292f9e40198/go.mod h1:DTh/Y2+NbnOVVoypCCQrovMPDKUGp4yZpSbWg5D0XIM=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
Expand Down
48 changes: 44 additions & 4 deletions tuiexporter/internal/telemetry/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ type TraceSpanDataMap map[string][]*SpanData
// This is used to quickly look up all spans in a trace for a service
type TraceServiceSpanDataMap map[string]map[string][]*SpanData

// TraceLogDataMap is a map of trace id to a slice of logs
// This is used to quickly look up all logs in a trace
type TraceLogDataMap map[string][]*LogData

// TraceCache is a cache of trace spans
type TraceCache struct {
spanid2span SpanDataMap
Expand Down Expand Up @@ -103,6 +99,10 @@ func (c *TraceCache) flush() {
c.tracesvc2spans = TraceServiceSpanDataMap{}
}

// TraceLogDataMap is a map of trace id to a slice of logs
// This is used to quickly look up all logs in a trace
type TraceLogDataMap map[string][]*LogData

// LogCache is a cache of logs
type LogCache struct {
traceid2logs TraceLogDataMap
Expand Down Expand Up @@ -149,3 +149,43 @@ func (c *LogCache) GetLogsByTraceID(traceID string) ([]*LogData, bool) {
func (c *LogCache) flush() {
c.traceid2logs = TraceLogDataMap{}
}

// MetricServiceMetricDataMap is a map of service name and metric name to a slice of metrics
// This is used to quickly look up datapoints in a service metric
type MetricServiceMetricDataMap map[string]map[string][]*MetricData

// MetricCache is a cache of metrics
type MetricCache struct {
svcmetric2metrics MetricServiceMetricDataMap
}

// NewMetricCache returns a new metric cache
func NewMetricCache() *MetricCache {
return &MetricCache{
svcmetric2metrics: MetricServiceMetricDataMap{},
}
}

// UpdateCache updates the cache with a new metric
func (c *MetricCache) UpdateCache(sname string, data *MetricData) {
mname := data.Metric.Name()
if sms, ok := c.svcmetric2metrics[sname]; ok {
if _, ok := sms[mname]; ok {
c.svcmetric2metrics[sname][mname] = append(c.svcmetric2metrics[sname][mname], data)
} else {
c.svcmetric2metrics[sname][mname] = []*MetricData{data}
}
} else {
c.svcmetric2metrics[sname] = map[string][]*MetricData{mname: {data}}
}
}

// GetMetricsBySvcAndMetricName returns all metrics for a given service name and metric name
func (c *MetricCache) GetMetricsBySvcAndMetricName(sname, mname string) ([]*MetricData, bool) {
if sms, ok := c.svcmetric2metrics[sname]; ok {
if ms, ok := sms[mname]; ok {
return ms, ok
}
}
return nil, false
}
16 changes: 15 additions & 1 deletion tuiexporter/internal/telemetry/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type Store struct {
svcspansFiltered SvcSpans
tracecache *TraceCache
metrics []*MetricData
metriccache *MetricCache
logs []*LogData
logsFiltered []*LogData
logcache *LogCache
Expand All @@ -86,6 +87,8 @@ func NewStore() *Store {
svcspans: SvcSpans{},
svcspansFiltered: SvcSpans{},
tracecache: NewTraceCache(),
metrics: []*MetricData{},
metriccache: NewMetricCache(),
logs: []*LogData{},
logsFiltered: []*LogData{},
logcache: NewLogCache(),
Expand All @@ -95,11 +98,16 @@ func NewStore() *Store {
}
}

// GetTraceCache returns the cache
// GetTraceCache returns the trace cache
func (s *Store) GetTraceCache() *TraceCache {
return s.tracecache
}

// GetMetricCache returns the metric cache
func (s *Store) GetMetricCache() *MetricCache {
return s.metriccache
}

// GetLogCache returns the log cache
func (s *Store) GetLogCache() *LogCache {
return s.logcache
Expand Down Expand Up @@ -230,6 +238,7 @@ func (s *Store) AddSpan(traces *ptrace.Traces) {
span := ss.Spans().At(si)
// attribute service.name is required
// see: https://opentelemetry.io/docs/specs/semconv/resource/#service
// TODO: set default value when service name is not set
sname, _ := rs.Resource().Attributes().Get("service.name")
sd := &SpanData{
Span: &span,
Expand Down Expand Up @@ -273,6 +282,10 @@ func (s *Store) AddMetric(metrics *pmetric.Metrics) {
sm := rm.ScopeMetrics().At(smi)

for si := 0; si < sm.Metrics().Len(); si++ {
sname := "N/A"
if snameattr, ok := rm.Resource().Attributes().Get("service.name"); ok {
sname = snameattr.AsString()
}
metric := sm.Metrics().At(si)
sd := &MetricData{
Metric: &metric,
Expand All @@ -281,6 +294,7 @@ func (s *Store) AddMetric(metrics *pmetric.Metrics) {
ReceivedAt: time.Now(),
}
s.metrics = append(s.metrics, sd)
s.metriccache.UpdateCache(sname, sd)
}
}
}
Expand Down
179 changes: 179 additions & 0 deletions tuiexporter/internal/tui/component/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ package component
import (
"fmt"
"log"
"sort"

"github.com/gdamore/tcell/v2"
"github.com/navidys/tvxwidgets"
"github.com/rivo/tview"
"github.com/ymtdzzz/otel-tui/tuiexporter/internal/telemetry"
"go.opentelemetry.io/collector/pdata/pmetric"
Expand Down Expand Up @@ -358,3 +361,179 @@ func getMetricInfoTree(m *telemetry.MetricData) *tview.TreeView {

return tree
}

func drawMetricChartByRow(store *telemetry.Store, row int) tview.Primitive {
m := store.GetMetricByIdx(row)
mcache := store.GetMetricCache()
sname := "N/A"
if snameattr, ok := m.ResourceMetric.Resource().Attributes().Get("service.name"); ok {
sname = snameattr.AsString()
}
ms, ok := mcache.GetMetricsBySvcAndMetricName(sname, m.Metric.Name())
if !ok {
return nil
}

// attribute name and value map
dataMap := make(map[string]map[string][]float64, 1)
attrkeys := []string{}

support := true
for _, m := range ms {
var (
val float64
attrs map[string]any
)

switch m.Metric.Type() {
case pmetric.MetricTypeGauge:
for dpi := 0; dpi < m.Metric.Gauge().DataPoints().Len(); dpi++ {
dp := m.Metric.Gauge().DataPoints().At(dpi)
attrs = dp.Attributes().AsRaw()
switch dp.ValueType() {
case pmetric.NumberDataPointValueTypeDouble:
val = dp.DoubleValue()
case pmetric.NumberDataPointValueTypeInt:
val = float64(dp.IntValue())
}
}
case pmetric.MetricTypeSum:
for dpi := 0; dpi < m.Metric.Sum().DataPoints().Len(); dpi++ {
dp := m.Metric.Sum().DataPoints().At(dpi)
attrs = dp.Attributes().AsRaw()
switch dp.ValueType() {
case pmetric.NumberDataPointValueTypeDouble:
val = dp.DoubleValue()
case pmetric.NumberDataPointValueTypeInt:
val = float64(dp.IntValue())
}
}
case pmetric.MetricTypeHistogram:
support = false
break

Check failure on line 413 in tuiexporter/internal/tui/component/metric.go

View workflow job for this annotation

GitHub Actions / Run lint for exporter

ineffective break statement. Did you mean to break out of the outer loop? (SA4011)

Check failure on line 413 in tuiexporter/internal/tui/component/metric.go

View workflow job for this annotation

GitHub Actions / Run lint for exporter

redundant break statement (S1023)
case pmetric.MetricTypeExponentialHistogram:
support = false
break

Check failure on line 416 in tuiexporter/internal/tui/component/metric.go

View workflow job for this annotation

GitHub Actions / Run lint for exporter

ineffective break statement. Did you mean to break out of the outer loop? (SA4011)

Check failure on line 416 in tuiexporter/internal/tui/component/metric.go

View workflow job for this annotation

GitHub Actions / Run lint for exporter

redundant break statement (S1023)
case pmetric.MetricTypeSummary:
support = false
break

Check failure on line 419 in tuiexporter/internal/tui/component/metric.go

View workflow job for this annotation

GitHub Actions / Run lint for exporter

ineffective break statement. Did you mean to break out of the outer loop? (SA4011)

Check failure on line 419 in tuiexporter/internal/tui/component/metric.go

View workflow job for this annotation

GitHub Actions / Run lint for exporter

redundant break statement (S1023)
}

if len(attrs) > 0 {
// sort keys
keys := make([]string, 0, len(attrs))
for k := range attrs {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
v := attrs[k]
vstr := fmt.Sprintf("%s", v)
if attrkey, ok := dataMap[k]; ok {
if _, ok := attrkey[vstr]; ok {
dataMap[k][vstr] = append(dataMap[k][vstr], val)
} else {
dataMap[k][vstr] = []float64{val}
}
} else {
attrkeys = append(attrkeys, k)
dataMap[k] = map[string][]float64{vstr: {val}}
}
}
} else {
k := "N/A"
vstr := "N/A"
if attrkey, ok := dataMap[k]; ok {
if _, ok := attrkey[vstr]; ok {
dataMap[k][vstr] = append(dataMap[k][vstr], val)
} else {
dataMap[k][vstr] = []float64{val}
}
} else {
attrkeys = append(attrkeys, k)
dataMap[k] = map[string][]float64{vstr: {val}}
}
}
}

chart := tview.NewFlex().SetDirection(tview.FlexColumn)

if support {
getDataToDraw := func(attrkey string) ([][]float64, *tview.Flex) {
// sort keys
keys := make([]string, 0, len(dataMap[attrkey]))
for k := range dataMap[attrkey] {
keys = append(keys, k)
}
sort.Strings(keys)
d := make([][]float64, len(keys))
txts := tview.NewFlex().SetDirection(tview.FlexRow)
count := 0
for _, k := range keys {
d[count] = dataMap[attrkey][k]
txt := tview.NewTextView()
txt.SetTextColor(colors[count])
txt.SetText(fmt.Sprintf("● %s: %s", attrkey, k))
txts.AddItem(txt, 2, 1, false)
count++
}
return d, txts
}

getTitle := func(idx int) string {
return fmt.Sprintf("%s [%d / %d] ( <- | -> )", attrkeys[idx], idx+1, len(attrkeys))
}

// Draw a chart of the first attribute
attrkeyidx := 0
data, txts := getDataToDraw(attrkeys[attrkeyidx])
ch := tvxwidgets.NewPlot()
ch.SetMarker(tvxwidgets.PlotMarkerBraille)
ch.SetTitle(getTitle(attrkeyidx))
ch.SetBorder(true)
ch.SetData(data)
ch.SetDrawXAxisLabel(false)
ch.SetLineColor(colors)

legend := tview.NewFlex().SetDirection(tview.FlexRow)
legend.AddItem(txts, 0, 1, false)

ch.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() {
case tcell.KeyRight:
if attrkeyidx < len(attrkeys)-1 {
attrkeyidx++
} else {
attrkeyidx = 0
}
ch.SetTitle(getTitle(attrkeyidx))
data, txts := getDataToDraw(attrkeys[attrkeyidx])
legend.Clear()
legend.AddItem(txts, 0, 1, false)
ch.SetData(data)
return nil
case tcell.KeyLeft:
if attrkeyidx > 0 {
attrkeyidx--
} else {
attrkeyidx = len(attrkeys) - 1
}
ch.SetTitle(getTitle(attrkeyidx))
data, txts := getDataToDraw(attrkeys[attrkeyidx])
legend.Clear()
legend.AddItem(txts, 0, 1, false)
ch.SetData(data)
return nil
}
return event
})

chart.AddItem(ch, 0, 7, true).AddItem(legend, 0, 3, false)

return chart
}

txt := tview.NewTextView().SetText("This metric type is not supported")
chart.AddItem(txt, 0, 1, false)
return chart
}
Loading

0 comments on commit aab7ebd

Please sign in to comment.