From df61b6b53ebc1346185d4e0ed2987853a3f59312 Mon Sep 17 00:00:00 2001 From: Curtis Mattoon Date: Thu, 9 Jun 2022 20:11:59 -0400 Subject: [PATCH 1/3] Adds support for YAML configuration files. --- cmd/grafterm/main.go | 26 +++-- go.mod | 47 ++++++++-- internal/model/dashboard.go | 94 +++++++++---------- internal/model/datasource.go | 24 ++--- internal/service/configuration/loader.go | 6 +- internal/service/configuration/v1/v1.go | 6 +- internal/service/configuration/yaml_loader.go | 59 ++++++++++++ .../service/configuration/yaml_loader_test.go | 84 +++++++++++++++++ 8 files changed, 269 insertions(+), 77 deletions(-) create mode 100644 internal/service/configuration/yaml_loader.go create mode 100644 internal/service/configuration/yaml_loader_test.go diff --git a/cmd/grafterm/main.go b/cmd/grafterm/main.go index bdf12d0..19b48a8 100644 --- a/cmd/grafterm/main.go +++ b/cmd/grafterm/main.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "os/signal" + "path/filepath" "syscall" "time" @@ -55,7 +56,7 @@ func (m *Main) Run() error { if err != nil { return err } - + m.logger.Infof("Configuration: %+v\n", cfg) ddss, err := cfg.Datasources() if err != nil { return err @@ -168,12 +169,25 @@ func loadConfiguration(cfgPath string) (configuration.Configuration, error) { } defer f.Close() - cfg, err := configuration.JSONLoader{}.Load(f) - if err != nil { - return nil, err - } + switch filepath.Ext(cfgPath) { + case ".yaml", ".yml": + fmt.Fprintln(os.Stdout, "Using YAML Loader...") + cfg, err := configuration.YAMLLoader{}.Load(f) + if err != nil { + return nil, err + } + return cfg, nil - return cfg, nil + case ".json": + fallthrough + default: + fmt.Fprintln(os.Stdout, "Using JSON Loader...") + cfg, err := configuration.JSONLoader{}.Load(f) + if err != nil { + return nil, err + } + return cfg, nil + } } func (m *Main) loadUserDatasources() ([]model.Datasource, error) { diff --git a/go.mod b/go.mod index 9ef1bac..a37a15d 100644 --- a/go.mod +++ b/go.mod @@ -1,20 +1,55 @@ module github.com/slok/grafterm +go 1.18 + require ( - github.com/DATA-DOG/go-sqlmock v1.3.3 // indirect github.com/JensRantil/graphite-client v0.0.0-20151206234601-d93bf4b72f5a github.com/alecthomas/kingpin v2.2.6+incompatible - github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect - github.com/alecthomas/units v0.0.0-20190910110746-680d30ca3117 // indirect github.com/influxdata/influxdb1-client v0.0.0-20190809212627-fc22c7df067e - github.com/kylelemons/godebug v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.0.1 - github.com/mattn/go-runewidth v0.0.4 // indirect github.com/mum4k/termdash v0.10.0 - github.com/nsf/termbox-go v0.0.0-20190624072549-eeb6cd0a1762 // indirect github.com/oklog/run v1.0.0 github.com/prometheus/client_golang v1.0.0 github.com/prometheus/common v0.6.0 github.com/rs/zerolog v1.13.0 github.com/stretchr/testify v1.4.0 ) + +require ( + github.com/DATA-DOG/go-sqlmock v1.3.3 // indirect + github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect + github.com/alecthomas/units v0.0.0-20190910110746-680d30ca3117 // indirect + github.com/beorn7/perks v1.0.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-kit/kit v0.8.0 // indirect + github.com/go-logfmt/logfmt v0.4.0 // indirect + github.com/go-stack/stack v1.8.0 // indirect + github.com/gogo/protobuf v1.1.1 // indirect + github.com/golang/protobuf v1.3.1 // indirect + github.com/json-iterator/go v1.1.6 // indirect + github.com/julienschmidt/httprouter v1.2.0 // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.1 // indirect + github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/mattn/go-runewidth v0.0.4 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 // indirect + github.com/nsf/termbox-go v0.0.0-20190624072549-eeb6cd0a1762 // indirect + github.com/pkg/errors v0.8.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect + github.com/prometheus/procfs v0.0.2 // indirect + github.com/sirupsen/logrus v1.2.0 // indirect + github.com/stretchr/objx v0.1.1 // indirect + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 // indirect + golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 // indirect + golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect + golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a // indirect + golang.org/x/text v0.3.0 // indirect + gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 // indirect + gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 // indirect + gopkg.in/yaml.v2 v2.2.2 // indirect +) diff --git a/internal/model/dashboard.go b/internal/model/dashboard.go index 8bdb305..64f741e 100644 --- a/internal/model/dashboard.go +++ b/internal/model/dashboard.go @@ -15,39 +15,39 @@ const ( // Dashboard represents a dashboard. type Dashboard struct { - Grid Grid `json:"grid,omitempty"` - Variables []Variable `json:"variables,omitempty"` - Widgets []Widget `json:"widgets,omitempty"` + Grid Grid `json:"grid,omitempty" yaml:"grid,omitempty"` + Variables []Variable `json:"variables,omitempty" yaml:"variables,omitempty"` + Widgets []Widget `json:"widgets,omitempty" yaml:"widgets,omitempty"` } // Variable is a dynamic variable that will be available through the // dashboard. type Variable struct { - Name string `json:"name,omitempty"` - VariableSource `json:",inline"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` + VariableSource `json:",inline" yaml:",inline"` } // VariableSource is the variable kind with it's data. type VariableSource struct { - Constant *ConstantVariableSource `json:"constant,omitempty"` - Interval *IntervalVariableSource `json:"interval,omitempty"` + Constant *ConstantVariableSource `json:"constant,omitempty" yaml:"constant,omitempty"` + Interval *IntervalVariableSource `json:"interval,omitempty" yaml:"interval,omitempty"` } // ConstantVariableSource represents the constant variables. type ConstantVariableSource struct { - Value string `json:"value,omitempty"` + Value string `json:"value,omitempty" yaml:"value,omitempty"` } // IntervalVariableSource represents the interval variables. type IntervalVariableSource struct { - Steps int `json:"steps,omitempty"` + Steps int `json:"steps,omitempty" yaml:"steps,omitempty"` } // Widget represents a widget. type Widget struct { - Title string `json:"title,omitempty"` - GridPos GridPos `json:"gridPos,omitempty"` - WidgetSource `json:",inline"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + GridPos GridPos `json:"gridPos,omitempty" yaml:"gridPos,omitempty"` + WidgetSource `json:",inline" yaml:",inline"` } // Grid represents the options of the grid in the dashboard. @@ -56,72 +56,72 @@ type Grid struct { // will be fixed and need X and Y values. // If false it will be adaptive and will ignore X and Y values // and only use the size of the widget (W, width). - FixedWidgets bool `json:"fixedWidgets,omitempty"` + FixedWidgets bool `json:"fixedWidgets,omitempty" yaml:"fixedWidgets,omitempty"` // MaxWidth is the maximum width (horizontal) the Grid will have, this will be // the scale for the widgets `GridPos.W`. For example a `GridPos.W: 50` // in a `Grid.MaxWidth: 100` would be half of the row, but in a `Grid.MaxWidth: 1000` // would be a 5% of the row. // Not setting MaxWidth or setting to 0 would fallback to default MaxWidth. - MaxWidth int `json:"maxWidth,omitempty"` + MaxWidth int `json:"maxWidth,omitempty" yaml:"maxWidth,omitempty"` } // GridPos represents the grid position. type GridPos struct { // X represents the position on the grid (from 0 to 100). - X int `json:"x,omitempty"` + X int `json:"x,omitempty" yaml:"x,omitempty"` // Y represents the position on the grid (from 0 to infinite, // where the total will be used using all the widgets Y and H). - Y int `json:"y,omitempty"` + Y int `json:"y,omitempty" yaml:"y,omitempty"` // W represents the width of the widget (same unit as X). - W int `json:"w,omitempty"` + W int `json:"w,omitempty" yaml:"w,omitempty"` // TODO(slok): H represents the height of the widget (same unit as Y). // H int `json:"h,omitempty"` } // WidgetSource will tell what kind of widget is. type WidgetSource struct { - Singlestat *SinglestatWidgetSource `json:"singlestat,omitempty"` - Gauge *GaugeWidgetSource `json:"gauge,omitempty"` - Graph *GraphWidgetSource `json:"graph,omitempty"` + Singlestat *SinglestatWidgetSource `json:"singlestat,omitempty" yaml:"singlestat,omitempty"` + Gauge *GaugeWidgetSource `json:"gauge,omitempty" yaml:"gauge,omitempty"` + Graph *GraphWidgetSource `json:"graph,omitempty" yaml:"graph,omitempty"` } // SinglestatWidgetSource represents a simple value widget. type SinglestatWidgetSource struct { - ValueRepresentation `json:",inline"` - Query Query `json:"query,omitempty"` - ValueText string `json:"valueText,omitempty"` - Thresholds []Threshold `json:"thresholds,omitempty"` + ValueRepresentation `json:",inline" yaml:",inline"` + Query Query `json:"query,omitempty" yaml:"query,omitempty"` + ValueText string `json:"valueText,omitempty" yaml:"valueText,omitempty"` + Thresholds []Threshold `json:"thresholds,omitempty" yaml:"thresholds,omitempty"` } // GaugeWidgetSource represents a simple value widget in donut format. type GaugeWidgetSource struct { - Query Query `json:"query,omitempty"` - PercentValue bool `json:"percentValue,omitempty"` - Max int `json:"max,omitempty"` - Min int `json:"min,omitempty"` - Thresholds []Threshold `json:"thresholds,omitempty"` + Query Query `json:"query,omitempty" yaml:"query,omitempty"` + PercentValue bool `json:"percentValue,omitempty" yaml:"percentValue,omitempty"` + Max int `json:"max,omitempty" yaml:"max,omitempty"` + Min int `json:"min,omitempty" yaml:"min,omitempty"` + Thresholds []Threshold `json:"thresholds,omitempty" yaml:"thresholds,omitempty"` } // GraphWidgetSource represents a simple value widget in donut format. type GraphWidgetSource struct { - Queries []Query `json:"queries,omitempty"` - Visualization GraphVisualization `json:"visualization,omitempty"` + Queries []Query `json:"queries,omitempty" yaml:"queries,omitempty"` + Visualization GraphVisualization `json:"visualization,omitempty" yaml:"vizualization,omitempty"` } // Query is the query that will be made to the datasource. type Query struct { - Expr string `json:"expr,omitempty"` + Expr string `json:"expr,omitempty" yaml:"expr,omitempty"` // Legend accepts `text.template` format. - Legend string `json:"legend,omitempty"` - DatasourceID string `json:"datasourceID,omitempty"` + Legend string `json:"legend,omitempty" yaml:"legend,omitempty"` + DatasourceID string `json:"datasourceID,omitempty" yaml:"datasourceID,omitempty"` } // Threshold is a color threshold that is composed // with the start value, 0 means the base or starting // threshold. type Threshold struct { - StartValue float64 `json:"startValue"` - Color string `json:"color"` + StartValue float64 `json:"startValue" yaml:"startValue"` + Color string `json:"color" yaml:"color"` } // GraphVisualization controls how the graph will visualize @@ -148,27 +148,27 @@ const ( // SeriesOverride will override visualization based on // the regex legend. type SeriesOverride struct { - Regex string `json:"regex,omitempty"` - CompiledRegex *regexp.Regexp `json:"-"` - Color string `json:"color,omitempty"` - NullPointMode NullPointMode `json:"nullPointMode,omitempty"` + Regex string `json:"regex,omitempty" yaml:"regex,omitempty"` + CompiledRegex *regexp.Regexp `json:"-" yaml:"-"` + Color string `json:"color,omitempty" yaml:"color,omitempty"` + NullPointMode NullPointMode `json:"nullPointMode,omitempty" yaml:"nullPointMode,omitempty"` } // Legend controls the legend of a widget. type Legend struct { - Disable bool `json:"disable,omitempty"` - RightSide bool `json:"rightSide,omitempty"` + Disable bool `json:"disable,omitempty" yaml:"disable,omitempty"` + RightSide bool `json:"rightSide,omitempty" yaml:"rightSide,omitempty"` } // YAxis controls the YAxis of a widget. type YAxis struct { - ValueRepresentation `json:",inline"` + ValueRepresentation `json:",inline" yaml:",inline"` } // ValueRepresentation controls the representation of a value. type ValueRepresentation struct { - Unit string `json:"unit,omitempty"` - Decimals int `json:"decimals,omitempty"` + Unit string `json:"unit,omitempty" yaml:"unit,omitempty"` + Decimals int `json:"decimals,omitempty" yaml:"decimals,omitempty"` } // Validate validates the object model is correct. @@ -263,11 +263,11 @@ func (g GridPos) validate(gr Grid) error { } if gr.FixedWidgets && g.X <= 0 { - return fmt.Errorf("widget grid position in a fixed grid should have am X position") + return fmt.Errorf("widget grid position in a fixed grid should have an X position") } if gr.FixedWidgets && g.Y <= 0 { - return fmt.Errorf("widget grid position in a fixed grid should have am Y position") + return fmt.Errorf("widget grid position in a fixed grid should have an Y position") } return nil diff --git a/internal/model/datasource.go b/internal/model/datasource.go index ebc48a1..0bc0845 100644 --- a/internal/model/datasource.go +++ b/internal/model/datasource.go @@ -5,15 +5,15 @@ import "fmt" // Datasource is where the data will be retrieved. type Datasource struct { ID string - DatasourceSource `json:",inline"` + DatasourceSource `json:",inline" yaml:",inline"` } // DatasourceSource represents the datasource. type DatasourceSource struct { - Fake *FakeDatasource `json:"fake,omitempty"` - Prometheus *PrometheusDatasource `json:"prometheus,omitempty"` - Graphite *GraphiteDatasource `json:"graphite,omitempty"` - InfluxDB *InfluxDBDatasource `json:"influxdb,omitempty"` + Fake *FakeDatasource `json:"fake,omitempty" yaml:"fake,omitempty"` + Prometheus *PrometheusDatasource `json:"prometheus,omitempty" yaml:"prometheus,omitempty"` + Graphite *GraphiteDatasource `json:"graphite,omitempty" yaml:"graphite,omitempty"` + InfluxDB *InfluxDBDatasource `json:"influxdb,omitempty" yaml:"influxdb,omitempty"` } // FakeDatasource is the fake datasource. @@ -21,21 +21,21 @@ type FakeDatasource struct{} // PrometheusDatasource is the Prometheus kind datasource. type PrometheusDatasource struct { - Address string `json:"address,omitempty"` + Address string `json:"address,omitempty" yaml:"address,omitempty"` } // GraphiteDatasource is the Graphite kind datasource. type GraphiteDatasource struct { - Address string `json:"address,omitempty"` + Address string `json:"address,omitempty" yaml:"address,omitempty"` } // InfluxDBDatasource is the Graphite kind datasource. type InfluxDBDatasource struct { - Address string `json:"address,omitempty"` - Insecure bool `json:"insecure,omitempty"` - Database string `json:"database,omitempty"` - Username string `json:"username,omitempty"` - Password string `json:"password,omitempty"` + Address string `json:"address,omitempty" yaml:"address,omitempty"` + Insecure bool `json:"insecure,omitempty" yaml:"insecure,omitempty"` + Database string `json:"database,omitempty" yaml:"database,omitempty"` + Username string `json:"username,omitempty" yaml:"username,omitempty"` + Password string `json:"password,omitempty" yaml:"password,omitempty"` } // Validate validates the object model is correct. diff --git a/internal/service/configuration/loader.go b/internal/service/configuration/loader.go index f2ead7f..ffd8c8b 100644 --- a/internal/service/configuration/loader.go +++ b/internal/service/configuration/loader.go @@ -22,7 +22,7 @@ func (j JSONLoader) Load(r io.Reader) (Configuration, error) { return nil, err } - cfg, err := newConfig(bs) + cfg, err := newJSONConfig(bs) if err != nil { return nil, err } @@ -35,9 +35,9 @@ func (j JSONLoader) Load(r io.Reader) (Configuration, error) { return cfg, nil } -// newConfig will get the correct object configuration +// newJSONConfig will get the correct object configuration // based on the version of the configuration file. -func newConfig(cfgData []byte) (Configuration, error) { +func newJSONConfig(cfgData []byte) (Configuration, error) { cfgVersion := &struct { meta.Meta }{} diff --git a/internal/service/configuration/v1/v1.go b/internal/service/configuration/v1/v1.go index d8c0be5..147e7c7 100644 --- a/internal/service/configuration/v1/v1.go +++ b/internal/service/configuration/v1/v1.go @@ -22,9 +22,9 @@ type Dashboard struct { // Configuration is the v1 configuration.Satisfies configuration.Configuration interface. type Configuration struct { - meta.Meta `json:",inline"` - V1Datasources map[string]*Datasource `json:"datasources,omitempty"` - V1Dashboard Dashboard `json:"dashboard,omitempty"` + meta.Meta `json:",inline" yaml:",inline"` + V1Datasources map[string]*Datasource `json:"datasources,omitempty" yaml:"datasources"` + V1Dashboard Dashboard `json:"dashboard,omitempty" yaml:"dashboard"` } // Version satisfies Configuration interface. diff --git a/internal/service/configuration/yaml_loader.go b/internal/service/configuration/yaml_loader.go new file mode 100644 index 0000000..cb26129 --- /dev/null +++ b/internal/service/configuration/yaml_loader.go @@ -0,0 +1,59 @@ +package configuration + +import ( + "fmt" + "io" + "io/ioutil" + + "gopkg.in/yaml.v2" + + "github.com/slok/grafterm/internal/service/configuration/meta" + v1 "github.com/slok/grafterm/internal/service/configuration/v1" +) + +// YAMLLoader will load configuration in YAML format. +// It autodetects the version configuration so the user +// doesn't know what version of configuration is loading. +type YAMLLoader struct{} + +// Load satisfies configuration.Loader interface. +func (j YAMLLoader) Load(r io.Reader) (Configuration, error) { + bs, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + cfg, err := newYAMLConfig(bs) + if err != nil { + return nil, err + } + + err = yaml.Unmarshal(bs, cfg) + if err != nil { + return nil, fmt.Errorf("error unmarshalling yaml: %s", err) + } + + return cfg, nil +} + +// newYAMLConfig will get the correct object configuration +// based on the version of the configuration file. +func newYAMLConfig(cfgData []byte) (Configuration, error) { + cfgVersion := &struct { + meta.Meta `yaml:",inline"` + }{} + err := yaml.Unmarshal(cfgData, cfgVersion) + if err != nil { + return nil, fmt.Errorf("error unmarshalling yaml: %s", err) + } + + var cfg Configuration + switch cfgVersion.Version { + case v1.Version: + cfg = &v1.Configuration{} + default: + return nil, fmt.Errorf("%s is not a valid configuration version", cfgVersion.Version) + } + + return cfg, nil +} diff --git a/internal/service/configuration/yaml_loader_test.go b/internal/service/configuration/yaml_loader_test.go new file mode 100644 index 0000000..2ee9444 --- /dev/null +++ b/internal/service/configuration/yaml_loader_test.go @@ -0,0 +1,84 @@ +package configuration_test + +import ( + "io" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/slok/grafterm/internal/service/configuration" +) + +func TestLoadYAML(t *testing.T) { + tests := []struct { + name string + config func() io.Reader + loader func() configuration.Loader + expVersion string + expErr bool + }{ + { + name: "Invalid YAML should return an error.", + loader: func() configuration.Loader { + return &configuration.YAMLLoader{} + }, + config: func() io.Reader { + return strings.NewReader(`{"version": "v1"}`) + }, + expVersion: "v1", + }, + { + name: "Unknown YAML version should error.", + loader: func() configuration.Loader { + return &configuration.YAMLLoader{} + }, + config: func() io.Reader { + return strings.NewReader(`version: v0.987654321`) + }, + expErr: true, + }, + { + name: "Nested 'meta.version' should fail", + loader: func() configuration.Loader { + return &configuration.YAMLLoader{} + }, + config: func() io.Reader { + return strings.NewReader(`# +--- +meta: + version: v1 +`) + }, + expErr: true, + }, + { + name: "Valid YAML V1 load.", + loader: func() configuration.Loader { + return &configuration.YAMLLoader{} + }, + config: func() io.Reader { + return strings.NewReader(`# +--- +version: v1 +`) + }, + expVersion: "v1", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + assert := assert.New(t) + + loader := test.loader() + gotcfg, err := loader.Load(test.config()) + + if test.expErr { + assert.Error(err) + } else if assert.NoError(err) { + assert.Equal(test.expVersion, gotcfg.Version()) + } + }) + } +} From c3c4fdb4193e25494aab2963774889124e457ef9 Mon Sep 17 00:00:00 2001 From: Curtis Mattoon Date: Thu, 9 Jun 2022 20:12:37 -0400 Subject: [PATCH 2/3] Copy JSON examples to YAML directory with "yq" --- dashboard-examples/{ => json}/gitlab.json | 0 dashboard-examples/{ => json}/go.json | 0 .../{ => json}/kubernetes-cluster-status.json | 0 dashboard-examples/{ => json}/red.json | 0 .../{ => json}/test-grid-adaptive.json | 0 .../{ => json}/test-grid-fixed.json | 0 dashboard-examples/{ => json}/wikimedia.json | 0 dashboard-examples/yaml/gitlab.yaml | 180 ++++++++++++++++ dashboard-examples/yaml/go.yaml | 128 ++++++++++++ .../yaml/kubernetes-cluster-status.yaml | 163 +++++++++++++++ dashboard-examples/yaml/red.yaml | 101 +++++++++ .../yaml/test-grid-adaptive.yaml | 76 +++++++ dashboard-examples/yaml/test-grid-fixed.yaml | 197 ++++++++++++++++++ dashboard-examples/yaml/wikimedia.yaml | 91 ++++++++ 14 files changed, 936 insertions(+) rename dashboard-examples/{ => json}/gitlab.json (100%) rename dashboard-examples/{ => json}/go.json (100%) rename dashboard-examples/{ => json}/kubernetes-cluster-status.json (100%) rename dashboard-examples/{ => json}/red.json (100%) rename dashboard-examples/{ => json}/test-grid-adaptive.json (100%) rename dashboard-examples/{ => json}/test-grid-fixed.json (100%) rename dashboard-examples/{ => json}/wikimedia.json (100%) create mode 100644 dashboard-examples/yaml/gitlab.yaml create mode 100644 dashboard-examples/yaml/go.yaml create mode 100644 dashboard-examples/yaml/kubernetes-cluster-status.yaml create mode 100644 dashboard-examples/yaml/red.yaml create mode 100644 dashboard-examples/yaml/test-grid-adaptive.yaml create mode 100644 dashboard-examples/yaml/test-grid-fixed.yaml create mode 100644 dashboard-examples/yaml/wikimedia.yaml diff --git a/dashboard-examples/gitlab.json b/dashboard-examples/json/gitlab.json similarity index 100% rename from dashboard-examples/gitlab.json rename to dashboard-examples/json/gitlab.json diff --git a/dashboard-examples/go.json b/dashboard-examples/json/go.json similarity index 100% rename from dashboard-examples/go.json rename to dashboard-examples/json/go.json diff --git a/dashboard-examples/kubernetes-cluster-status.json b/dashboard-examples/json/kubernetes-cluster-status.json similarity index 100% rename from dashboard-examples/kubernetes-cluster-status.json rename to dashboard-examples/json/kubernetes-cluster-status.json diff --git a/dashboard-examples/red.json b/dashboard-examples/json/red.json similarity index 100% rename from dashboard-examples/red.json rename to dashboard-examples/json/red.json diff --git a/dashboard-examples/test-grid-adaptive.json b/dashboard-examples/json/test-grid-adaptive.json similarity index 100% rename from dashboard-examples/test-grid-adaptive.json rename to dashboard-examples/json/test-grid-adaptive.json diff --git a/dashboard-examples/test-grid-fixed.json b/dashboard-examples/json/test-grid-fixed.json similarity index 100% rename from dashboard-examples/test-grid-fixed.json rename to dashboard-examples/json/test-grid-fixed.json diff --git a/dashboard-examples/wikimedia.json b/dashboard-examples/json/wikimedia.json similarity index 100% rename from dashboard-examples/wikimedia.json rename to dashboard-examples/json/wikimedia.json diff --git a/dashboard-examples/yaml/gitlab.yaml b/dashboard-examples/yaml/gitlab.yaml new file mode 100644 index 0000000..87e0a87 --- /dev/null +++ b/dashboard-examples/yaml/gitlab.yaml @@ -0,0 +1,180 @@ +version: v1 +datasources: + gitlab: + prometheus: + address: https://dashboards.gitlab.com/api/datasources/proxy/1/ +dashboard: + variables: + env: + constant: + value: gprd + monitor: + constant: + value: default + interval: + interval: + steps: 50 + widgets: + - title: GitLab.com + gridPos: + w: 10 + singlestat: + query: + datasourceID: gitlab + expr: avg_over_time(probe_success{env="{{.env}}",monitor="default",instance="https://gitlab.com", + job="blackbox-tls-redirect"}[{{.interval}}]) + valueText: '{{ if (lt .value 1.0) }}DOWN{{else}}UP{{end}}' + thresholds: + - color: '#d44a3a' + - color: '#2dc937' + startValue: 1 + - title: Infrastructure alerts + gridPos: + w: 10 + singlestat: + query: + datasourceID: gitlab + expr: sum(max_over_time(ALERTS{env="{{.env}}",monitor="{{.monitor}}",alertstate="firing"}[{{.interval}}])) + OR vector(0) + valueText: '{{ printf "%.0f" .value }}' + thresholds: + - color: '#2dc937' + - color: '#e7b416' + startValue: 1 + - color: '#d44a3a' + startValue: 10 + - title: GitLab registry + gridPos: + w: 10 + singlestat: + query: + datasourceID: gitlab + expr: avg_over_time(probe_success{env="{{.env}}",monitor="default",instance="https://registry.gitlab.com", + job="blackbox"}[{{.interval}}]) + valueText: '{{ if (lt .value 1.0) }}DOWN{{else}}UP{{end}}' + thresholds: + - color: '#d44a3a' + - color: '#2dc937' + startValue: 1 + - title: Application alerts + gridPos: + w: 10 + singlestat: + query: + datasourceID: gitlab + expr: sum(max_over_time(ALERTS{env="{{.env}}",monitor="app",alertstate="firing"}[{{.interval}}])) + or vector(0) + valueText: '{{ printf "%.0f" .value }}' + thresholds: + - color: '#2dc937' + - color: '#e7b416' + startValue: 1 + - color: '#d44a3a' + startValue: 10 + - title: Reboots (last 48h) + gridPos: + w: 10 + singlestat: + query: + datasourceID: gitlab + expr: sum(count((time() - node_boot_time_seconds{env="{{.env}}",monitor="default"}) + / 3600 < 48) by (fqdn)) + valueText: '{{ printf "%.0f" .value }}' + - title: GitLab Pages + gridPos: + w: 10 + singlestat: + query: + datasourceID: gitlab + expr: avg_over_time(probe_success{env="{{.env}}",instance="https://gitlab-examples.gitlab.io",job="blackbox"}[{{.interval}}]) + valueText: '{{ if (lt .value 1.0) }}DOWN{{else}}UP{{end}}' + thresholds: + - color: '#d44a3a' + - color: '#2dc937' + startValue: 1 + - title: GitLab Sign-in + gridPos: + w: 10 + singlestat: + query: + datasourceID: gitlab + expr: avg_over_time(probe_success{env="{{.env}}",instance="https://gitlab.com/users/sign_in",job="blackbox"}[{{.interval}}]) + valueText: '{{ if (lt .value 1.0) }}DOWN{{else}}UP{{end}}' + thresholds: + - color: '#d44a3a' + - color: '#2dc937' + startValue: 1 + - title: internal LB + gridPos: + w: 10 + singlestat: + query: + datasourceID: gitlab + expr: avg_over_time(probe_success{env="{{.env}}",instance="https://int.{{.env}}.gitlab.net/-/liveness",job="blackbox-internal"}[{{.interval}}]) + valueText: '{{ if (lt .value 1.0) }}DOWN{{else}}UP{{end}}' + thresholds: + - color: '#d44a3a' + - color: '#2dc937' + startValue: 1 + - title: Chef errors + gridPos: + w: 20 + singlestat: + query: + datasourceID: gitlab + expr: sum(round(avg_over_time(chef_client_error{env="{{.env}}"}[{{.interval}}]))) + valueText: '{{ printf "%.0f" .value }} NODES' + - title: Gitlab HAProxy backend responses + gridPos: + w: 100 + graph: + visualization: + legend: + disable: false + rightSide: false + yAxis: + unit: reqps + decimals: 2 + queries: + - datasourceID: gitlab + legend: '{{ .backend }}' + expr: sum(avg_over_time(backend_code:haproxy_server_http_responses_total:irate1m{env="{{.env}}",monitor="{{.monitor}}"}[{{.interval}}])) + by (backend) + - title: Gitlab HAProxy frontend responses + gridPos: + w: 100 + graph: + visualization: + legend: + disable: false + rightSide: false + yAxis: + unit: reqps + decimals: 2 + queries: + - datasourceID: gitlab + legend: '{{ .frontend }}' + expr: sum(avg_over_time(frontend_code:haproxy_frontend_http_responses_total:irate1m{env="{{.env}}",monitor="{{.monitor}}"}[{{.interval}}])) + by (frontend) + - title: Gitlab 5XX responses + gridPos: + w: 100 + graph: + visualization: + legend: + disable: false + rightSide: false + yAxis: + unit: reqps + decimals: 2 + queries: + - legend: haproxy 5xx {{ .backend }} + datasourceID: gitlab + expr: sum(avg_over_time(backend_code:haproxy_server_http_responses_total:irate1m{env="{{.env}}",monitor="{{.monitor}}",code="5xx"}[{{.interval}}])) + by (backend) + - legend: SD haproxy {{ .code }} + datasourceID: gitlab + expr: sum without (zone) (avg_over_time(zone:stackdriver_haproxy_errors:rate1m{env="{{.env}}",monitor="{{.monitor}}"}[{{.interval}}])) + - legend: SD haproxy timeout + datasourceID: gitlab + expr: sum without (zone) (zone:stackdriver_haproxy_deadline_exceeded:rate1m{env="{{.env}}",monitor="{{.monitor}}"}) diff --git a/dashboard-examples/yaml/go.yaml b/dashboard-examples/yaml/go.yaml new file mode 100644 index 0000000..2647d0c --- /dev/null +++ b/dashboard-examples/yaml/go.yaml @@ -0,0 +1,128 @@ +version: v1 +datasources: + prometheus: + prometheus: + address: http://127.0.0.1:9090 +dashboard: + variables: + job: + constant: + value: prometheus + interval: + interval: + steps: 50 + widgets: + - title: Goroutines + gridPos: + w: 20 + singlestat: + thresholds: + - color: '#47D038' + query: + datasourceID: prometheus + expr: sum(go_goroutines{job="{{.job}}"}) + - title: GC duration + gridPos: + w: 20 + singlestat: + unit: second + query: + datasourceID: prometheus + expr: max(go_gc_duration_seconds{job="{{.job}}"}) + - title: Stack + gridPos: + w: 20 + singlestat: + unit: bytes + thresholds: + - color: '#22F1F1' + query: + datasourceID: prometheus + expr: sum(go_memstats_stack_inuse_bytes{job="{{.job}}"}) + - title: Heap + gridPos: + w: 20 + singlestat: + unit: bytes + thresholds: + - color: '#22F1F1' + query: + datasourceID: prometheus + expr: sum(go_memstats_heap_inuse_bytes{job="{{.job}}"}) + - title: Alloc + gridPos: + w: 20 + singlestat: + unit: bytes + thresholds: + - color: '#22F1F1' + query: + datasourceID: prometheus + expr: sum(go_memstats_alloc_bytes{job="{{.job}}"}) + - title: Goroutines + gridPos: + w: 50 + graph: + visualization: + legend: + disable: true + yAxis: + unit: '' + decimals: 2 + queries: + - datasourceID: prometheus + expr: sum(go_goroutines{job="{{.job}}"}) + - title: GC duration + gridPos: + w: 50 + graph: + queries: + - datasourceID: prometheus + expr: max(go_gc_duration_seconds{job="{{.job}}"}) by (quantile) + legend: Q{{.quantile}} + visualization: + yAxis: + unit: second + seriesOverride: + - regex: ^Q0$ + color: '#F9E2D2' + - regex: ^Q0.25$ + color: '#F2C96D' + - regex: ^Q0.5(0)?$ + color: '#EAB839' + - regex: ^Q0.75$ + color: '#EF843C' + - regex: ^Q1(.0)?$ + color: '#E24D42' + - title: Memory + gridPos: + w: 50 + graph: + visualization: + yAxis: + unit: byte + decimals: 0 + queries: + - datasourceID: prometheus + expr: sum(go_memstats_stack_inuse_bytes{job="{{.job}}"}) + legend: stack inuse + - datasourceID: prometheus + expr: sum(go_memstats_heap_inuse_bytes{job="{{.job}}"}) + legend: heap inuse + - datasourceID: prometheus + expr: sum(go_memstats_alloc_bytes{job="{{.job}}"}) + legend: alloc + - title: Memory ops rate + gridPos: + w: 50 + graph: + queries: + - datasourceID: prometheus + expr: sum(rate(go_memstats_frees_total{job="{{.job}}"}[{{.interval}}])) + legend: frees/s + - datasourceID: prometheus + expr: sum(rate(go_memstats_mallocs_total{job="{{.job}}"}[{{.interval}}])) + legend: mallocs/s + - datasourceID: prometheus + expr: sum(rate(go_memstats_lookups_total{job="{{.job}}"}[{{.interval}}])) + legend: lookups/s diff --git a/dashboard-examples/yaml/kubernetes-cluster-status.yaml b/dashboard-examples/yaml/kubernetes-cluster-status.yaml new file mode 100644 index 0000000..0c9d777 --- /dev/null +++ b/dashboard-examples/yaml/kubernetes-cluster-status.yaml @@ -0,0 +1,163 @@ +version: v1 +datasources: + prometheus: + prometheus: + address: http://127.0.0.1:9090 +dashboard: + variables: + interval: + interval: + steps: 50 + widgets: + - title: Control plane UP + gridPos: + w: 50 + singlestat: + query: + datasourceID: prometheus + expr: sum(up{job=~"apiserver|kube-scheduler|kube-controller-manager"} == + 0) or vector(0) + valueText: '{{ if (gt .value 0.0) }}DOWN{{else}}UP{{end}}' + thresholds: + - color: '#299c46' + - color: '#d44a3a' + startValue: 1 + - title: Alerts firing + gridPos: + w: 50 + singlestat: + query: + datasourceID: prometheus + expr: sum(ALERTS{alertstate="firing",alertname!="DeadMansSwitch"}) + unit: none + thresholds: + - color: '#299c46' + - startValue: 3 + color: '#FF780A' + - startValue: 5 + color: '#d44a3a' + - title: APIservers UP + gridPos: + w: 25 + gauge: + percentValue: true + max: 100 + query: + datasourceID: prometheus + expr: (sum(up{job="apiserver"} == 1) / count(up{job="apiserver"})) * 100 + thresholds: + - color: '#d44a3a' + - startValue: 50 + color: '#FF780A' + - startValue: 80 + color: '#299c46' + - title: Kubelets UP + gridPos: + w: 25 + gauge: + percentValue: true + max: 100 + query: + datasourceID: prometheus + expr: (sum(up{job="kubelet"} == 1) / count(up{job="kubelet"})) * 100 + thresholds: + - color: '#d44a3a' + - startValue: 50 + color: '#FF780A' + - startValue: 80 + color: '#299c46' + - title: Schedulers UP + gridPos: + w: 25 + gauge: + percentValue: true + max: 100 + query: + datasourceID: prometheus + expr: (sum(up{job="kube-scheduler"} == 1) / count(up{job="kube-scheduler"})) + * 100 + thresholds: + - color: '#d44a3a' + - startValue: 50 + color: '#FF780A' + - startValue: 80 + color: '#299c46' + - title: Crashlooping control-plane pods + gridPos: + w: 25 + singlestat: + query: + datasourceID: prometheus + expr: count(increase(kube_pod_container_status_restarts{namespace=~"kube-system|tectonic-system"}[1h])) + or vector(0) + thresholds: + - color: '#299c46' + - startValue: 1 + color: '#FF780A' + - startValue: 3 + color: '#d44a3a' + - title: CPU utilization + gridPos: + w: 25 + gauge: + percentValue: true + max: 100 + query: + datasourceID: prometheus + expr: sum(100 - (avg by (instance) (rate(node_cpu_seconds_total{job="node-exporter",mode="idle"}[5m])) + * 100)) / count(node_cpu_seconds_total{job="node-exporter",mode="idle"}) + thresholds: + - color: '#299c46' + - startValue: 80 + color: '#FF780A' + - startValue: 90 + color: '#d44a3a' + - title: Memory utilization + gridPos: + w: 25 + gauge: + percentValue: true + max: 100 + query: + datasourceID: prometheus + expr: ((sum(node_memory_MemTotal_bytes) - sum(node_memory_MemFree_bytes) + - sum(node_memory_Buffers_bytes) - sum(node_memory_Cached_bytes)) / sum(node_memory_MemTotal_bytes)) + * 100 + thresholds: + - color: '#299c46' + - startValue: 80 + color: '#FF780A' + - startValue: 90 + color: '#d44a3a' + - title: Filesystem utilization + gridPos: + w: 25 + gauge: + percentValue: true + max: 100 + query: + datasourceID: prometheus + expr: (sum(node_filesystem_size_bytes{device!="rootfs"}) - sum(node_filesystem_free_bytes{device!="rootfs"})) + / sum(node_filesystem_size_bytes{device!="rootfs"}) + thresholds: + - color: '#299c46' + - startValue: 80 + color: '#FF780A' + - startValue: 90 + color: '#d44a3a' + - title: Pod utilization + gridPos: + w: 25 + gauge: + percentValue: true + max: 100 + query: + datasourceID: prometheus + expr: 100 - (sum(kube_node_status_capacity_pods) - sum(kube_pod_info)) / + sum(kube_node_status_capacity_pods) * 100 + thresholds: + - color: '#299c46' + - startValue: 80 + color: '#FF780A' + - startValue: 90 + color: '#d44a3a' diff --git a/dashboard-examples/yaml/red.yaml b/dashboard-examples/yaml/red.yaml new file mode 100644 index 0000000..bf2de13 --- /dev/null +++ b/dashboard-examples/yaml/red.yaml @@ -0,0 +1,101 @@ +version: v1 +datasources: + ds: + prometheus: + address: http://127.0.0.1:9090 +dashboard: + grid: + maxWidth: 100 + variables: + prefix: + constant: + value: ' ' + job: + constant: + value: .* + interval: + interval: + steps: 50 + widgets: + - title: RPS + gridPos: + w: 33 + singlestat: + query: + expr: sum(rate({{.prefix}}http_request_duration_seconds_count{job=~"{{.job}}"}[{{.interval}}])) + datasourceID: ds + decimals: 2 + thresholds: + - color: '#1f78c1' + - title: Errors(5xx) + gridPos: + w: 33 + singlestat: + query: + expr: (sum(rate({{.prefix}}http_request_duration_seconds_count{job=~"{{.job}}",code=~"5.."}[{{.interval}}])) + / sum(rate({{.prefix}}http_request_duration_seconds_count[{{.interval}}])) + ) * 100 OR vector(0) + datasourceID: ds + unit: percent + decimals: 2 + thresholds: + - color: '#299c46' + - color: '#FF780A' + startValue: 0.01 + - color: '#d44a3a' + startValue: 2 + - title: Latency + gridPos: + w: 34 + singlestat: + query: + expr: histogram_quantile(0.99, sum(rate({{.prefix}}http_request_duration_seconds_bucket{job=~"{{.job}}"}[{{.interval}}])) + by (le)) + datasourceID: ds + unit: seconds + thresholds: + - color: '#299c46' + - color: '#FF780A' + startValue: 0.35 + - color: '#d44a3a' + startValue: 0.6 + - title: RPS + gridPos: + w: 100 + graph: + visualization: + yAxis: + unit: reqps + decimals: 2 + queries: + - datasourceID: ds + expr: sum(rate({{.prefix}}http_request_duration_seconds_count{job=~"{{.job}}"}[{{.interval}}])) + by (code) + legend: '{{ .code }}' + - title: Latency + gridPos: + w: 100 + graph: + visualization: + seriesOverride: + - regex: ^p50$ + color: '#EAB839' + - regex: ^p95$ + color: '#EF843C' + - regex: ^p99$ + color: '#E24D42' + yAxis: + unit: seconds + queries: + - datasourceID: ds + expr: histogram_quantile(0.99, sum(rate({{.prefix}}http_request_duration_seconds_bucket{job=~"{{.job}}"}[{{.interval}}])) + by (le)) + legend: p99 + - datasourceID: ds + expr: 'histogram_quantile(0.95, sum(rate({{.prefix}}http_request_duration_seconds_bucket{job=~"{{.job}}"}[{{.interval}}])) + by (le)) ' + legend: p95 + - datasourceID: ds + expr: 'histogram_quantile(0.50, sum(rate({{.prefix}}http_request_duration_seconds_bucket{job=~"{{.job}}"}[{{.interval}}])) + by (le)) ' + legend: p50 diff --git a/dashboard-examples/yaml/test-grid-adaptive.yaml b/dashboard-examples/yaml/test-grid-adaptive.yaml new file mode 100644 index 0000000..28bf191 --- /dev/null +++ b/dashboard-examples/yaml/test-grid-adaptive.yaml @@ -0,0 +1,76 @@ +version: v1 +datasources: + ds: + fake: {} +dashboard: + widgets: + - title: '' + gridPos: + x: 0 + y: 0 + w: 100 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - title: '' + gridPos: + x: 0 + y: 0 + w: 50 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - title: '' + gridPos: + x: 0 + y: 0 + w: 75 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - title: '' + gridPos: + x: 0 + y: 0 + w: 10 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - title: '' + gridPos: + x: 0 + y: 0 + w: 10 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - title: '' + gridPos: + x: 0 + y: 0 + w: 50 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - title: '' + gridPos: + x: 0 + y: 0 + w: 100 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test diff --git a/dashboard-examples/yaml/test-grid-fixed.yaml b/dashboard-examples/yaml/test-grid-fixed.yaml new file mode 100644 index 0000000..7854f0e --- /dev/null +++ b/dashboard-examples/yaml/test-grid-fixed.yaml @@ -0,0 +1,197 @@ +version: v1 +datasources: + ds: + fake: {} +dashboard: + grid: + fixedWidgets: true + widgets: + - gridPos: + x: 0 + y: 0 + w: 50 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 50 + y: 0 + w: 50 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 0 + y: 1 + w: 15 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 25 + y: 1 + w: 15 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 50 + y: 1 + w: 15 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 75 + y: 1 + w: 15 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 15 + y: 2 + w: 10 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 40 + y: 2 + w: 10 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 65 + y: 2 + w: 10 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 90 + y: 2 + w: 10 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 0 + y: 3 + w: 100 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 0 + y: 4 + w: 5 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 10 + y: 4 + w: 5 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 20 + y: 4 + w: 5 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 30 + y: 4 + w: 5 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 40 + y: 4 + w: 5 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 50 + y: 4 + w: 5 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 60 + y: 4 + w: 5 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 70 + y: 4 + w: 5 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 80 + y: 4 + w: 5 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test + - gridPos: + x: 90 + y: 4 + w: 5 + h: 0 + graph: + queries: + - datasourceID: ds + expr: test diff --git a/dashboard-examples/yaml/wikimedia.yaml b/dashboard-examples/yaml/wikimedia.yaml new file mode 100644 index 0000000..dd839ce --- /dev/null +++ b/dashboard-examples/yaml/wikimedia.yaml @@ -0,0 +1,91 @@ +version: v1 +datasources: + graphite: + graphite: + address: https://graphite.wikimedia.org +dashboard: + widgets: + - title: API request rate + gridPos: + w: 50 + graph: + visualization: + yAxis: + unit: reqps + decimals: 1 + seriesOverride: + - regex: .* + nullPointMode: connected + queries: + - datasourceID: graphite + expr: alias(sumSeries(MediaWiki.api.*.executeTiming.sample_rate), 'rate') + - datasourceID: graphite + expr: alias(sumSeries(timeShift(MediaWiki.api.*.executeTiming.sample_rate,'2d')), 'last week') + - title: API request rate + gridPos: + w: 50 + graph: + visualization: + yAxis: + unit: reqps + decimals: 1 + seriesOverride: + - regex: .* + nullPointMode: connected + queries: + - datasourceID: graphite + expr: aliasByNode(highestAverage(MediaWiki.api.*.executeTiming.sample_rate, 10), 2) + - title: Mean latency + gridPos: + w: 70 + graph: + visualization: + yAxis: + unit: milliseconds + legend: + disable: true + seriesOverride: + - regex: .* + nullPointMode: connected + queries: + - datasourceID: graphite + expr: divideSeries(sumSeries(MediaWiki.api.*.executeTiming.sum),sumSeries(MediaWiki.api.*.executeTiming.count)) + - title: Mean latency now + gridPos: + w: 30 + singlestat: + unit: milliseconds + query: + datasourceID: graphite + expr: divideSeries(sumSeries(MediaWiki.api.*.executeTiming.sum),sumSeries(MediaWiki.api.*.executeTiming.count)) + thresholds: + - color: '#299c46' + - color: '#FF780A' + startValue: 350 + - color: '#d44a3a' + startValue: 600 + - title: Top 10 load breakdown + gridPos: + w: 50 + graph: + visualization: + seriesOverride: + - regex: .* + nullPointMode: connected + queries: + - datasourceID: graphite + expr: aliasByNode(highestAverage(scaleToSeconds(MediaWiki.api.*.executeTiming.sum,0.001), + 10), 2) + - title: Top 10 load (percentage) + gridPos: + w: 50 + graph: + visualization: + yAxis: + unit: percent + seriesOverride: + - regex: .* + nullPointMode: connected + queries: + - datasourceID: graphite + expr: aliasByNode(asPercent(highestAverage(MediaWiki.api.*.executeTiming.sum, 10), alias(sumSeries(MediaWiki.api.*.executeTiming.sum), 'Total')), 2) From e51f82046d9727a70df782c3a2eccc0b9d5e74e9 Mon Sep 17 00:00:00 2001 From: Curtis Mattoon Date: Thu, 9 Jun 2022 20:14:09 -0400 Subject: [PATCH 3/3] [ci skip] remove debug statements --- cmd/grafterm/main.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cmd/grafterm/main.go b/cmd/grafterm/main.go index 19b48a8..1a77338 100644 --- a/cmd/grafterm/main.go +++ b/cmd/grafterm/main.go @@ -56,7 +56,7 @@ func (m *Main) Run() error { if err != nil { return err } - m.logger.Infof("Configuration: %+v\n", cfg) + ddss, err := cfg.Datasources() if err != nil { return err @@ -171,7 +171,6 @@ func loadConfiguration(cfgPath string) (configuration.Configuration, error) { switch filepath.Ext(cfgPath) { case ".yaml", ".yml": - fmt.Fprintln(os.Stdout, "Using YAML Loader...") cfg, err := configuration.YAMLLoader{}.Load(f) if err != nil { return nil, err @@ -181,7 +180,6 @@ func loadConfiguration(cfgPath string) (configuration.Configuration, error) { case ".json": fallthrough default: - fmt.Fprintln(os.Stdout, "Using JSON Loader...") cfg, err := configuration.JSONLoader{}.Load(f) if err != nil { return nil, err