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/console"
|
||||||
"github.com/sqshq/sampler/data"
|
"github.com/sqshq/sampler/data"
|
||||||
"image"
|
"image"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AsciiBox struct {
|
type AsciiBox struct {
|
||||||
|
@ -56,8 +57,8 @@ func NewAsciiBox(c config.AsciiBoxConfig, palette console.Palette) *AsciiBox {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case sample := <-box.SampleChannel:
|
case sample := <-box.SampleChannel:
|
||||||
box.text = sample.Value
|
box.text = strings.TrimSpace(sample.Value)
|
||||||
box.ascii, _ = box.render.RenderOpts(sample.Value, box.options)
|
box.ascii, _ = box.render.RenderOpts(box.text, box.options)
|
||||||
case alert := <-box.AlertChannel:
|
case alert := <-box.AlertChannel:
|
||||||
box.alert = alert
|
box.alert = alert
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const DefaultValueLength = 4
|
||||||
|
|
||||||
type ChartGrid struct {
|
type ChartGrid struct {
|
||||||
timeRange TimeRange
|
timeRange TimeRange
|
||||||
timePerPoint time.Duration
|
timePerPoint time.Duration
|
||||||
|
@ -136,3 +138,24 @@ func getLocalExtrema(items []TimeLine, timeRange TimeRange) ValueExtrema {
|
||||||
func (r *TimeRange) isInRange(time time.Time) bool {
|
func (r *TimeRange) isInRange(time time.Time) bool {
|
||||||
return time.After(r.min) && time.Before(r.max)
|
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))
|
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) {
|
func (c *RunChart) moveSelection(shift int) {
|
||||||
|
|
||||||
if c.mode == ModeDefault {
|
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))
|
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 {
|
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(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))
|
if s.maxValue != s.minValue {
|
||||||
buffer.SetString(maxValue, textStyle, image.Pt(s.Inner.Max.X-n-indent+2, s.Min.Y+1))
|
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:
|
runcharts:
|
||||||
- title: SEARCH ENGINE RESPONSE TIME (sec)
|
- title: SEARCH ENGINE RESPONSE TIME (sec)
|
||||||
position: [[0, 0], [52, 16]]
|
position: [[0, 0], [52, 16]]
|
||||||
triggers:
|
triggers:
|
||||||
- title: Latency threshold exceeded
|
- title: Latency threshold exceeded
|
||||||
condition: echo "$prev < 0.8 && $cur > 0.8" |bc -l
|
condition: echo "$prev < 0.8 && $cur > 0.8" |bc -l
|
||||||
actions:
|
actions:
|
||||||
terminal-bell: true
|
terminal-bell: true
|
||||||
sound: true
|
sound: true
|
||||||
visual: true
|
visual: true
|
||||||
script: 'say alert: ${label} latency exceeded ${cur} second'
|
script: 'say alert: ${label} latency exceeded ${cur} second'
|
||||||
scale: 3
|
scale: 3
|
||||||
items:
|
items:
|
||||||
- label: GOOGLE
|
- label: GOOGLE
|
||||||
sample: curl -o /dev/null -s -w '%{time_total}' https://www.google.com
|
sample: curl -o /dev/null -s -w '%{time_total}' https://www.google.com
|
||||||
- label: YAHOO
|
- label: YAHOO
|
||||||
sample: curl -o /dev/null -s -w '%{time_total}' https://search.yahoo.com
|
sample: curl -o /dev/null -s -w '%{time_total}' https://search.yahoo.com
|
||||||
- label: BING
|
- label: BING
|
||||||
sample: curl -o /dev/null -s -w '%{time_total}' https://www.bing.com
|
sample: curl -o /dev/null -s -w '%{time_total}' https://www.bing.com
|
||||||
- title: MONGO COLLECTIONS COUNT
|
- title: MONGO COLLECTIONS COUNT
|
||||||
position: [[53, 0], [27, 8]]
|
position: [[53, 0], [27, 8]]
|
||||||
legend:
|
legend:
|
||||||
|
@ -24,32 +26,32 @@ runcharts:
|
||||||
details: false
|
details: false
|
||||||
scale: 0
|
scale: 0
|
||||||
items:
|
items:
|
||||||
- label: ACTIVE
|
- label: ACTIVE
|
||||||
init: mongo --quiet --host=localhost blog
|
init: ${mongoconnection}
|
||||||
sample: db.getCollection('posts').find({status:'ACTIVE'}).itcount()
|
sample: db.getCollection('posts').find({status:'ACTIVE'}).itcount()
|
||||||
transform: $sample | grep cpu
|
- label: INACTIVE
|
||||||
- label: INACTIVE
|
init: ${mongoconnection}
|
||||||
sample: mongo --quiet --host=localhost blog --eval "db.getCollection('posts').find({status:'INACTIVE'}).itcount()"
|
sample: db.getCollection('posts').find({status:'INACTIVE'}).itcount()
|
||||||
barcharts:
|
barcharts:
|
||||||
- title: EVENTS BY STATUS
|
- title: EVENTS BY STATUS
|
||||||
position: [[0, 17], [27, 12]]
|
position: [[0, 17], [27, 12]]
|
||||||
rate-ms: 300
|
rate-ms: 300
|
||||||
scale: 0
|
scale: 0
|
||||||
items:
|
items:
|
||||||
- label: NEW
|
- label: NEW
|
||||||
init: mongo --quiet --host=localhost blog
|
init: ${mongoconnection}
|
||||||
sample: db.getCollection('posts').find({status:'ACTIVE'}).itcount()
|
sample: db.getCollection('posts').find({status:'ACTIVE'}).itcount()
|
||||||
- label: TRIGGERED
|
- label: TRIGGERED
|
||||||
init: mongo --quiet --host=localhost blog
|
init: ${mongoconnection}
|
||||||
sample: db.getCollection('posts').find({status:'INACTIVE'}).itcount()
|
sample: db.getCollection('posts').find({status:'INACTIVE'}).itcount()
|
||||||
- label: IN_PROCESS
|
- label: IN_PROCESS
|
||||||
sample: echo 0
|
sample: echo 0
|
||||||
- label: FAILED
|
- label: FAILED
|
||||||
init: mongo --quiet --host=localhost blog
|
init: ${mongoconnection}
|
||||||
sample: db.getCollection('posts').find({status:'ACTIVE'}).itcount()
|
sample: db.getCollection('posts').find({status:'ACTIVE'}).itcount()
|
||||||
- label: FINISHED
|
- label: FINISHED
|
||||||
init: mongo --quiet --host=localhost blog
|
init: ${mongoconnection}
|
||||||
sample: db.getCollection('posts').find({status:'INACTIVE'}).itcount()
|
sample: db.getCollection('posts').find({status:'INACTIVE'}).itcount()
|
||||||
gauges:
|
gauges:
|
||||||
- title: YEAR PROGRESS
|
- title: YEAR PROGRESS
|
||||||
position: [[53, 8], [27, 2]]
|
position: [[53, 8], [27, 2]]
|
||||||
|
@ -78,11 +80,11 @@ gauges:
|
||||||
- title: MINUTE PROGRESS
|
- title: MINUTE PROGRESS
|
||||||
position: [[53, 14], [27, 2]]
|
position: [[53, 14], [27, 2]]
|
||||||
triggers:
|
triggers:
|
||||||
- title: CLOCK BELL EVERY MINUTE
|
- title: CLOCK BELL EVERY MINUTE
|
||||||
condition: '[ $label == "cur" ] && [ $cur -eq 0 ] && echo 1 || echo 0'
|
condition: '[ $label == "cur" ] && [ $cur -eq 0 ] && echo 1 || echo 0'
|
||||||
actions:
|
actions:
|
||||||
sound: true
|
sound: true
|
||||||
script: say -v samantha `date +%I:%M%p`
|
script: say -v samantha `date +%I:%M%p`
|
||||||
cur:
|
cur:
|
||||||
sample: date +%S
|
sample: date +%S
|
||||||
max:
|
max:
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Theme *console.Theme `yaml:"theme,omitempty"`
|
Theme *console.Theme `yaml:"theme,omitempty"`
|
||||||
|
Variables map[string]string `yaml:"variables,omitempty"`
|
||||||
RunCharts []RunChartConfig `yaml:"runcharts,omitempty"`
|
RunCharts []RunChartConfig `yaml:"runcharts,omitempty"`
|
||||||
BarCharts []BarChartConfig `yaml:"barcharts,omitempty"`
|
BarCharts []BarChartConfig `yaml:"barcharts,omitempty"`
|
||||||
Gauges []GaugeConfig `yaml:"gauges,omitempty"`
|
Gauges []GaugeConfig `yaml:"gauges,omitempty"`
|
||||||
|
|
|
@ -2,6 +2,6 @@ package config
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
ConfigFile string `short:"c" long:"config" required:"true" description:"path to YAML config file"`
|
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"`
|
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
|
package data
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/sqshq/sampler/config"
|
"github.com/sqshq/sampler/config"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -10,9 +11,10 @@ type Sampler struct {
|
||||||
items []*Item
|
items []*Item
|
||||||
triggers []*Trigger
|
triggers []*Trigger
|
||||||
triggersChannel chan *Sample
|
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)))
|
ticker := time.NewTicker(time.Duration(rateMs * int(time.Millisecond)))
|
||||||
|
|
||||||
|
@ -21,6 +23,7 @@ func NewSampler(consumer *Consumer, items []*Item, triggers []*Trigger, options
|
||||||
items,
|
items,
|
||||||
triggers,
|
triggers,
|
||||||
make(chan *Sample),
|
make(chan *Sample),
|
||||||
|
mergeVariables(fileVariables, options.Variables),
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -47,7 +50,7 @@ func NewSampler(consumer *Consumer, items []*Item, triggers []*Trigger, options
|
||||||
|
|
||||||
func (s *Sampler) sample(item *Item, options config.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 {
|
if len(val) > 0 {
|
||||||
sample := &Sample{Label: item.Label, Value: val, Color: item.Color}
|
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
|
lout *layout.Layout
|
||||||
player *asset.AudioPlayer
|
player *asset.AudioPlayer
|
||||||
opt config.Options
|
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) {
|
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)
|
cpt := component.NewComponent(drawable, consumer, componentConfig)
|
||||||
triggers := data.NewTriggers(triggersConfig, consumer, s.opt, s.player)
|
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)
|
s.lout.AddComponent(cpt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +46,7 @@ func main() {
|
||||||
|
|
||||||
lout := layout.NewLayout(width, height, component.NewStatusLine(opt.ConfigFile, palette), component.NewMenu(palette))
|
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 {
|
for _, c := range cfg.RunCharts {
|
||||||
cpt := runchart.NewRunChart(c, palette)
|
cpt := runchart.NewRunChart(c, palette)
|
||||||
|
|
Loading…
Reference in New Issue