1741 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			1741 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			Go
		
	
	
	
| package mysql
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"database/sql"
 | |
| 	"fmt"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/influxdata/telegraf"
 | |
| 	"github.com/influxdata/telegraf/internal/tls"
 | |
| 	"github.com/influxdata/telegraf/plugins/inputs"
 | |
| 	"github.com/influxdata/telegraf/plugins/inputs/mysql/v1"
 | |
| 
 | |
| 	"github.com/go-sql-driver/mysql"
 | |
| )
 | |
| 
 | |
| type Mysql struct {
 | |
| 	Servers                             []string `toml:"servers"`
 | |
| 	PerfEventsStatementsDigestTextLimit int64    `toml:"perf_events_statements_digest_text_limit"`
 | |
| 	PerfEventsStatementsLimit           int64    `toml:"perf_events_statements_limit"`
 | |
| 	PerfEventsStatementsTimeLimit       int64    `toml:"perf_events_statements_time_limit"`
 | |
| 	TableSchemaDatabases                []string `toml:"table_schema_databases"`
 | |
| 	GatherProcessList                   bool     `toml:"gather_process_list"`
 | |
| 	GatherUserStatistics                bool     `toml:"gather_user_statistics"`
 | |
| 	GatherInfoSchemaAutoInc             bool     `toml:"gather_info_schema_auto_inc"`
 | |
| 	GatherInnoDBMetrics                 bool     `toml:"gather_innodb_metrics"`
 | |
| 	GatherSlaveStatus                   bool     `toml:"gather_slave_status"`
 | |
| 	GatherBinaryLogs                    bool     `toml:"gather_binary_logs"`
 | |
| 	GatherTableIOWaits                  bool     `toml:"gather_table_io_waits"`
 | |
| 	GatherTableLockWaits                bool     `toml:"gather_table_lock_waits"`
 | |
| 	GatherIndexIOWaits                  bool     `toml:"gather_index_io_waits"`
 | |
| 	GatherEventWaits                    bool     `toml:"gather_event_waits"`
 | |
| 	GatherTableSchema                   bool     `toml:"gather_table_schema"`
 | |
| 	GatherFileEventsStats               bool     `toml:"gather_file_events_stats"`
 | |
| 	GatherPerfEventsStatements          bool     `toml:"gather_perf_events_statements"`
 | |
| 	IntervalSlow                        string   `toml:"interval_slow"`
 | |
| 	MetricVersion                       int      `toml:"metric_version"`
 | |
| 	tls.ClientConfig
 | |
| }
 | |
| 
 | |
| var sampleConfig = `
 | |
|   ## specify servers via a url matching:
 | |
|   ##  [username[:password]@][protocol[(address)]]/[?tls=[true|false|skip-verify|custom]]
 | |
|   ##  see https://github.com/go-sql-driver/mysql#dsn-data-source-name
 | |
|   ##  e.g.
 | |
|   ##    servers = ["user:passwd@tcp(127.0.0.1:3306)/?tls=false"]
 | |
|   ##    servers = ["user@tcp(127.0.0.1:3306)/?tls=false"]
 | |
|   #
 | |
|   ## If no servers are specified, then localhost is used as the host.
 | |
|   servers = ["tcp(127.0.0.1:3306)/"]
 | |
| 
 | |
|   ## Selects the metric output format.
 | |
|   ##
 | |
|   ## This option exists to maintain backwards compatibility, if you have
 | |
|   ## existing metrics do not set or change this value until you are ready to
 | |
|   ## migrate to the new format.
 | |
|   ##
 | |
|   ## If you do not have existing metrics from this plugin set to the latest
 | |
|   ## version.
 | |
|   ##
 | |
|   ## Telegraf >=1.6: metric_version = 2
 | |
|   ##           <1.6: metric_version = 1 (or unset)
 | |
|   metric_version = 2
 | |
| 
 | |
|   ## the limits for metrics form perf_events_statements
 | |
|   perf_events_statements_digest_text_limit  = 120
 | |
|   perf_events_statements_limit              = 250
 | |
|   perf_events_statements_time_limit         = 86400
 | |
|   #
 | |
|   ## if the list is empty, then metrics are gathered from all databasee tables
 | |
|   table_schema_databases                    = []
 | |
|   #
 | |
|   ## gather metrics from INFORMATION_SCHEMA.TABLES for databases provided above list
 | |
|   gather_table_schema                       = false
 | |
|   #
 | |
|   ## gather thread state counts from INFORMATION_SCHEMA.PROCESSLIST
 | |
|   gather_process_list                       = true
 | |
|   #
 | |
|   ## gather user statistics from INFORMATION_SCHEMA.USER_STATISTICS
 | |
|   gather_user_statistics                    = true
 | |
|   #
 | |
|   ## gather auto_increment columns and max values from information schema
 | |
|   gather_info_schema_auto_inc               = true
 | |
|   #
 | |
|   ## gather metrics from INFORMATION_SCHEMA.INNODB_METRICS
 | |
|   gather_innodb_metrics                     = true
 | |
|   #
 | |
|   ## gather metrics from SHOW SLAVE STATUS command output
 | |
|   gather_slave_status                       = true
 | |
|   #
 | |
|   ## gather metrics from SHOW BINARY LOGS command output
 | |
|   gather_binary_logs                        = false
 | |
|   #
 | |
|   ## gather metrics from PERFORMANCE_SCHEMA.TABLE_IO_WAITS_SUMMARY_BY_TABLE
 | |
|   gather_table_io_waits                     = false
 | |
|   #
 | |
|   ## gather metrics from PERFORMANCE_SCHEMA.TABLE_LOCK_WAITS
 | |
|   gather_table_lock_waits                   = false
 | |
|   #
 | |
|   ## gather metrics from PERFORMANCE_SCHEMA.TABLE_IO_WAITS_SUMMARY_BY_INDEX_USAGE
 | |
|   gather_index_io_waits                     = false
 | |
|   #
 | |
|   ## gather metrics from PERFORMANCE_SCHEMA.EVENT_WAITS
 | |
|   gather_event_waits                        = false
 | |
|   #
 | |
|   ## gather metrics from PERFORMANCE_SCHEMA.FILE_SUMMARY_BY_EVENT_NAME
 | |
|   gather_file_events_stats                  = false
 | |
|   #
 | |
|   ## gather metrics from PERFORMANCE_SCHEMA.EVENTS_STATEMENTS_SUMMARY_BY_DIGEST
 | |
|   gather_perf_events_statements             = false
 | |
|   #
 | |
|   ## Some queries we may want to run less often (such as SHOW GLOBAL VARIABLES)
 | |
|   interval_slow                   = "30m"
 | |
| 
 | |
|   ## Optional TLS Config (will be used if tls=custom parameter specified in server uri)
 | |
|   # tls_ca = "/etc/telegraf/ca.pem"
 | |
|   # tls_cert = "/etc/telegraf/cert.pem"
 | |
|   # tls_key = "/etc/telegraf/key.pem"
 | |
|   ## Use TLS but skip chain & host verification
 | |
|   # insecure_skip_verify = false
 | |
| `
 | |
| 
 | |
| var defaultTimeout = time.Second * time.Duration(5)
 | |
| 
 | |
| func (m *Mysql) SampleConfig() string {
 | |
| 	return sampleConfig
 | |
| }
 | |
| 
 | |
| func (m *Mysql) Description() string {
 | |
| 	return "Read metrics from one or many mysql servers"
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	localhost        = ""
 | |
| 	lastT            time.Time
 | |
| 	initDone         = false
 | |
| 	scanIntervalSlow uint32
 | |
| )
 | |
| 
 | |
| func (m *Mysql) InitMysql() {
 | |
| 	if len(m.IntervalSlow) > 0 {
 | |
| 		interval, err := time.ParseDuration(m.IntervalSlow)
 | |
| 		if err == nil && interval.Seconds() >= 1.0 {
 | |
| 			scanIntervalSlow = uint32(interval.Seconds())
 | |
| 		}
 | |
| 	}
 | |
| 	initDone = true
 | |
| }
 | |
| 
 | |
