From 1017a0cd3c66a6e0a2006fdc5d63a3aafecf217a Mon Sep 17 00:00:00 2001 From: sqshq Date: Mon, 8 Apr 2019 22:04:08 -0400 Subject: [PATCH] added config variables support --- component/asciibox/asciibox.go | 5 +- component/runchart/grid.go | 23 ++++++++++ component/runchart/runchart.go | 17 ------- component/sparkline/sparkline.go | 6 ++- config.yml | 78 ++++++++++++++++---------------- config/config.go | 1 + config/options.go | 2 +- data/sampler.go | 19 +++++++- main.go | 6 ++- 9 files changed, 93 insertions(+), 64 deletions(-) diff --git a/component/asciibox/asciibox.go b/component/asciibox/asciibox.go index 5159816..098d85f 100644 --- a/component/asciibox/asciibox.go +++ b/component/asciibox/asciibox.go @@ -9,6 +9,7 @@ import ( "github.com/sqshq/sampler/console" "github.com/sqshq/sampler/data" "image" + "strings" ) type AsciiBox struct { @@ -56,8 +57,8 @@ func NewAsciiBox(c config.AsciiBoxConfig, palette console.Palette) *AsciiBox { for { select { case sample := <-box.SampleChannel: - box.text = sample.Value - box.ascii, _ = box.render.RenderOpts(sample.Value, box.options) + box.text = strings.TrimSpace(sample.Value) + box.ascii, _ = box.render.RenderOpts(box.text, box.options) case alert := <-box.AlertChannel: box.alert = alert } diff --git a/component/runchart/grid.go b/component/runchart/grid.go index 161515e..a019df5 100644 --- a/component/runchart/grid.go +++ b/component/runchart/grid.go @@ -8,6 +8,8 @@ import ( "time" ) +const DefaultValueLength = 4 + type ChartGrid struct { timeRange TimeRange timePerPoint time.Duration @@ -136,3 +138,24 @@ func getLocalExtrema(items []TimeLine, timeRange TimeRange) ValueExtrema { func (r *TimeRange) isInRange(time time.Time) bool { return time.After(r.min) && time.Before(r.max) } + +// TODO add boundaries for values in range +func (c *RunChart) getMaxValueLength() int { + + maxValueLength := -1 + + for _, line := range c.lines { + for _, point := range line.points { + l := len(util.FormatValue(point.value, c.scale)) + if l > maxValueLength { + maxValueLength = l + } + } + } + + if maxValueLength < 0 { + return DefaultValueLength + } + + return maxValueLength +} diff --git a/component/runchart/runchart.go b/component/runchart/runchart.go index 03bc747..ab5fa86 100644 --- a/component/runchart/runchart.go +++ b/component/runchart/runchart.go @@ -315,23 +315,6 @@ func (c *RunChart) calculateTimeCoordinate(t time.Time) int { return c.grid.maxTimeWidth - int(math.Ceil(float64(xAxisGridWidth)*timeDeltaToPaddingRelation)) } -// TODO add boundaries for values in range -func (c *RunChart) getMaxValueLength() int { - - maxValueLength := 0 - - for _, line := range c.lines { - for _, point := range line.points { - l := len(util.FormatValue(point.value, c.scale)) - if l > maxValueLength { - maxValueLength = l - } - } - } - - return maxValueLength -} - func (c *RunChart) moveSelection(shift int) { if c.mode == ModeDefault { diff --git a/component/sparkline/sparkline.go b/component/sparkline/sparkline.go index b0451e1..a9c7e03 100644 --- a/component/sparkline/sparkline.go +++ b/component/sparkline/sparkline.go @@ -114,8 +114,10 @@ func (s *SparkLine) Draw(buffer *ui.Buffer) { buffer.SetCell(ui.NewCell(console.SymbolVerticalBar, ui.NewStyle(console.GetGradientColor(s.gradient, j-1, height))), image.Pt(s.Inner.Max.X-n-indent, s.Inner.Max.Y-j)) if i == len(s.values)-1 && j == top { buffer.SetString(curValue, textStyle, image.Pt(s.Inner.Max.X-n-indent+2, s.Inner.Max.Y-j)) - buffer.SetString(minValue, textStyle, image.Pt(s.Inner.Max.X-n-indent+2, s.Max.Y-2)) - buffer.SetString(maxValue, textStyle, image.Pt(s.Inner.Max.X-n-indent+2, s.Min.Y+1)) + if s.maxValue != s.minValue { + buffer.SetString(minValue, textStyle, image.Pt(s.Inner.Max.X-n-indent+2, s.Max.Y-2)) + buffer.SetString(maxValue, textStyle, image.Pt(s.Inner.Max.X-n-indent+2, s.Min.Y+1)) + } } } } diff --git a/config.yml b/config.yml index b48812c..80eb868 100644 --- a/config.yml +++ b/config.yml @@ -1,22 +1,24 @@ +variables: + mongoconnection: mongo --quiet --host=localhost blog runcharts: - title: SEARCH ENGINE RESPONSE TIME (sec) position: [[0, 0], [52, 16]] triggers: - - title: Latency threshold exceeded - condition: echo "$prev < 0.8 && $cur > 0.8" |bc -l - actions: - terminal-bell: true - sound: true - visual: true - script: 'say alert: ${label} latency exceeded ${cur} second' + - title: Latency threshold exceeded + condition: echo "$prev < 0.8 && $cur > 0.8" |bc -l + actions: + terminal-bell: true + sound: true + visual: true + script: 'say alert: ${label} latency exceeded ${cur} second' scale: 3 items: - - label: GOOGLE - sample: curl -o /dev/null -s -w '%{time_total}' https://www.google.com - - label: YAHOO - sample: curl -o /dev/null -s -w '%{time_total}' https://search.yahoo.com - - label: BING - sample: curl -o /dev/null -s -w '%{time_total}' https://www.bing.com + - label: GOOGLE + sample: curl -o /dev/null -s -w '%{time_total}' https://www.google.com + - label: YAHOO + sample: curl -o /dev/null -s -w '%{time_total}' https://search.yahoo.com + - label: BING + sample: curl -o /dev/null -s -w '%{time_total}' https://www.bing.com - title: MONGO COLLECTIONS COUNT position: [[53, 0], [27, 8]] legend: @@ -24,32 +26,32 @@ runcharts: details: false scale: 0 items: - - label: ACTIVE - init: mongo --quiet --host=localhost blog - sample: db.getCollection('posts').find({status:'ACTIVE'}).itcount() - transform: $sample | grep cpu - - label: INACTIVE - sample: mongo --quiet --host=localhost blog --eval "db.getCollection('posts').find({status:'INACTIVE'}).itcount()" + - label: ACTIVE + init: ${mongoconnection} + sample: db.getCollection('posts').find({status:'ACTIVE'}).itcount() + - label: INACTIVE + init: ${mongoconnection} + sample: db.getCollection('posts').find({status:'INACTIVE'}).itcount() barcharts: - title: EVENTS BY STATUS position: [[0, 17], [27, 12]] rate-ms: 300 scale: 0 items: - - label: NEW - init: mongo --quiet --host=localhost blog - sample: db.getCollection('posts').find({status:'ACTIVE'}).itcount() - - label: TRIGGERED - init: mongo --quiet --host=localhost blog - sample: db.getCollection('posts').find({status:'INACTIVE'}).itcount() - - label: IN_PROCESS - sample: echo 0 - - label: FAILED - init: mongo --quiet --host=localhost blog - sample: db.getCollection('posts').find({status:'ACTIVE'}).itcount() - - label: FINISHED - init: mongo --quiet --host=localhost blog - sample: db.getCollection('posts').find({status:'INACTIVE'}).itcount() + - label: NEW + init: ${mongoconnection} + sample: db.getCollection('posts').find({status:'ACTIVE'}).itcount() + - label: TRIGGERED + init: ${mongoconnection} + sample: db.getCollection('posts').find({status:'INACTIVE'}).itcount() + - label: IN_PROCESS + sample: echo 0 + - label: FAILED + init: ${mongoconnection} + sample: db.getCollection('posts').find({status:'ACTIVE'}).itcount() + - label: FINISHED + init: ${mongoconnection} + sample: db.getCollection('posts').find({status:'INACTIVE'}).itcount() gauges: - title: YEAR PROGRESS position: [[53, 8], [27, 2]] @@ -78,11 +80,11 @@ gauges: - title: MINUTE PROGRESS position: [[53, 14], [27, 2]] triggers: - - title: CLOCK BELL EVERY MINUTE - condition: '[ $label == "cur" ] && [ $cur -eq 0 ] && echo 1 || echo 0' - actions: - sound: true - script: say -v samantha `date +%I:%M%p` + - title: CLOCK BELL EVERY MINUTE + condition: '[ $label == "cur" ] && [ $cur -eq 0 ] && echo 1 || echo 0' + actions: + sound: true + script: say -v samantha `date +%I:%M%p` cur: sample: date +%S max: diff --git a/config/config.go b/config/config.go index 8ff9454..34327fd 100644 --- a/config/config.go +++ b/config/config.go @@ -12,6 +12,7 @@ import ( type Config struct { Theme *console.Theme `yaml:"theme,omitempty"` + Variables map[string]string `yaml:"variables,omitempty"` RunCharts []RunChartConfig `yaml:"runcharts,omitempty"` BarCharts []BarChartConfig `yaml:"barcharts,omitempty"` Gauges []GaugeConfig `yaml:"gauges,omitempty"` diff --git a/config/options.go b/config/options.go index da0d382..849dd52 100644 --- a/config/options.go +++ b/config/options.go @@ -2,6 +2,6 @@ package config type Options struct { ConfigFile string `short:"c" long:"config" required:"true" description:"path to YAML config file"` - Variables []string `short:"v" long:"variable" required:"false" description:"specify name=value variable to use in script placeholder as $name" long-description:"one or more variables can be specified as flags, in order to replace repeated patterns in the scripts, which can be replaced with {$variable-name} placeholder" ` + Variables []string `short:"v" long:"variable" required:"false" description:"specify name=value variable to use in script placeholder as $name. This flag takes precedence over the same name variables, specified in config yml" long-description:"one or more variables can be specified as flags, in order to replace repeated patterns in the scripts, which can be replaced with {$variable-name} placeholder" ` Examples []string `short:"e" long:"example" required:"false" choice:"runchart" choice:"barchart" choice:"asciibox" choice:"textbox" choice:"gauge" choice:"sparkline" description:"add an example component to the specified config file" long-description:"one or more example component types can be added to the specified config file, in order to jump-start the configuration"` } diff --git a/data/sampler.go b/data/sampler.go index 59323e0..c3e5b25 100644 --- a/data/sampler.go +++ b/data/sampler.go @@ -1,6 +1,7 @@ package data import ( + "fmt" "github.com/sqshq/sampler/config" "time" ) @@ -10,9 +11,10 @@ type Sampler struct { items []*Item triggers []*Trigger triggersChannel chan *Sample + variables []string } -func NewSampler(consumer *Consumer, items []*Item, triggers []*Trigger, options config.Options, rateMs int) Sampler { +func NewSampler(consumer *Consumer, items []*Item, triggers []*Trigger, options config.Options, fileVariables map[string]string, rateMs int) Sampler { ticker := time.NewTicker(time.Duration(rateMs * int(time.Millisecond))) @@ -21,6 +23,7 @@ func NewSampler(consumer *Consumer, items []*Item, triggers []*Trigger, options items, triggers, make(chan *Sample), + mergeVariables(fileVariables, options.Variables), } go func() { @@ -47,7 +50,7 @@ func NewSampler(consumer *Consumer, items []*Item, triggers []*Trigger, options func (s *Sampler) sample(item *Item, options config.Options) { - val, err := item.nextValue(options.Variables) + val, err := item.nextValue(s.variables) if len(val) > 0 { sample := &Sample{Label: item.Label, Value: val, Color: item.Color} @@ -61,3 +64,15 @@ func (s *Sampler) sample(item *Item, options config.Options) { } } } + +// option variables takes precedence over the file variables with the same name +func mergeVariables(fileVariables map[string]string, optionsVariables []string) []string { + + result := optionsVariables + + for key, value := range fileVariables { + result = append([]string{fmt.Sprintf("%s=%s", key, value)}, result...) + } + + return result +} diff --git a/main.go b/main.go index 7f75951..a29f242 100644 --- a/main.go +++ b/main.go @@ -20,12 +20,14 @@ type Starter struct { lout *layout.Layout player *asset.AudioPlayer opt config.Options + cfg config.Config } func (s *Starter) start(drawable ui.Drawable, consumer *data.Consumer, componentConfig config.ComponentConfig, itemsConfig []config.Item, triggersConfig []config.TriggerConfig) { cpt := component.NewComponent(drawable, consumer, componentConfig) triggers := data.NewTriggers(triggersConfig, consumer, s.opt, s.player) - data.NewSampler(consumer, data.NewItems(itemsConfig, *componentConfig.RateMs), triggers, s.opt, *componentConfig.RateMs) + items := data.NewItems(itemsConfig, *componentConfig.RateMs) + data.NewSampler(consumer, items, triggers, s.opt, s.cfg.Variables, *componentConfig.RateMs) s.lout.AddComponent(cpt) } @@ -44,7 +46,7 @@ func main() { lout := layout.NewLayout(width, height, component.NewStatusLine(opt.ConfigFile, palette), component.NewMenu(palette)) - starter := &Starter{lout, player, opt} + starter := &Starter{lout, player, opt, cfg} for _, c := range cfg.RunCharts { cpt := runchart.NewRunChart(c, palette)