169 lines
5.4 KiB
Go
169 lines
5.4 KiB
Go
package mysql
|
|
|
|
import (
|
|
"database/sql"
|
|
"log"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"flashcat.cloud/categraf/inputs"
|
|
"flashcat.cloud/categraf/pkg/tagx"
|
|
"github.com/toolkits/pkg/container/list"
|
|
)
|
|
|
|
// Regexp to match various groups of status vars.
|
|
var globalStatusRE = regexp.MustCompile(`^(com|handler|connection_errors|innodb_buffer_pool_pages|innodb_rows|performance_schema)_(.*)$`)
|
|
|
|
func (m *MySQL) gatherGlobalStatus(slist *list.SafeList, ins *Instance, db *sql.DB, globalTags map[string]string, cache map[string]float64) {
|
|
rows, err := db.Query(SQL_GLOBAL_STATUS)
|
|
if err != nil {
|
|
log.Println("E! failed to query global status:", err)
|
|
return
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
var (
|
|
tags = tagx.Copy(globalTags)
|
|
textItems = map[string]string{
|
|
"wsrep_local_state_uuid": "",
|
|
"wsrep_cluster_state_uuid": "",
|
|
"wsrep_provider_version": "",
|
|
"wsrep_evs_repl_latency": "",
|
|
}
|
|
)
|
|
|
|
for rows.Next() {
|
|
var key string
|
|
var val sql.RawBytes
|
|
|
|
if err = rows.Scan(&key, &val); err != nil {
|
|
continue
|
|
}
|
|
|
|
// key to lower
|
|
key = strings.ToLower(key)
|
|
|
|
// collect some string fields
|
|
if _, has := textItems[key]; has {
|
|
textItems[key] = string(val)
|
|
continue
|
|
}
|
|
|
|
if floatVal, ok := parseStatus(val); ok {
|
|
cache[key] = floatVal
|
|
|
|
// collect float fields
|
|
if _, has := ins.validMetrics[key]; !has {
|
|
continue
|
|
}
|
|
|
|
match := globalStatusRE.FindStringSubmatch(key)
|
|
if match == nil {
|
|
slist.PushFront(inputs.NewSample("global_status_"+key, floatVal, tags))
|
|
continue
|
|
}
|
|
|
|
switch match[1] {
|
|
case "com":
|
|
// Total number of executed MySQL commands.
|
|
slist.PushFront(inputs.NewSample("global_status_commands_total", floatVal, tags, map[string]string{"command": match[2]}))
|
|
case "handler":
|
|
// Total number of executed MySQL handlers.
|
|
slist.PushFront(inputs.NewSample("global_status_handlers_total", floatVal, tags, map[string]string{"handler": match[2]}))
|
|
case "connection_errors":
|
|
// Total number of MySQL connection errors.
|
|
slist.PushFront(inputs.NewSample("global_status_connection_errors_total", floatVal, tags, map[string]string{"error": match[2]}))
|
|
case "innodb_buffer_pool_pages":
|
|
switch match[2] {
|
|
case "data", "free", "misc", "old", "total", "dirty":
|
|
// Innodb buffer pool pages by state.
|
|
slist.PushFront(inputs.NewSample("global_status_buffer_pool_pages", floatVal, tags, map[string]string{"state": match[2]}))
|
|
default:
|
|
// Innodb buffer pool page state changes.
|
|
slist.PushFront(inputs.NewSample("global_status_buffer_pool_page_changes_total", floatVal, tags, map[string]string{"operation": match[2]}))
|
|
}
|
|
case "innodb_rows":
|
|
// Total number of MySQL InnoDB row operations.
|
|
slist.PushFront(inputs.NewSample("global_status_innodb_row_ops_total", floatVal, tags, map[string]string{"operation": match[2]}))
|
|
case "performance_schema":
|
|
// Total number of MySQL instrumentations that could not be loaded or created due to memory constraints.
|
|
slist.PushFront(inputs.NewSample("global_status_performance_schema_lost_total", floatVal, tags, map[string]string{"instrumentation": match[2]}))
|
|
}
|
|
}
|
|
}
|
|
|
|
// mysql_galera_variables_info metric.
|
|
if textItems["wsrep_local_state_uuid"] != "" {
|
|
slist.PushFront(inputs.NewSample("galera_status_info", 1, tags, map[string]string{
|
|
"wsrep_local_state_uuid": textItems["wsrep_local_state_uuid"],
|
|
"wsrep_cluster_state_uuid": textItems["wsrep_cluster_state_uuid"],
|
|
"wsrep_provider_version": textItems["wsrep_provider_version"],
|
|
}))
|
|
}
|
|
|
|
// mysql_galera_evs_repl_latency
|
|
if textItems["wsrep_evs_repl_latency"] != "" {
|
|
type evsValue struct {
|
|
name string
|
|
value float64
|
|
index int
|
|
help string
|
|
}
|
|
|
|
evsMap := []evsValue{
|
|
{name: "min_seconds", value: 0, index: 0, help: "PXC/Galera group communication latency. Min value."},
|
|
{name: "avg_seconds", value: 0, index: 1, help: "PXC/Galera group communication latency. Avg value."},
|
|
{name: "max_seconds", value: 0, index: 2, help: "PXC/Galera group communication latency. Max value."},
|
|
{name: "stdev", value: 0, index: 3, help: "PXC/Galera group communication latency. Standard Deviation."},
|
|
{name: "sample_size", value: 0, index: 4, help: "PXC/Galera group communication latency. Sample Size."},
|
|
}
|
|
|
|
evsParsingSuccess := true
|
|
values := strings.Split(textItems["wsrep_evs_repl_latency"], "/")
|
|
|
|
if len(evsMap) == len(values) {
|
|
for i, v := range evsMap {
|
|
evsMap[i].value, err = strconv.ParseFloat(values[v.index], 64)
|
|
if err != nil {
|
|
evsParsingSuccess = false
|
|
}
|
|
}
|
|
|
|
if evsParsingSuccess {
|
|
for _, v := range evsMap {
|
|
slist.PushFront(inputs.NewSample("galera_evs_repl_latency_"+v.name, v.value, tags))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func parseStatus(data sql.RawBytes) (float64, bool) {
|
|
dataString := strings.ToLower(string(data))
|
|
switch dataString {
|
|
case "yes", "on":
|
|
return 1, true
|
|
case "no", "off", "disabled":
|
|
return 0, true
|
|
// SHOW SLAVE STATUS Slave_IO_Running can return "Connecting" which is a non-running state.
|
|
case "connecting":
|
|
return 0, true
|
|
// SHOW GLOBAL STATUS like 'wsrep_cluster_status' can return "Primary" or "non-Primary"/"Disconnected"
|
|
case "primary":
|
|
return 1, true
|
|
case "non-primary", "disconnected":
|
|
return 0, true
|
|
}
|
|
if ts, err := time.Parse("Jan 02 15:04:05 2006 MST", string(data)); err == nil {
|
|
return float64(ts.Unix()), true
|
|
}
|
|
if ts, err := time.Parse("2006-01-02 15:04:05", string(data)); err == nil {
|
|
return float64(ts.Unix()), true
|
|
}
|
|
value, err := strconv.ParseFloat(string(data), 64)
|
|
return value, err == nil
|
|
}
|