| func (m *Mysql) Gather(acc telegraf.Accumulator) error {
 | |
| 	if len(m.Servers) == 0 {
 | |
| 		// default to localhost if nothing specified.
 | |
| 		return m.gatherServer(localhost, acc)
 | |
| 	}
 | |
| 	// Initialise additional query intervals
 | |
| 	if !initDone {
 | |
| 		m.InitMysql()
 | |
| 	}
 | |
| 
 | |
| 	tlsConfig, err := m.ClientConfig.TLSConfig()
 | |
| 	if err != nil {
 | |
| 		return fmt.Errorf("registering TLS config: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	if tlsConfig != nil {
 | |
| 		mysql.RegisterTLSConfig("custom", tlsConfig)
 | |
| 	}
 | |
| 
 | |
| 	var wg sync.WaitGroup
 | |
| 
 | |
| 	// Loop through each server and collect metrics
 | |
| 	for _, server := range m.Servers {
 | |
| 		wg.Add(1)
 | |
| 		go func(s string) {
 | |
| 			defer wg.Done()
 | |
| 			acc.AddError(m.gatherServer(s, acc))
 | |
| 		}(server)
 | |
| 	}
 | |
| 
 | |
| 	wg.Wait()
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	// status counter
 | |
| 	generalThreadStates = map[string]uint32{
 | |
| 		"after create":              uint32(0),
 | |
| 		"altering table":            uint32(0),
 | |
| 		"analyzing":                 uint32(0),
 | |
| 		"checking permissions":      uint32(0),
 | |
| 		"checking table":            uint32(0),
 | |
| 		"cleaning up":               uint32(0),
 | |
| 		"closing tables":            uint32(0),
 | |
| 		"converting heap to myisam": uint32(0),
 | |
| 		"copying to tmp table":      uint32(0),
 | |
| 		"creating sort index":       uint32(0),
 | |
| 		"creating table":            uint32(0),
 | |
| 		"creating tmp table":        uint32(0),
 | |
| 		"deleting":                  uint32(0),
 | |
| 		"executing":                 uint32(0),
 | |
| 		"execution of init_command": uint32(0),
 | |
| 		"end":                       uint32(0),
 | |
| 		"freeing items":             uint32(0),
 | |
| 		"flushing tables":           uint32(0),
 | |
| 		"fulltext initialization":   uint32(0),
 | |
| 		"idle":                      uint32(0),
 | |
| 		"init":                      uint32(0),
 | |
| 		"killed":                    uint32(0),
 | |
| 		"waiting for lock":          uint32(0),
 | |
| 		"logging slow query":        uint32(0),
 | |
| 		"login":                     uint32(0),
 | |
| 		"manage keys":               uint32(0),
 | |
| 		"opening tables":            uint32(0),
 | |
| 		"optimizing":                uint32(0),
 | |
| 		"preparing":                 uint32(0),
 | |
| 		"reading from net":          uint32(0),
 | |
| 		"removing duplicates":       uint32(0),
 | |
| 		"removing tmp table":        uint32(0),
 | |
| 		"reopen tables":             uint32(0),
 | |
| 		"repair by sorting":         uint32(0),
 | |
| 		"repair done":               uint32(0),
 | |
| 		"repair with keycache":      uint32(0),
 | |
| 		"replication master":        uint32(0),
 | |
| 		"rolling back":              uint32(0),
 | |
| 		"searching rows for update": uint32(0),
 | |
| 		"sending data":              uint32(0),
 | |
| 		"sorting for group":         uint32(0),
 | |
| 		"sorting for order":         uint32(0),
 | |
| 		"sorting index":             uint32(0),
 | |
| 		"sorting result":            uint32(0),
 | |
| 		"statistics":                uint32(0),
 | |
| 		"updating":                  uint32(0),
 | |
| 		"waiting for tables":        uint32(0),
 | |
| 		"waiting for table flush":   uint32(0),
 | |
| 		"waiting on cond":           uint32(0),
 | |
| 		"writing to net":            uint32(0),
 | |
| 		"other":                     uint32(0),
 | |
| 	}
 | |
| 	// plaintext statuses
 | |
| 	stateStatusMappings = map[string]string{
 | |
| 		"user sleep":     "idle",
 | |
| 		"creating index": "altering table",
 | |
| 		"committing alter table to storage engine": "altering table",
 | |
| 		"discard or import tablespace":             "altering table",
 | |
| 		"rename":                                   "altering table",
 | |
| 		"setup":                                    "altering table",
 | |
| 		"renaming result table":                    "altering table",
 | |
| 		"preparing for alter table":                "altering table",
 | |
| 		"copying to group table":                   "copying to tmp table",
 | |
| 		"copy to tmp table":                        "copying to tmp table",
 | |
| 		"query end":                                "end",
 | |
| 		"update":                                   "updating",
 | |
| 		"updating main table":                      "updating",
 | |
| 		"updating reference tables":                "updating",
 | |
| 		"system lock":                              "waiting for lock",
 | |
| 		"user lock":                                "waiting for lock",
 | |
| 		"table lock":                               "waiting for lock",
 | |
| 		"deleting from main table":                 "deleting",
 | |
| 		"deleting from reference tables":           "deleting",
 | |
| 	}
 | |
| )
 | |
| 
 | |
| // Math constants
 | |
| const (
 | |
| 	picoSeconds = 1e12
 | |
| )
 | |
| 
 | |
| // metric queries
 | |
| const (
 | |
| 	globalStatusQuery          = `SHOW GLOBAL STATUS`
 | |
| 	globalVariablesQuery       = `SHOW GLOBAL VARIABLES`
 | |
| 	slaveStatusQuery           = `SHOW SLAVE STATUS`
 | |
| 	binaryLogsQuery            = `SHOW BINARY LOGS`
 | |
| 	infoSchemaProcessListQuery = `
 | |
|         SELECT COALESCE(command,''),COALESCE(state,''),count(*)
 | |
|         FROM information_schema.processlist
 | |
|         WHERE ID != connection_id()
 | |
|         GROUP BY command,state
 | |
|         ORDER BY null`
 | |
| 	infoSchemaUserStatisticsQuery = `
 | |
|         SELECT *
 | |
|         FROM information_schema.user_statistics`
 | |
| 	infoSchemaAutoIncQuery = `
 | |
|         SELECT table_schema, table_name, column_name, auto_increment,
 | |
|           CAST(pow(2, case data_type
 | |
|             when 'tinyint'   then 7
 | |
|             when 'smallint'  then 15
 | |
|             when 'mediumint' then 23
 | |
|             when 'int'       then 31
 | |
|             when 'bigint'    then 63
 | |
|             end+(column_type like '% unsigned'))-1 as decimal(19)) as max_int
 | |
|           FROM information_schema.tables t
 | |
|           JOIN information_schema.columns c USING (table_schema,table_name)
 | |
|           WHERE c.extra = 'auto_increment' AND t.auto_increment IS NOT NULL
 | |
|     `
 | |
| 	innoDBMetricsQuery = `
 | |
|         SELECT NAME, COUNT
 | |
|         FROM information_schema.INNODB_METRICS
 | |
|         WHERE status='enabled'
 | |
|     `
 | |
| 	perfTableIOWaitsQuery = `
 | |
|         SELECT OBJECT_SCHEMA, OBJECT_NAME, COUNT_FETCH, COUNT_INSERT, COUNT_UPDATE, COUNT_DELETE,
 | |
|         SUM_TIMER_FETCH, SUM_TIMER_INSERT, SUM_TIMER_UPDATE, SUM_TIMER_DELETE
 | |
|         FROM performance_schema.table_io_waits_summary_by_table
 | |
|         WHERE OBJECT_SCHEMA NOT IN ('mysql', 'performance_schema')
 | |
|     `
 | |
| 	perfIndexIOWaitsQuery = `
 | |
|         SELECT OBJECT_SCHEMA, OBJECT_NAME, ifnull(INDEX_NAME, 'NONE') as INDEX_NAME,
 | |
|         COUNT_FETCH, COUNT_INSERT, COUNT_UPDATE, COUNT_DELETE,
 | |
|         SUM_TIMER_FETCH, SUM_TIMER_INSERT, SUM_TIMER_UPDATE, SUM_TIMER_DELETE
 | |
|         FROM performance_schema.table_io_waits_summary_by_index_usage
 | |
|         WHERE OBJECT_SCHEMA NOT IN ('mysql', 'performance_schema')
 | |
|     `
 | |
| 	perfTableLockWaitsQuery = `
 | |
|         SELECT
 | |
|             OBJECT_SCHEMA,
 | |
|             OBJECT_NAME,
 | |
|             COUNT_READ_NORMAL,
 | |
|             COUNT_READ_WITH_SHARED_LOCKS,
 | |
|             COUNT_READ_HIGH_PRIORITY,
 | |
|             COUNT_READ_NO_INSERT,
 | |
|             COUNT_READ_EXTERNAL,
 | |
|             COUNT_WRITE_ALLOW_WRITE,
 | |
|             COUNT_WRITE_CONCURRENT_INSERT,
 | |
|             COUNT_WRITE_LOW_PRIORITY,
 | |
|             COUNT_WRITE_NORMAL,
 | |
|             COUNT_WRITE_EXTERNAL,
 | |
|             SUM_TIMER_READ_NORMAL,
 | |
|             SUM_TIMER_READ_WITH_SHARED_LOCKS,
 | |
|             SUM_TIMER_READ_HIGH_PRIORITY,
 | |
|             SUM_TIMER_READ_NO_INSERT,
 | |
|             SUM_TIMER_READ_EXTERNAL,
 | |
|             SUM_TIMER_WRITE_ALLOW_WRITE,
 | |
|             SUM_TIMER_WRITE_CONCURRENT_INSERT,
 | |
|             SUM_TIMER_WRITE_LOW_PRIORITY,
 | |
|             SUM_TIMER_WRITE_NORMAL,
 | |
|             SUM_TIMER_WRITE_EXTERNAL
 | |
|         FROM performance_schema.table_lock_waits_summary_by_table
 | |
|         WHERE OBJECT_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema')
 | |
|     `
 | |
| 	perfEventsStatementsQuery = `
 | |
|         SELECT
 | |
|             ifnull(SCHEMA_NAME, 'NONE') as SCHEMA_NAME,
 | |
|             DIGEST,
 | |
|             LEFT(DIGEST_TEXT, %d) as DIGEST_TEXT,
 | |
|             COUNT_STAR,
 | |
|             SUM_TIMER_WAIT,
 | |
|             SUM_ERRORS,
 | |
|             SUM_WARNINGS,
 | |
|             SUM_ROWS_AFFECTED,
 | |
|             SUM_ROWS_SENT,
 | |
|             SUM_ROWS_EXAMINED,
 | |
|             SUM_CREATED_TMP_DISK_TABLES,
 | |
|             SUM_CREATED_TMP_TABLES,
 | |
|             SUM_SORT_MERGE_PASSES,
 | |
|             SUM_SORT_ROWS,
 | |
|             SUM_NO_INDEX_USED
 | |
|         FROM performance_schema.events_statements_summary_by_digest
 | |
|         WHERE SCHEMA_NAME NOT IN ('mysql', 'performance_schema', 'information_schema')
 | |
|             AND last_seen > DATE_SUB(NOW(), INTERVAL %d SECOND)
 | |
|         ORDER BY SUM_TIMER_WAIT DESC
 | |
|         LIMIT %d
 | |
|     `
 | |
| 	perfEventWaitsQuery = `
 | |
|         SELECT EVENT_NAME, COUNT_STAR, SUM_TIMER_WAIT
 | |
|         FROM performance_schema.events_waits_summary_global_by_event_name
 | |
|     `
 | |
| 	perfFileEventsQuery = `
 | |
|         SELECT
 | |
|             EVENT_NAME,
 | |
|             COUNT_READ, SUM_TIMER_READ, SUM_NUMBER_OF_BYTES_READ,
 | |
|             COUNT_WRITE, SUM_TIMER_WRITE, SUM_NUMBER_OF_BYTES_WRITE,
 | |
|             COUNT_MISC, SUM_TIMER_MISC
 | |
|         FROM performance_schema.file_summary_by_event_name
 | |
|     `
 | |
| 	tableSchemaQuery = `
 | |
|         SELECT
 | |
|             TABLE_SCHEMA,
 | |
|             TABLE_NAME,
 | |
|             TABLE_TYPE,
 | |
|             ifnull(ENGINE, 'NONE') as ENGINE,
 | |
|             ifnull(VERSION, '0') as VERSION,
 | |
|             ifnull(ROW_FORMAT, 'NONE') as ROW_FORMAT,
 | |
|             ifnull(TABLE_ROWS, '0') as TABLE_ROWS,
 | |
|             ifnull(DATA_LENGTH, '0') as DATA_LENGTH,
 | |
|             ifnull(INDEX_LENGTH, '0') as INDEX_LENGTH,
 | |
|             ifnull(DATA_FREE, '0') as DATA_FREE,
 | |
|             ifnull(CREATE_OPTIONS, 'NONE') as CREATE_OPTIONS
 | |
|         FROM information_schema.tables
 | |
|         WHERE TABLE_SCHEMA = '%s'
 | |
|     `
 | |
| 	dbListQuery = `
 | |
|         SELECT
 | |
|             SCHEMA_NAME
 | |
|             FROM information_schema.schemata
 | |
|         WHERE SCHEMA_NAME NOT IN ('mysql', 'performance_schema', 'information_schema')
 | |
|     `
 | |
| 	perfSchemaTablesQuery = `
 | |
| 		SELECT
 | |
| 			table_name
 | |
| 			FROM information_schema.tables
 | |
| 		WHERE table_schema = 'performance_schema' AND table_name = ?
 | |
| 	`
 | |
| )
 | |
| 
 | |
| func (m *Mysql) gatherServer(serv string, acc telegraf.Accumulator) error {
 | |
| 	serv, err := dsnAddTimeout(serv)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	db, err := sql.Open("mysql", serv)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	defer db.Close()
 | |
| 
 | |
| 	err = m.gatherGlobalStatuses(db, serv, acc)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	// Global Variables may be gathered less often
 | |
| 	if len(m.IntervalSlow) > 0 {
 | |
| 		if uint32(time.Since(lastT).Seconds()) >= scanIntervalSlow {
 | |
| 			err = m.gatherGlobalVariables(db, serv, acc)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			lastT = time.Now()
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if m.GatherBinaryLogs {
 | |
| 		err = m.gatherBinaryLogs(db, serv, acc)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if m.GatherProcessList {
 | |
| 		err = m.GatherProcessListStatuses(db, serv, acc)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if m.GatherUserStatistics {
 | |
| 		err = m.GatherUserStatisticsStatuses(db, serv, acc)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if m.GatherSlaveStatus {
 | |
| 		err = m.gatherSlaveStatuses(db, serv, acc)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if m.GatherInfoSchemaAutoInc {
 | |
| 		err = m.gatherInfoSchemaAutoIncStatuses(db, serv, acc)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if m.GatherInnoDBMetrics {
 | |
| 		err = m.gatherInnoDBMetrics(db, serv, acc)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if m.GatherTableIOWaits {
 | |
| 		err = m.gatherPerfTableIOWaits(db, serv, acc)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if m.GatherIndexIOWaits {
 | |
| 		err = m.gatherPerfIndexIOWaits(db, serv, acc)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if m.GatherTableLockWaits {
 | |
| 		err = m.gatherPerfTableLockWaits(db, serv, acc)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if m.GatherEventWaits {
 | |
| 		err = m.gatherPerfEventWaits(db, serv, acc)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if m.GatherFileEventsStats {
 | |
| 		err = m.gatherPerfFileEventsStatuses(db, serv, acc)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if m.GatherPerfEventsStatements {
 | |
| 		err = m.gatherPerfEventsStatements(db, serv, acc)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if m.GatherTableSchema {
 | |
| 		err = m.gatherTableSchema(db, serv, acc)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // gatherGlobalVariables can be used to fetch all global variables from
 | |
| // MySQL environment.
 | |
| func (m *Mysql) gatherGlobalVariables(db *sql.DB, serv string, acc telegraf.Accumulator) error {
 | |
| 	// run query
 | |
| 	rows, err := db.Query(globalVariablesQuery)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer rows.Close()
 | |
| 
 | |
| 	var key string
 | |
| 	var val sql.RawBytes
 | |
| 
 | |
| 	// parse DSN and save server tag
 | |
| 	servtag := getDSNTag(serv)
 | |
| 	tags := map[string]string{"server": servtag}
 | |
| 	fields := make(map[string]interface{})
 | |
| 	for rows.Next() {
 | |
| 		if err := rows.Scan(&key, &val); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		key = strings.ToLower(key)
 | |
| 		// parse mysql version and put into field and tag
 | |
| 		if strings.Contains(key, "version") {
 | |
| 			fields[key] = string(val)
 | |
| 			tags[key] = string(val)
 | |
| 		}
 | |
| 		if value, ok := m.parseValue(val); ok {
 | |
| 			fields[key] = value
 | |
| 		}
 | |
| 		// Send 20 fields at a time
 | |
| 		if len(fields) >= 20 {
 | |
| 			acc.AddFields("mysql_variables", fields, tags)
 | |
| 			fields = make(map[string]interface{})
 | |
| 		}
 | |
| 	}
 | |
| 	// Send any remaining fields
 | |
| 	if len(fields) > 0 {
 | |
| 		acc.AddFields("mysql_variables", fields, tags)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // gatherSlaveStatuses can be used to get replication analytics
 | |
| // When the server is slave, then it returns only one row.
 | |
| // If the multi-source replication is set, then everything works differently
 | |
| // This code does not work with multi-source replication.
 | |
| func (m *Mysql) gatherSlaveStatuses(db *sql.DB, serv string, acc telegraf.Accumulator) error {
 | |
| 	// run query
 | |
| 	rows, err := db.Query(slaveStatusQuery)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer rows.Close()
 | |
| 
 | |
| 	servtag := getDSNTag(serv)
 | |
| 
 | |
| 	tags := map[string]string{"server": servtag}
 | |
| 	fields := make(map[string]interface{})
 | |
| 
 | |
| 	// to save the column names as a field key
 | |
| 	// scanning keys and values separately
 | |
| 	if rows.Next() {
 | |
| 		// get columns names, and create an array with its length
 | |
| 		cols, err := rows.Columns()
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		vals := make([]interface{}, len(cols))
 | |
| 		// fill the array with sql.Rawbytes
 | |
| 		for i := range vals {
 | |
| 			vals[i] = &sql.RawBytes{}
 | |
| 		}
 | |
| 		if err = rows.Scan(vals...); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		// range over columns, and try to parse values
 | |
| 		for i, col := range cols {
 | |
| 			if m.MetricVersion >= 2 {
 | |
| 				col = strings.ToLower(col)
 | |
| 			}
 | |
| 			if value, ok := m.parseValue(*vals[i].(*sql.RawBytes)); ok {
 | |
| 				fields["slave_"+col] = value
 | |
| 			}
 | |
| 		}
 | |
| 		acc.AddFields("mysql", fields, tags)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // gatherBinaryLogs can be used to collect size and count of all binary files
 | |
| // binlogs metric requires the MySQL server to turn it on in configuration
 | |
| func (m *Mysql) gatherBinaryLogs(db *sql.DB, serv string, acc telegraf.Accumulator) error {
 | |
| 	// run query
 | |
| 	rows, err := db.Query(binaryLogsQuery)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer rows.Close()
 | |
| 
 | |
| 	// parse DSN and save host as a tag
 | |
| 	servtag := getDSNTag(serv)
 | |
| 	tags := map[string]string{"server": servtag}
 | |
| 	var (
 | |
| 		size     uint64 = 0
 | |
| 		count    uint64 = 0
 | |
| 		fileSize uint64
 | |
| 		fileName string
 | |
| 	)
 | |
| 
 | |
| 	// iterate over rows and count the size and count of files
 | |
| 	for rows.Next() {
 | |
| 		if err := rows.Scan(&fileName, &fileSize); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		size += fileSize
 | |
| 		count++
 | |
| 	}
 | |
| 	fields := map[string]interface{}{
 | |
| 		"binary_size_bytes":  size,
 | |
| 		"binary_files_count": count,
 | |
| 	}
 | |
| 	acc.AddFields("mysql", fields, tags)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // gatherGlobalStatuses can be used to get MySQL status metrics
 | |
| // the mappings of actual names and names of each status to be exported
 | |
| // to output is provided on mappings variable
 | |
| func (m *Mysql) gatherGlobalStatuses(db *sql.DB, serv string, acc telegraf.Accumulator) error {
 | |
| 	// run query
 | |
| 	rows, err := db.Query(globalStatusQuery)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer rows.Close()
 | |
| 
 | |
| 	// parse the DSN and save host name as a tag
 | |
| 	servtag := getDSNTag(serv)
 | |
| 	tags := map[string]string{"server": servtag}
 | |
| 	fields := make(map[string]interface{})
 | |
| 	for rows.Next() {
 | |
| 		var key string
 | |
| 		var val sql.RawBytes
 | |
| 
 | |
| 		if err = rows.Scan(&key, &val); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		if m.MetricVersion < 2 {
 | |
| 			var found bool
 | |
| 			for _, mapped := range v1.Mappings {
 | |
| 				if strings.HasPrefix(key, mapped.OnServer) {
 | |
| 					// convert numeric values to integer
 | |
| 					i, _ := strconv.Atoi(string(val))
 | |
| 					fields[mapped.InExport+key[len(mapped.OnServer):]] = i
 | |
| 					found = true
 | |
| 				}
 | |
| 			}
 | |
| 			// Send 20 fields at a time
 | |
| 			if len(fields) >= 20 {
 | |
| 				acc.AddFields("mysql", fields, tags)
 | |
| 				fields = make(map[string]interface{})
 | |
| 			}
 | |
| 			if found {
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			// search for specific values
 | |
| 			switch key {
 | |
| 			case "Queries":
 | |
| 				i, err := strconv.ParseInt(string(val), 10, 64)
 | |
| 				if err != nil {
 | |
| 					acc.AddError(fmt.Errorf("E! Error mysql: parsing %s int value (%s)", key, err))
 | |
| 				} else {
 | |
| 					fields["queries"] = i
 | |
| 				}
 | |
| 			case "Questions":
 | |
| 				i, err := strconv.ParseInt(string(val), 10, 64)
 | |
| 				if err != nil {
 | |
| 					acc.AddError(fmt.Errorf("E! Error mysql: parsing %s int value (%s)", key, err))
 | |
| 				} else {
 | |
| 					fields["questions"] = i
 | |
| 				}
 | |
| 			case "Slow_queries":
 | |
| 				i, err := strconv.ParseInt(string(val), 10, 64)
 | |
| 				if err != nil {
 | |
| 					acc.AddError(fmt.Errorf("E! Error mysql: parsing %s int value (%s)", key, err))
 | |
| 				} else {
 | |
| 					fields["slow_queries"] = i
 | |
| 				}
 | |
| 			case "Connections":
 | |
| 				i, err := strconv.ParseInt(string(val), 10, 64)
 | |
| 				if err != nil {
 | |
| 					acc.AddError(fmt.Errorf("E! Error mysql: parsing %s int value (%s)", key, err))
 | |
| 				} else {
 | |
| 					fields["connections"] = i
 | |
| 				}
 | |
| 			case "Syncs":
 | |
| 				i, err := strconv.ParseInt(string(val), 10, 64)
 | |
| 				if err != nil {
 | |
| 					acc.AddError(fmt.Errorf("E! Error mysql: parsing %s int value (%s)", key, err))
 | |
| 				} else {
 | |
| 					fields["syncs"] = i
 | |
| 				}
 | |
| 			case "Uptime":
 | |
| 				i, err := strconv.ParseInt(string(val), 10, 64)
 | |
| 				if err != nil {
 | |
| 					acc.AddError(fmt.Errorf("E! Error mysql: parsing %s int value (%s)", key, err))
 | |
| 				} else {
 | |
| 					fields["uptime"] = i
 | |
| 				}
 | |
| 			}
 | |
| 		} else {
 | |
| 			key = strings.ToLower(key)
 | |
| 			if value, ok := m.parseValue(val); ok {
 | |
| 				fields[key] = value
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// Send 20 fields at a time
 | |
| 		if len(fields) >= 20 {
 | |
| 			acc.AddFields("mysql", fields, tags)
 | |
| 			fields = make(map[string]interface{})
 | |
| 		}
 | |
| 	}
 | |
| 	// Send any remaining fields
 | |
| 	if len(fields) > 0 {
 | |
| 		acc.AddFields("mysql", fields, tags)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // GatherProcessList can be used to collect metrics on each running command
 | |
| // and its state with its running count
 | |
| func (m *Mysql) GatherProcessListStatuses(db *sql.DB, serv string, acc telegraf.Accumulator) error {
 | |
| 	// run query
 | |
| 	rows, err := db.Query(infoSchemaProcessListQuery)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer rows.Close()
 | |
| 	var (
 | |
| 		command string
 | |
| 		state   string
 | |
| 		count   uint32
 | |
| 	)
 | |
| 
 | |
| 	var servtag string
 | |
| 	fields := make(map[string]interface{})
 | |
| 	servtag = getDSNTag(serv)
 | |
| 
 | |
| 	// mapping of state with its counts
 | |
| 	stateCounts := make(map[string]uint32, len(generalThreadStates))
 | |
| 	// set map with keys and default values
 | |
| 	for k, v := range generalThreadStates {
 | |
| 		stateCounts[k] = v
 | |
| 	}
 | |
| 
 | |
| 	for rows.Next() {
 | |
| 		err = rows.Scan(&command, &state, &count)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		// each state has its mapping
 | |
| 		foundState := findThreadState(command, state)
 | |
| 		// count each state
 | |
| 		stateCounts[foundState] += count
 | |
| 	}
 | |
| 
 | |
| 	tags := map[string]string{"server": servtag}
 | |
| 	for s, c := range stateCounts {
 | |
| 		fields[newNamespace("threads", s)] = c
 | |
| 	}
 | |
| 	if m.MetricVersion < 2 {
 | |
| 		acc.AddFields("mysql_info_schema", fields, tags)
 | |
| 	} else {
 | |
| 		acc.AddFields("mysql_process_list", fields, tags)
 | |
| 	}
 | |
| 
 | |
| 	// get count of connections from each user
 | |
| 	conn_rows, err := db.Query("SELECT user, sum(1) AS connections FROM INFORMATION_SCHEMA.PROCESSLIST GROUP BY user")
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	for conn_rows.Next() {
 | |
| 		var user string
 | |
| 		var connections int64
 | |
| 
 | |
| 		err = conn_rows.Scan(&user, &connections)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		tags := map[string]string{"server": servtag, "user": user}
 | |
| 		fields := make(map[string]interface{})
 | |
| 
 | |
| 		fields["connections"] = connections
 | |
| 		acc.AddFields("mysql_users", fields, tags)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // GatherUserStatistics can be used to collect metrics on each running command
 | |
| // and its state with its running count
 | |
| func (m *Mysql) GatherUserStatisticsStatuses(db *sql.DB, serv string, acc telegraf.Accumulator) error {
 | |
| 	// run query
 | |
| 	rows, err := db.Query(infoSchemaUserStatisticsQuery)
 | |
| 	if err != nil {
 | |
| 		// disable collecting if table is not found (mysql specific error)
 | |
| 		// (suppresses repeat errors)
 | |
| 		if strings.Contains(err.Error(), "nknown table 'user_statistics'") {
 | |
| 			m.GatherUserStatistics = false
 | |
| 		}
 | |
| 		return err
 | |
| 	}
 | |
| 	defer rows.Close()
 | |
| 
 | |
| 	cols, err := columnsToLower(rows.Columns())
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	read, err := getColSlice(len(cols))
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	servtag := getDSNTag(serv)
 | |
| 	for rows.Next() {
 | |
| 		err = rows.Scan(read...)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		tags := map[string]string{"server": servtag, "user": *read[0].(*string)}
 | |
| 		fields := map[string]interface{}{}
 | |
| 
 | |
| 		for i := range cols {
 | |
| 			if i == 0 {
 | |
| 				continue // skip "user"
 | |
| 			}
 | |
| 			switch v := read[i].(type) {
 | |
| 			case *int64:
 | |
| 				fields[cols[i]] = *v
 | |
| 			case *float64:
 | |
| 				fields[cols[i]] = *v
 | |
| 			case *string:
 | |
| 				fields[cols[i]] = *v
 | |
| 			default:
 | |
| 				return fmt.Errorf("Unknown column type - %T", v)
 | |
| 			}
 | |
| 		}
 | |
| 		acc.AddFields("mysql_user_stats", fields, tags)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // columnsToLower converts selected column names to lowercase.
 | |
| func columnsToLower(s []string, e error) ([]string, error) {
 | |
| 	if e != nil {
 | |
| 		return nil, e
 | |
| 	}
 | |
| 	d := make([]string, len(s))
 | |
| 
 | |
| 	for i := range s {
 | |
| 		d[i] = strings.ToLower(s[i])
 | |
| 	}
 | |
| 	return d, nil
 | |
| }
 | |
| 
 | |
| // getColSlice returns an in interface slice that can be used in the row.Scan().
 | |
| func getColSlice(l int) ([]interface{}, error) {
 | |
| 	// list of all possible column names
 | |
| 	var (
 | |
| 		user                        string
 | |
| 		total_connections           int64
 | |
| 		concurrent_connections      int64
 | |
| 		connected_time              int64
 | |
| 		busy_time                   int64
 | |
| 		cpu_time                    int64
 | |
| 		bytes_received              int64
 | |
| 		bytes_sent                  int64
 | |
| 		binlog_bytes_written        int64
 | |
| 		rows_read                   int64
 | |
| 		rows_sent                   int64
 | |
| 		rows_deleted                int64
 | |
| 		rows_inserted               int64
 | |
| 		rows_updated                int64
 | |
| 		select_commands             int64
 | |
| 		update_commands             int64
 | |
| 		other_commands              int64
 | |
| 		commit_transactions         int64
 | |
| 		rollback_transactions       int64
 | |
| 		denied_connections          int64
 | |
| 		lost_connections            int64
 | |
| 		access_denied               int64
 | |
| 		empty_queries               int64
 | |
| 		total_ssl_connections       int64
 | |
| 		max_statement_time_exceeded int64
 | |
| 		// maria specific
 | |
| 		fbusy_time float64
 | |
| 		fcpu_time  float64
 | |
| 		// percona specific
 | |
| 		rows_fetched    int64
 | |
| 		table_rows_read int64
 | |
| 	)
 | |
| 
 | |
| 	switch l {
 | |
| 	case 23: // maria5
 | |
| 		return []interface{}{
 | |
| 			&user,
 | |
| 			&total_connections,
 | |
| 			&concurrent_connections,
 | |
| 			&connected_time,
 | |
| 			&fbusy_time,
 | |
| 			&fcpu_time,
 | |
| 			&bytes_received,
 | |
| 			&bytes_sent,
 | |
| 			&binlog_bytes_written,
 | |
| 			&rows_read,
 | |
| 			&rows_sent,
 | |
| 			&rows_deleted,
 | |
| 			&rows_inserted,
 | |
| 			&rows_updated,
 | |
| 			&select_commands,
 | |
| 			&update_commands,
 | |
| 			&other_commands,
 | |
| 			&commit_transactions,
 | |
| 			&rollback_transactions,
 | |
| 			&denied_connections,
 | |
| 			&lost_connections,
 | |
| 			&access_denied,
 | |
| 			&empty_queries,
 | |
| 		}, nil
 | |
| 	case 25: // maria10
 | |
| 		return []interface{}{
 | |
| 			&user,
 | |
| 			&total_connections,
 | |
| 			&concurrent_connections,
 | |
| 			&connected_time,
 | |
| 			&fbusy_time,
 | |
| 			&fcpu_time,
 | |
| 			&bytes_received,
 | |
| 			&bytes_sent,
 | |
| 			&binlog_bytes_written,
 | |
| 			&rows_read,
 | |
| 			&rows_sent,
 | |
| 			&rows_deleted,
 | |
| 			&rows_inserted,
 | |
| 			&rows_updated,
 | |
| 			&select_commands,
 | |
| 			&update_commands,
 | |
| 			&other_commands,
 | |
| 			&commit_transactions,
 | |
| 			&rollback_transactions,
 | |
| 			&denied_connections,
 | |
| 			&lost_connections,
 | |
| 			&access_denied,
 | |
| 			&empty_queries,
 | |
| 			&total_ssl_connections,
 | |
| 			&max_statement_time_exceeded,
 | |
| 		}, nil
 | |
| 	case 21: // mysql 5.5
 | |
| 		return []interface{}{
 | |
| 			&user,
 | |
| 			&total_connections,
 | |
| 			&concurrent_connections,
 | |
| 			&connected_time,
 | |
| 			&busy_time,
 | |
| 			&cpu_time,
 | |
| 			&bytes_received,
 | |
| 			&bytes_sent,
 | |
| 			&binlog_bytes_written,
 | |
| 			&rows_fetched,
 | |
| 			&rows_updated,
 | |
| 			&table_rows_read,
 | |
| 			&select_commands,
 | |
| 			&update_commands,
 | |
| 			&other_commands,
 | |
| 			&commit_transactions,
 | |
| 			&rollback_transactions,
 | |
| 			&denied_connections,
 | |
| 			&lost_connections,
 | |
| 			&access_denied,
 | |
| 			&empty_queries,
 | |
| 		}, nil
 | |
| 	case 22: // percona
 | |
| 		return []interface{}{
 | |
| 			&user,
 | |
| 			&total_connections,
 | |
| 			&concurrent_connections,
 | |
| 			&connected_time,
 | |
| 			&busy_time,
 | |
| 			&cpu_time,
 | |
| 			&bytes_received,
 | |
| 			&bytes_sent,
 | |
| 			&binlog_bytes_written,
 | |
| 			&rows_fetched,
 | |
| 			&rows_updated,
 | |
| 			&table_rows_read,
 | |
| 			&select_commands,
 | |
| 			&update_commands,
 | |
| 			&other_commands,
 | |
| 			&commit_transactions,
 | |
| 			&rollback_transactions,
 | |
| 			&denied_connections,
 | |
| 			&lost_connections,
 | |
| 			&access_denied,
 | |
| 			&empty_queries,
 | |
| 			&total_ssl_connections,
 | |
| 		}, nil
 | |
| 	}
 | |
| 
 | |
| 	return nil, fmt.Errorf("Not Supported - %d columns", l)
 | |
| }
 | |
| 
 | |
| // gatherPerfTableIOWaits can be used to get total count and time
 | |
| // of I/O wait event for each table and process
 | |
| func (m *Mysql) gatherPerfTableIOWaits(db *sql.DB, serv string, acc telegraf.Accumulator) error {
 | |
| 	rows, err := db.Query(perfTableIOWaitsQuery)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	defer rows.Close()
 | |
| 	var (
 | |
| 		objSchema, objName, servtag                       string
 | |
| 		countFetch, countInsert, countUpdate, countDelete float64
 | |
| 		timeFetch, timeInsert, timeUpdate, timeDelete     float64
 | |
| 	)
 | |
| 
 | |
| 	servtag = getDSNTag(serv)
 | |
| 
 | |
| 	for rows.Next() {
 | |
| 		err = rows.Scan(&objSchema, &objName,
 | |
| 			&countFetch, &countInsert, &countUpdate, &countDelete,
 | |
| 			&timeFetch, &timeInsert, &timeUpdate, &timeDelete,
 | |
| 		)
 | |
| 
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		tags := map[string]string{
 | |
| 			"server": servtag,
 | |
| 			"schema": objSchema,
 | |
| 			"name":   objName,
 | |
| 		}
 | |
| 
 | |
| 		fields := map[string]interface{}{
 | |
| 			"table_io_waits_total_fetch":          countFetch,
 | |
| 			"table_io_waits_total_insert":         countInsert,
 | |
| 			"table_io_waits_total_update":         countUpdate,
 | |
| 			"table_io_waits_total_delete":         countDelete,
 | |
| 			"table_io_waits_seconds_total_fetch":  timeFetch / picoSeconds,
 | |
| 			"table_io_waits_seconds_total_insert": timeInsert / picoSeconds,
 | |
| 			"table_io_waits_seconds_total_update": timeUpdate / picoSeconds,
 | |
| 			"table_io_waits_seconds_total_delete": timeDelete / picoSeconds,
 | |
| 		}
 | |
| 
 | |
| 		acc.AddFields("mysql_perf_schema", fields, tags)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // gatherPerfIndexIOWaits can be used to get total count and time
 | |
| // of I/O wait event for each index and process
 | |
| func (m *Mysql) gatherPerfIndexIOWaits(db *sql.DB, serv string, acc telegraf.Accumulator) error {
 | |
| 	rows, err := db.Query(perfIndexIOWaitsQuery)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer rows.Close()
 | |
| 
 | |
| 	var (
 | |
| 		objSchema, objName, indexName, servtag            string
 | |
| 		countFetch, countInsert, countUpdate, countDelete float64
 | |
| 		timeFetch, timeInsert, timeUpdate, timeDelete     float64
 | |
| 	)
 | |
| 
 | |
| 	servtag = getDSNTag(serv)
 | |
| 
 | |
| 	for rows.Next() {
 | |
| 		err = rows.Scan(&objSchema, &objName, &indexName,
 | |
| 			&countFetch, &countInsert, &countUpdate, &countDelete,
 | |
| 			&timeFetch, &timeInsert, &timeUpdate, &timeDelete,
 | |
| 		)
 | |
| 
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		tags := map[string]string{
 | |
| 			"server": servtag,
 | |
| 			"schema": objSchema,
 | |
| 			"name":   objName,
 | |
| 			"index":  indexName,
 | |
| 		}
 | |
| 		fields := map[string]interface{}{
 | |
| 			"index_io_waits_total_fetch":         countFetch,
 | |
| 			"index_io_waits_seconds_total_fetch": timeFetch / picoSeconds,
 | |
| 		}
 | |
| 
 | |
| 		// update write columns only when index is NONE
 | |
| 		if indexName == "NONE" {
 | |
| 			fields["index_io_waits_total_insert"] = countInsert
 | |
| 			fields["index_io_waits_total_update"] = countUpdate
 | |
| 			fields["index_io_waits_total_delete"] = countDelete
 | |
| 
 | |
| 			fields["index_io_waits_seconds_total_insert"] = timeInsert / picoSeconds
 | |
| 			fields["index_io_waits_seconds_total_update"] = timeUpdate / picoSeconds
 | |
| 			fields["index_io_waits_seconds_total_delete"] = timeDelete / picoSeconds
 | |
| 		}
 | |
| 
 | |
| 		acc.AddFields("mysql_perf_schema", fields, tags)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // gatherInfoSchemaAutoIncStatuses can be used to get auto incremented values of the column
 | |
| func (m *Mysql) gatherInfoSchemaAutoIncStatuses(db *sql.DB, serv string, acc telegraf.Accumulator) error {
 | |
| 	rows, err := db.Query(infoSchemaAutoIncQuery)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer rows.Close()
 | |
| 
 | |
| 	var (
 | |
| 		schema, table, column string
 | |
| 		incValue, maxInt      uint64
 | |
| 	)
 | |
| 
 | |
| 	servtag := getDSNTag(serv)
 | |
| 
 | |
| 	for rows.Next() {
 | |
| 		if err := rows.Scan(&schema, &table, &column, &incValue, &maxInt); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		tags := map[string]string{
 | |
| 			"server": servtag,
 | |
| 			"schema": schema,
 | |
| 			"table":  table,
 | |
| 			"column": column,
 | |
| 		}
 | |
| 		fields := make(map[string]interface{})
 | |
| 		fields["auto_increment_column"] = incValue
 | |
| 		fields["auto_increment_column_max"] = maxInt
 | |
| 
 | |
| 		if m.MetricVersion < 2 {
 | |
| 			acc.AddFields("mysql_info_schema", fields, tags)
 | |
| 		} else {
 | |
| 			acc.AddFields("mysql_table_schema", fields, tags)
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // gatherInnoDBMetrics can be used to fetch enabled metrics from
 | |
| // information_schema.INNODB_METRICS
 | |
| func (m *Mysql) gatherInnoDBMetrics(db *sql.DB, serv string, acc telegraf.Accumulator) error {
 | |
| 	// run query
 | |
| 	rows, err := db.Query(innoDBMetricsQuery)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer rows.Close()
 | |
| 
 | |
| 	// parse DSN and save server tag
 | |
| 	servtag := getDSNTag(serv)
 | |
| 	tags := map[string]string{"server": servtag}
 | |
| 	fields := make(map[string]interface{})
 | |
| 	for rows.Next() {
 | |
| 		var key string
 | |
| 		var val sql.RawBytes
 | |
| 		if err := rows.Scan(&key, &val); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		key = strings.ToLower(key)
 | |
| 		if value, ok := m.parseValue(val); ok {
 | |
| 			fields[key] = value
 | |
| 		}
 | |
| 		// Send 20 fields at a time
 | |
| 		if len(fields) >= 20 {
 | |
| 			acc.AddFields("mysql_innodb", fields, tags)
 | |
| 			fields = make(map[string]interface{})
 | |
| 		}
 | |
| 	}
 | |
| 	// Send any remaining fields
 | |
| 	if len(fields) > 0 {
 | |
| 		acc.AddFields("mysql_innodb", fields, tags)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // gatherPerfTableLockWaits can be used to get
 | |
| // the total number and time for SQL and external lock wait events
 | |
| // for each table and operation
 | |
| // requires the MySQL server to be enabled to save this metric
 | |
| func (m *Mysql) gatherPerfTableLockWaits(db *sql.DB, serv string, acc telegraf.Accumulator) error {
 | |
| 	// check if table exists,
 | |
| 	// if performance_schema is not enabled, tables do not exist
 | |
| 	// then there is no need to scan them
 | |
| 	var tableName string
 | |
| 	err := db.QueryRow(perfSchemaTablesQuery, "table_lock_waits_summary_by_table").Scan(&tableName)
 | |
| 	switch {
 | |
| 	case err == sql.ErrNoRows:
 | |
| 		return nil
 | |
| 	case err != nil:
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	rows, err := db.Query(perfTableLockWaitsQuery)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer rows.Close()
 | |
| 
 | |
| 	servtag := getDSNTag(serv)
 | |
| 
 | |
| 	var (
 | |
| 		objectSchema               string
 | |
| 		objectName                 string
 | |
| 		countReadNormal            float64
 | |
| 		countReadWithSharedLocks   float64
 | |
| 		countReadHighPriority      float64
 | |
| 		countReadNoInsert          float64
 | |
| 		countReadExternal          float64
 | |
| 		countWriteAllowWrite       float64
 | |
| 		countWriteConcurrentInsert float64
 | |
| 		countWriteLowPriority      float64
 | |
| 		countWriteNormal           float64
 | |
| 		countWriteExternal         float64
 | |
| 		timeReadNormal             float64
 | |
| 		timeReadWithSharedLocks    float64
 | |
| 		timeReadHighPriority       float64
 | |
| 		timeReadNoInsert           float64
 | |
| 		timeReadExternal           float64
 | |
| 		timeWriteAllowWrite        float64
 | |
| 		timeWriteConcurrentInsert  float64
 | |
| 		timeWriteLowPriority       float64
 | |
| 		timeWriteNormal            float64
 | |
| 		timeWriteExternal          float64
 | |
| 	)
 | |
| 
 | |
| 	for rows.Next() {
 | |
| 		err = rows.Scan(
 | |
| 			&objectSchema,
 | |
| 			&objectName,
 | |
| 			&countReadNormal,
 | |
| 			&countReadWithSharedLocks,
 | |
| 			&countReadHighPriority,
 | |
| 			&countReadNoInsert,
 | |
| 			&countReadExternal,
 | |
| 			&countWriteAllowWrite,
 | |
| 			&countWriteConcurrentInsert,
 | |
| 			&countWriteLowPriority,
 | |
| 			&countWriteNormal,
 | |
| 			&countWriteExternal,
 | |
| 			&timeReadNormal,
 | |
| 			&timeReadWithSharedLocks,
 | |
| 			&timeReadHighPriority,
 | |
| 			&timeReadNoInsert,
 | |
| 			&timeReadExternal,
 | |
| 			&timeWriteAllowWrite,
 | |
| 			&timeWriteConcurrentInsert,
 | |
| 			&timeWriteLowPriority,
 | |
| 			&timeWriteNormal,
 | |
| 			&timeWriteExternal,
 | |
| 		)
 | |
| 
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		tags := map[string]string{
 | |
| 			"server": servtag,
 | |
| 			"schema": objectSchema,
 | |
| 			"table":  objectName,
 | |
| 		}
 | |
| 
 | |
| 		sqlLWTags := copyTags(tags)
 | |
| 		sqlLWTags["perf_query"] = "sql_lock_waits_total"
 | |
| 		sqlLWFields := map[string]interface{}{
 | |
| 			"read_normal":             countReadNormal,
 | |
| 			"read_with_shared_locks":  countReadWithSharedLocks,
 | |
| 			"read_high_priority":      countReadHighPriority,
 | |
| 			"read_no_insert":          countReadNoInsert,
 | |
| 			"write_normal":            countWriteNormal,
 | |
| 			"write_allow_write":       countWriteAllowWrite,
 | |
| 			"write_concurrent_insert": countWriteConcurrentInsert,
 | |
| 			"write_low_priority":      countWriteLowPriority,
 | |
| 		}
 | |
| 		acc.AddFields("mysql_perf_schema", sqlLWFields, sqlLWTags)
 | |
| 
 | |
| 		externalLWTags := copyTags(tags)
 | |
| 		externalLWTags["perf_query"] = "external_lock_waits_total"
 | |
| 		externalLWFields := map[string]interface{}{
 | |
| 			"read":  countReadExternal,
 | |
| 			"write": countWriteExternal,
 | |
| 		}
 | |
| 		acc.AddFields("mysql_perf_schema", externalLWFields, externalLWTags)
 | |
| 
 | |
| 		sqlLWSecTotalTags := copyTags(tags)
 | |
| 		sqlLWSecTotalTags["perf_query"] = "sql_lock_waits_seconds_total"
 | |
| 		sqlLWSecTotalFields := map[string]interface{}{
 | |
| 			"read_normal":             timeReadNormal / picoSeconds,
 | |
| 			"read_with_shared_locks":  timeReadWithSharedLocks / picoSeconds,
 | |
| 			"read_high_priority":      timeReadHighPriority / picoSeconds,
 | |
| 			"read_no_insert":          timeReadNoInsert / picoSeconds,
 | |
| 			"write_normal":            timeWriteNormal / picoSeconds,
 | |
| 			"write_allow_write":       timeWriteAllowWrite / picoSeconds,
 | |
| 			"write_concurrent_insert": timeWriteConcurrentInsert / picoSeconds,
 | |
| 			"write_low_priority":      timeWriteLowPriority / picoSeconds,
 | |
| 		}
 | |
| 		acc.AddFields("mysql_perf_schema", sqlLWSecTotalFields, sqlLWSecTotalTags)
 | |
| 
 | |
| 		externalLWSecTotalTags := copyTags(tags)
 | |
| 		externalLWSecTotalTags["perf_query"] = "external_lock_waits_seconds_total"
 | |
| 		externalLWSecTotalFields := map[string]interface{}{
 | |
| 			"read":  timeReadExternal / picoSeconds,
 | |
| 			"write": timeWriteExternal / picoSeconds,
 | |
| 		}
 | |
| 		acc.AddFields("mysql_perf_schema", externalLWSecTotalFields, externalLWSecTotalTags)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // gatherPerfEventWaits can be used to get total time and number of event waits
 | |
| func (m *Mysql) gatherPerfEventWaits(db *sql.DB, serv string, acc telegraf.Accumulator) error {
 | |
| 	rows, err := db.Query(perfEventWaitsQuery)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer rows.Close()
 | |
| 
 | |
| 	var (
 | |
| 		event               string
 | |
| 		starCount, timeWait float64
 | |
| 	)
 | |
| 
 | |
| 	servtag := getDSNTag(serv)
 | |
| 	tags := map[string]string{
 | |
| 		"server": servtag,
 | |
| 	}
 | |
| 	for rows.Next() {
 | |
| 		if err := rows.Scan(&event, &starCount, &timeWait); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		tags["event_name"] = event
 | |
| 		fields := map[string]interface{}{
 | |
| 			"events_waits_total":         starCount,
 | |
| 			"events_waits_seconds_total": timeWait / picoSeconds,
 | |
| 		}
 | |
| 
 | |
| 		acc.AddFields("mysql_perf_schema", fields, tags)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // gatherPerfFileEvents can be used to get stats on file events
 | |
| func (m *Mysql) gatherPerfFileEventsStatuses(db *sql.DB, serv string, acc telegraf.Accumulator) error {
 | |
| 	rows, err := db.Query(perfFileEventsQuery)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	defer rows.Close()
 | |
| 
 | |
| 	var (
 | |
| 		eventName                                 string
 | |
| 		countRead, countWrite, countMisc          float64
 | |
| 		sumTimerRead, sumTimerWrite, sumTimerMisc float64
 | |
| 		sumNumBytesRead, sumNumBytesWrite         float64
 | |
| 	)
 | |
| 
 | |
| 	servtag := getDSNTag(serv)
 | |
| 	tags := map[string]string{
 | |
| 		"server": servtag,
 | |
| 	}
 | |
| 	for rows.Next() {
 | |
| 		err = rows.Scan(
 | |
| 			&eventName,
 | |
| 			&countRead, &sumTimerRead, &sumNumBytesRead,
 | |
| 			&countWrite, &sumTimerWrite, &sumNumBytesWrite,
 | |
| 			&countMisc, &sumTimerMisc,
 | |
| 		)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		tags["event_name"] = eventName
 | |
| 		fields := make(map[string]interface{})
 | |
| 
 | |
| 		miscTags := copyTags(tags)
 | |
| 		miscTags["mode"] = "misc"
 | |
| 		fields["file_events_total"] = countWrite
 | |
| 		fields["file_events_seconds_total"] = sumTimerMisc / picoSeconds
 | |
| 		acc.AddFields("mysql_perf_schema", fields, miscTags)
 | |
| 
 | |
| 		readTags := copyTags(tags)
 | |
| 		readTags["mode"] = "read"
 | |
| 		fields["file_events_total"] = countRead
 | |
| 		fields["file_events_seconds_total"] = sumTimerRead / picoSeconds
 | |
| 		fields["file_events_bytes_totals"] = sumNumBytesRead
 | |
| 		acc.AddFields("mysql_perf_schema", fields, readTags)
 | |
| 
 | |
| 		writeTags := copyTags(tags)
 | |
| 		writeTags["mode"] = "write"
 | |
| 		fields["file_events_total"] = countWrite
 | |
| 		fields["file_events_seconds_total"] = sumTimerWrite / picoSeconds
 | |
| 		fields["file_events_bytes_totals"] = sumNumBytesWrite
 | |
| 		acc.AddFields("mysql_perf_schema", fields, writeTags)
 | |
| 
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // gatherPerfEventsStatements can be used to get attributes of each event
 | |
| func (m *Mysql) gatherPerfEventsStatements(db *sql.DB, serv string, acc telegraf.Accumulator) error {
 | |
| 	query := fmt.Sprintf(
 | |
| 		perfEventsStatementsQuery,
 | |
| 		m.PerfEventsStatementsDigestTextLimit,
 | |
| 		m.PerfEventsStatementsTimeLimit,
 | |
| 		m.PerfEventsStatementsLimit,
 | |
| 	)
 | |
| 
 | |
| 	rows, err := db.Query(query)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	defer rows.Close()
 | |
| 
 | |
| 	var (
 | |
| 		schemaName, digest, digest_text      string
 | |
| 		count, queryTime, errors, warnings   float64
 | |
| 		rowsAffected, rowsSent, rowsExamined float64
 | |
| 		tmpTables, tmpDiskTables             float64
 | |
| 		sortMergePasses, sortRows            float64
 | |
| 		noIndexUsed                          float64
 | |
| 	)
 | |
| 
 | |
| 	servtag := getDSNTag(serv)
 | |
| 	tags := map[string]string{
 | |
| 		"server": servtag,
 | |
| 	}
 | |
| 
 | |
| 	for rows.Next() {
 | |
| 		err = rows.Scan(
 | |
| 			&schemaName, &digest, &digest_text,
 | |
| 			&count, &queryTime, &errors, &warnings,
 | |
| 			&rowsAffected, &rowsSent, &rowsExamined,
 | |
| 			&tmpTables, &tmpDiskTables,
 | |
| 			&sortMergePasses, &sortRows,
 | |
| 			&noIndexUsed,
 | |
| 		)
 | |
| 
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		tags["schema"] = schemaName
 | |
| 		tags["digest"] = digest
 | |
| 		tags["digest_text"] = digest_text
 | |
| 
 | |
| 		fields := map[string]interface{}{
 | |
| 			"events_statements_total":                   count,
 | |
| 			"events_statements_seconds_total":           queryTime / picoSeconds,
 | |
| 			"events_statements_errors_total":            errors,
 | |
| 			"events_statements_warnings_total":          warnings,
 | |
| 			"events_statements_rows_affected_total":     rowsAffected,
 | |
| 			"events_statements_rows_sent_total":         rowsSent,
 | |
| 			"events_statements_rows_examined_total":     rowsExamined,
 | |
| 			"events_statements_tmp_tables_total":        tmpTables,
 | |
| 			"events_statements_tmp_disk_tables_total":   tmpDiskTables,
 | |
| 			"events_statements_sort_merge_passes_total": sortMergePasses,
 | |
| 			"events_statements_sort_rows_total":         sortRows,
 | |
| 			"events_statements_no_index_used_total":     noIndexUsed,
 | |
| 		}
 | |
| 
 | |
| 		acc.AddFields("mysql_perf_schema", fields, tags)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // gatherTableSchema can be used to gather stats on each schema
 | |
| func (m *Mysql) gatherTableSchema(db *sql.DB, serv string, acc telegraf.Accumulator) error {
 | |
| 	var dbList []string
 | |
| 	servtag := getDSNTag(serv)
 | |
| 
 | |
| 	// if the list of databases if empty, then get all databases
 | |
| 	if len(m.TableSchemaDatabases) == 0 {
 | |
| 		rows, err := db.Query(dbListQuery)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer rows.Close()
 | |
| 
 | |
| 		var database string
 | |
| 		for rows.Next() {
 | |
| 			err = rows.Scan(&database)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 
 | |
| 			dbList = append(dbList, database)
 | |
| 		}
 | |
| 	} else {
 | |
| 		dbList = m.TableSchemaDatabases
 | |
| 	}
 | |
| 
 | |
| 	for _, database := range dbList {
 | |
| 		rows, err := db.Query(fmt.Sprintf(tableSchemaQuery, database))
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer rows.Close()
 | |
| 		var (
 | |
| 			tableSchema   string
 | |
| 			tableName     string
 | |
| 			tableType     string
 | |
| 			engine        string
 | |
| 			version       float64
 | |
| 			rowFormat     string
 | |
| 			tableRows     float64
 | |
| 			dataLength    float64
 | |
| 			indexLength   float64
 | |
| 			dataFree      float64
 | |
| 			createOptions string
 | |
| 		)
 | |
| 		for rows.Next() {
 | |
| 			err = rows.Scan(
 | |
| 				&tableSchema,
 | |
| 				&tableName,
 | |
| 				&tableType,
 | |
| 				&engine,
 | |
| 				&version,
 | |
| 				&rowFormat,
 | |
| 				&tableRows,
 | |
| 				&dataLength,
 | |
| 				&indexLength,
 | |
| 				&dataFree,
 | |
| 				&createOptions,
 | |
| 			)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			tags := map[string]string{"server": servtag}
 | |
| 			tags["schema"] = tableSchema
 | |
| 			tags["table"] = tableName
 | |
| 
 | |
| 			if m.MetricVersion < 2 {
 | |
| 				acc.AddFields(newNamespace("info_schema", "table_rows"),
 | |
| 					map[string]interface{}{"value": tableRows}, tags)
 | |
| 
 | |
| 				dlTags := copyTags(tags)
 | |
| 				dlTags["component"] = "data_length"
 | |
| 				acc.AddFields(newNamespace("info_schema", "table_size", "data_length"),
 | |
| 					map[string]interface{}{"value": dataLength}, dlTags)
 | |
| 
 | |
| 				ilTags := copyTags(tags)
 | |
| 				ilTags["component"] = "index_length"
 | |
| 				acc.AddFields(newNamespace("info_schema", "table_size", "index_length"),
 | |
| 					map[string]interface{}{"value": indexLength}, ilTags)
 | |
| 
 | |
| 				dfTags := copyTags(tags)
 | |
| 				dfTags["component"] = "data_free"
 | |
| 				acc.AddFields(newNamespace("info_schema", "table_size", "data_free"),
 | |
| 					map[string]interface{}{"value": dataFree}, dfTags)
 | |
| 			} else {
 | |
| 				acc.AddFields("mysql_table_schema",
 | |
| 					map[string]interface{}{"rows": tableRows}, tags)
 | |
| 
 | |
| 				acc.AddFields("mysql_table_schema",
 | |
| 					map[string]interface{}{"data_length": dataLength}, tags)
 | |
| 
 | |
| 				acc.AddFields("mysql_table_schema",
 | |
| 					map[string]interface{}{"index_length": indexLength}, tags)
 | |
| 
 | |
| 				acc.AddFields("mysql_table_schema",
 | |
| 					map[string]interface{}{"data_free": dataFree}, tags)
 | |
| 			}
 | |
| 
 | |
| 			versionTags := copyTags(tags)
 | |
| 			versionTags["type"] = tableType
 | |
| 			versionTags["engine"] = engine
 | |
| 			versionTags["row_format"] = rowFormat
 | |
| 			versionTags["create_options"] = createOptions
 | |
| 
 | |
| 			if m.MetricVersion < 2 {
 | |
| 				acc.AddFields(newNamespace("info_schema", "table_version"),
 | |
| 					map[string]interface{}{"value": version}, versionTags)
 | |
| 			} else {
 | |
| 				acc.AddFields("mysql_table_schema_version",
 | |
| 					map[string]interface{}{"table_version": version}, versionTags)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *Mysql) parseValue(value sql.RawBytes) (interface{}, bool) {
 | |
| 	if m.MetricVersion < 2 {
 | |
| 		return v1.ParseValue(value)
 | |
| 	} else {
 | |
| 		return parseValue(value)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // parseValue can be used to convert values such as "ON","OFF","Yes","No" to 0,1
 | |
| func parseValue(value sql.RawBytes) (interface{}, bool) {
 | |
| 	if bytes.EqualFold(value, []byte("YES")) || bytes.Compare(value, []byte("ON")) == 0 {
 | |
| 		return 1, true
 | |
| 	}
 | |
| 
 | |
| 	if bytes.EqualFold(value, []byte("NO")) || bytes.Compare(value, []byte("OFF")) == 0 {
 | |
| 		return 0, true
 | |
| 	}
 | |
| 
 | |
| 	if val, err := strconv.ParseInt(string(value), 10, 64); err == nil {
 | |
| 		return val, true
 | |
| 	}
 | |
| 	if val, err := strconv.ParseFloat(string(value), 64); err == nil {
 | |
| 		return val, true
 | |
| 	}
 | |
| 
 | |
| 	if len(string(value)) > 0 {
 | |
| 		return string(value), true
 | |
| 	}
 | |
| 	return nil, false
 | |
| }
 | |
| 
 | |
| // findThreadState can be used to find thread state by command and plain state
 | |
| func findThreadState(rawCommand, rawState string) string {
 | |
| 	var (
 | |
| 		// replace '_' symbol with space
 | |
| 		command = strings.Replace(strings.ToLower(rawCommand), "_", " ", -1)
 | |
| 		state   = strings.Replace(strings.ToLower(rawState), "_", " ", -1)
 | |
| 	)
 | |
| 	// if the state is already valid, then return it
 | |
| 	if _, ok := generalThreadStates[state]; ok {
 | |
| 		return state
 | |
| 	}
 | |
| 
 | |
| 	// if state is plain, return the mapping
 | |
| 	if mappedState, ok := stateStatusMappings[state]; ok {
 | |
| 		return mappedState
 | |
| 	}
 | |
| 	// if the state is any lock, return the special state
 | |
| 	if strings.Contains(state, "waiting for") && strings.Contains(state, "lock") {
 | |
| 		return "waiting for lock"
 | |
| 	}
 | |
| 
 | |
| 	if command == "sleep" && state == "" {
 | |
| 		return "idle"
 | |
| 	}
 | |
| 
 | |
| 	if command == "query" {
 | |
| 		return "executing"
 | |
| 	}
 | |
| 
 | |
| 	if command == "binlog dump" {
 | |
| 		return "replication master"
 | |
| 	}
 | |
| 	// if no mappings found and state is invalid, then return "other" state
 | |
| 	return "other"
 | |
| }
 | |
| 
 | |
| // newNamespace can be used to make a namespace
 | |
| func newNamespace(words ...string) string {
 | |
| 	return strings.Replace(strings.Join(words, "_"), " ", "_", -1)
 | |
| }
 | |
| 
 | |
| func copyTags(in map[string]string) map[string]string {
 | |
| 	out := make(map[string]string)
 | |
| 	for k, v := range in {
 | |
| 		out[k] = v
 | |
| 	}
 | |
| 	return out
 | |
| }
 | |
| 
 | |
| func dsnAddTimeout(dsn string) (string, error) {
 | |
| 	conf, err := mysql.ParseDSN(dsn)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 
 | |
| 	if conf.Timeout == 0 {
 | |
| 		conf.Timeout = time.Second * 5
 | |
| 	}
 | |
| 
 | |
| 	return conf.FormatDSN(), nil
 | |
| }
 | |
| 
 | |
| func getDSNTag(dsn string) string {
 | |
| 	conf, err := mysql.ParseDSN(dsn)
 | |
| 	if err != nil {
 | |
| 		return "127.0.0.1:3306"
 | |
| 	}
 | |
| 	return conf.Addr
 | |
| }
 | |
| 
 | |
| func init() {
 | |
| 	inputs.Add("mysql", func() telegraf.Input {
 | |
| 		return &Mysql{}
 | |
| 	})
 | |
| }
 |