Skip to content

Commit

Permalink
Refactor to make exporter-based metrics and query-based metrics optio…
Browse files Browse the repository at this point in the history
…nal (#21)

Signed-off-by: Pradeep Chhetri <[email protected]>
  • Loading branch information
chhetripradeep authored Jan 15, 2023
1 parent cc141e4 commit 6f930ca
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 55 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ Usage:
chtop [flags]
Flags:
--config string config file (default: $HOME/.chtop.yaml)
-c, --config string path of the config file (default: $HOME/.chtop.yaml)
-h, --help help for chtop
--metrics-url string clickhouse url for pulling metrics in prometheus exposition format
--queries-database string clickhouse database for connecting clickhouse client (default "system")
--queries-password string clickhouse password for running clickhouse queries
--queries-url string clickhouse url for running clickhouse queries (native protocol port)
--queries-username string clickhouse username for running clickhouse queries (default "default")
-m, --metrics-url string clickhouse url for pulling metrics in prometheus exposition format
-d, --queries-database string clickhouse database for connecting from clickhouse client (default "system")
-p, --queries-password string clickhouse password of the provided clickhouse user for running clickhouse queries
-q, --queries-url string clickhouse endpoint for running clickhouse queries via native protocol
-u, --queries-username string clickhouse username for running clickhouse queries (default "default")
```

Run chtop pointing to prometheus stats endpoint & http endpoint of ClickHouse.
Expand Down
12 changes: 6 additions & 6 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ func Execute() {
func init() {
cobra.OnInitialize(initConfig)

rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default: $HOME/.chtop.yaml)")
rootCmd.PersistentFlags().StringVar(&clickhouseMetricsUrl, "metrics-url", "", "clickhouse url for pulling metrics in prometheus exposition format")
rootCmd.PersistentFlags().StringVar(&clickhouseQueriesUrl, "queries-url", "", "clickhouse url for running clickhouse queries (native protocol port)")
rootCmd.PersistentFlags().StringVar(&clickhouseDatabase, "queries-database", "system", "clickhouse database for connecting clickhouse client")
rootCmd.PersistentFlags().StringVar(&clickhouseUsername, "queries-username", "default", "clickhouse username for running clickhouse queries")
rootCmd.PersistentFlags().StringVar(&clickhousePassword, "queries-password", "", "clickhouse password for running clickhouse queries")
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "path of the config file (default: $HOME/.chtop.yaml)")
rootCmd.PersistentFlags().StringVarP(&clickhouseMetricsUrl, "metrics-url", "m", "", "clickhouse url for pulling metrics in prometheus exposition format")
rootCmd.PersistentFlags().StringVarP(&clickhouseQueriesUrl, "queries-url", "q", "", "clickhouse endpoint for running clickhouse queries via native protocol")
rootCmd.PersistentFlags().StringVarP(&clickhouseDatabase, "queries-database", "d", "system", "clickhouse database for connecting from clickhouse client")
rootCmd.PersistentFlags().StringVarP(&clickhouseUsername, "queries-username", "u", "default", "clickhouse username for running clickhouse queries")
rootCmd.PersistentFlags().StringVarP(&clickhousePassword, "queries-password", "p", "", "clickhouse password of the provided clickhouse user for running clickhouse queries")
}

func initConfig() {
Expand Down
5 changes: 3 additions & 2 deletions pkg/chtop/chtop.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ import (
"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/chhetripradeep/chtop/pkg/query"
"github.com/spf13/viper"

"github.com/chhetripradeep/chtop/pkg/metric"
"github.com/chhetripradeep/chtop/pkg/model"
"github.com/chhetripradeep/chtop/pkg/query"
"github.com/chhetripradeep/chtop/pkg/theme"
"github.com/chhetripradeep/chtop/pkg/utils"
)

func InitSpinner() spinner.Model {
spin := spinner.New()
spin.Spinner = spinner.Globe
spin.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))
spin.Style = lipgloss.NewStyle().Foreground(lipgloss.Color(utils.ColorNameToHex("magenta")))
return spin
}

Expand Down
129 changes: 88 additions & 41 deletions pkg/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io"
"net"
"net/http"
"os"
"strconv"
Expand Down Expand Up @@ -56,7 +57,8 @@ func (e errMsg) Error() string {
return e.err.Error()
}

func check(url string) tea.Cmd {
// checkUrl checks the http endpoint
func checkUrl(url string) tea.Cmd {
return func() tea.Msg {
resp, err := http.Get(url)
if err != nil {
Expand All @@ -67,9 +69,27 @@ func check(url string) tea.Cmd {
}
}

// checkEndpoint checks the tcp endpoint
func checkEndpoint(endpoint string) tea.Cmd {
return func() tea.Msg {
conn, err := net.Dial("tcp", endpoint)
if err != nil {
return errMsg{err}
}
defer conn.Close()
return statusMsg(200)
}
}

// Init inits the bubbletea model for use
func (m Model) Init() tea.Cmd {
return check(m.MetricsEndpoint)
if m.MetricsEndpoint != "" {
return checkUrl(m.MetricsEndpoint)
}
if m.QueriesEndpoint != "" {
return checkEndpoint(m.QueriesEndpoint)
}
return nil
}

// Query hits the prometheus exporter endpoint of clickhouse
Expand Down Expand Up @@ -180,51 +200,56 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, tea.Batch(cmds...)
}

func (m Model) UpdateData() tea.Cmd {
return tea.Tick(time.Minute/defaultFps, func(t time.Time) tea.Msg {
for i := range m.ClickHouseMetrics.Metrics {
value, err := m.Query(m.ClickHouseMetrics.Metrics[i].Name)
if err != nil {
fmt.Fprintln(os.Stderr, "unable to query endpoint:", m.MetricsEndpoint, "for metric:", m.ClickHouseMetrics.Metrics[i].Name)
continue
}

floatValue, err := strconv.ParseFloat(strings.TrimSpace(*value), 64)
if err != nil {
fmt.Fprintln(os.Stderr, "unable to parse value for metric:", m.ClickHouseMetrics.Metrics[i].Name)
continue
}

m.ClickHouseMetrics.Metrics[i].Update(floatValue)
// UpdateMetricsData updates the data from prometheus exporter's metrics endpoint
func (m Model) UpdateMetricsData() {
for i := range m.ClickHouseMetrics.Metrics {
value, err := m.Query(m.ClickHouseMetrics.Metrics[i].Name)
if err != nil {
fmt.Fprintln(os.Stderr, "unable to query endpoint:", m.MetricsEndpoint, "for metric:", m.ClickHouseMetrics.Metrics[i].Name)
continue
}

for i := range m.ClickHouseQueries.Queries {
value, err := m.Execute(m.ClickHouseQueries.Queries[i].Sql)
if err != nil {
fmt.Fprintln(os.Stderr, "unable to execute query:", m.ClickHouseQueries.Queries[i].Name, "at endpoint:", m.QueriesEndpoint)
}
floatValue, err := strconv.ParseFloat(strings.TrimSpace(*value), 64)
if err != nil {
fmt.Fprintln(os.Stderr, "unable to parse value for metric:", m.ClickHouseMetrics.Metrics[i].Name)
continue
}
m.ClickHouseMetrics.Metrics[i].Update(floatValue)
}
}

floatValue, err := strconv.ParseFloat(strings.TrimSpace(*value), 64)
if err != nil {
fmt.Fprintln(os.Stderr, "unable to parse value for metric:", m.ClickHouseQueries.Queries[i].Name)
continue
}
// UpdateQueriesData updates the data from clickhouse queries output
func (m Model) UpdateQueriesData() {
for i := range m.ClickHouseQueries.Queries {
value, err := m.Execute(m.ClickHouseQueries.Queries[i].Sql)
if err != nil {
fmt.Fprintln(os.Stderr, "unable to execute query:", m.ClickHouseQueries.Queries[i].Name, "at endpoint:", m.QueriesEndpoint)
}

m.ClickHouseQueries.Queries[i].Update(floatValue)
floatValue, err := strconv.ParseFloat(strings.TrimSpace(*value), 64)
if err != nil {
fmt.Fprintln(os.Stderr, "unable to parse value for metric:", m.ClickHouseQueries.Queries[i].Name)
continue
}
m.ClickHouseQueries.Queries[i].Update(floatValue)
}
}

// UpdateData updates the complete data
func (m Model) UpdateData() tea.Cmd {
return tea.Tick(time.Minute/defaultFps, func(t time.Time) tea.Msg {
if m.MetricsEndpoint != "" {
m.UpdateMetricsData()
}
if m.QueriesEndpoint != "" {
m.UpdateQueriesData()
}
return dataMsg(1)
})
}

// View shows the current state of the chtop
func (m Model) View() string {
var metricsPlot, queriesPlot, finalPlot string

if !m.Ready {
return m.Spinner.View() + " Initializing..."
}

func (m Model) ViewMetricsData() string {
var plot string
for i := range m.ClickHouseMetrics.Metrics {
caption := m.ClickHouseMetrics.Metrics[i].Alias + fmt.Sprintf(" (Current Value: %.2f)\n\n", m.ClickHouseMetrics.Metrics[i].Latest)

Expand All @@ -237,13 +262,17 @@ func (m Model) View() string {
asciigraph.Caption(caption),
)

metricsPlot += lipgloss.JoinVertical(
plot += lipgloss.JoinVertical(
lipgloss.Top,
graph,
)
metricsPlot += "\n\n"
plot += "\n\n"
}
return plot
}

func (m Model) ViewQueriesData() string {
var plot string
for i := range m.ClickHouseQueries.Queries {
caption := m.ClickHouseQueries.Queries[i].Name + fmt.Sprintf(" (Current Value: %.2f)\n\n", m.ClickHouseQueries.Queries[i].Latest)

Expand All @@ -256,11 +285,29 @@ func (m Model) View() string {
asciigraph.Caption(caption),
)

queriesPlot += lipgloss.JoinVertical(
plot += lipgloss.JoinVertical(
lipgloss.Top,
graph,
)
queriesPlot += "\n\n"
plot += "\n\n"
}
return plot
}

// View shows the current state of the complete data
func (m Model) View() string {
var metricsPlot, queriesPlot, finalPlot string

// If we don't have data to show in view
if !m.Ready {
return m.Spinner.View() + " Initializing..."
}

if m.MetricsEndpoint != "" {
metricsPlot = m.ViewMetricsData()
}
if m.QueriesEndpoint != "" {
queriesPlot = m.ViewQueriesData()
}

finalPlot = lipgloss.JoinHorizontal(
Expand Down

0 comments on commit 6f930ca

Please sign in to comment.