added config variables support
This commit is contained in:
parent
19f2d028a6
commit
1017a0cd3c
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
78
config.yml
78
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:
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
6
main.go
6
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)
|
||||
|
|
Loading…
Reference in New Issue