renaming plugins -> inputs
This commit is contained in:
213
plugins/inputs/mysql/mysql.go
Normal file
213
plugins/inputs/mysql/mysql.go
Normal file
@@ -0,0 +1,213 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/influxdb/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
type Mysql struct {
|
||||
Servers []string
|
||||
}
|
||||
|
||||
var sampleConfig = `
|
||||
# specify servers via a url matching:
|
||||
# [username[:password]@][protocol[(address)]]/[?tls=[true|false|skip-verify]]
|
||||
# see https://github.com/go-sql-driver/mysql#dsn-data-source-name
|
||||
# e.g.
|
||||
# root:passwd@tcp(127.0.0.1:3306)/?tls=false
|
||||
# root@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)/"]
|
||||
`
|
||||
|
||||
func (m *Mysql) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (m *Mysql) Description() string {
|
||||
return "Read metrics from one or many mysql servers"
|
||||
}
|
||||
|
||||
var localhost = ""
|
||||
|
||||
func (m *Mysql) Gather(acc inputs.Accumulator) error {
|
||||
if len(m.Servers) == 0 {
|
||||
// if we can't get stats in this case, thats fine, don't report
|
||||
// an error.
|
||||
m.gatherServer(localhost, acc)
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, serv := range m.Servers {
|
||||
err := m.gatherServer(serv, acc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type mapping struct {
|
||||
onServer string
|
||||
inExport string
|
||||
}
|
||||
|
||||
var mappings = []*mapping{
|
||||
{
|
||||
onServer: "Aborted_",
|
||||
inExport: "aborted_",
|
||||
},
|
||||
{
|
||||
onServer: "Bytes_",
|
||||
inExport: "bytes_",
|
||||
},
|
||||
{
|
||||
onServer: "Com_",
|
||||
inExport: "commands_",
|
||||
},
|
||||
{
|
||||
onServer: "Created_",
|
||||
inExport: "created_",
|
||||
},
|
||||
{
|
||||
onServer: "Handler_",
|
||||
inExport: "handler_",
|
||||
},
|
||||
{
|
||||
onServer: "Innodb_",
|
||||
inExport: "innodb_",
|
||||
},
|
||||
{
|
||||
onServer: "Key_",
|
||||
inExport: "key_",
|
||||
},
|
||||
{
|
||||
onServer: "Open_",
|
||||
inExport: "open_",
|
||||
},
|
||||
{
|
||||
onServer: "Opened_",
|
||||
inExport: "opened_",
|
||||
},
|
||||
{
|
||||
onServer: "Qcache_",
|
||||
inExport: "qcache_",
|
||||
},
|
||||
{
|
||||
onServer: "Table_",
|
||||
inExport: "table_",
|
||||
},
|
||||
{
|
||||
onServer: "Tokudb_",
|
||||
inExport: "tokudb_",
|
||||
},
|
||||
{
|
||||
onServer: "Threads_",
|
||||
inExport: "threads_",
|
||||
},
|
||||
}
|
||||
|
||||
func (m *Mysql) gatherServer(serv string, acc inputs.Accumulator) error {
|
||||
// If user forgot the '/', add it
|
||||
if strings.HasSuffix(serv, ")") {
|
||||
serv = serv + "/"
|
||||
} else if serv == "localhost" {
|
||||
serv = ""
|
||||
}
|
||||
|
||||
db, err := sql.Open("mysql", serv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query(`SHOW /*!50002 GLOBAL */ STATUS`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var servtag string
|
||||
servtag, err = parseDSN(serv)
|
||||
if err != nil {
|
||||
servtag = "localhost"
|
||||
}
|
||||
tags := map[string]string{"server": servtag}
|
||||
fields := make(map[string]interface{})
|
||||
for rows.Next() {
|
||||
var name string
|
||||
var val interface{}
|
||||
|
||||
err = rows.Scan(&name, &val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var found bool
|
||||
|
||||
for _, mapped := range mappings {
|
||||
if strings.HasPrefix(name, mapped.onServer) {
|
||||
i, _ := strconv.Atoi(string(val.([]byte)))
|
||||
fields[mapped.inExport+name[len(mapped.onServer):]] = i
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
continue
|
||||
}
|
||||
|
||||
switch name {
|
||||
case "Queries":
|
||||
i, err := strconv.ParseInt(string(val.([]byte)), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields["queries"] = i
|
||||
case "Slow_queries":
|
||||
i, err := strconv.ParseInt(string(val.([]byte)), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields["slow_queries"] = i
|
||||
}
|
||||
}
|
||||
acc.AddFields("mysql", fields, tags)
|
||||
|
||||
conn_rows, err := db.Query("SELECT user, sum(1) FROM INFORMATION_SCHEMA.PROCESSLIST GROUP BY user")
|
||||
|
||||
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{})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fields["connections"] = connections
|
||||
acc.AddFields("mysql_users", fields, tags)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("mysql", func() inputs.Input {
|
||||
return &Mysql{}
|
||||
})
|
||||
}
|
||||
86
plugins/inputs/mysql/mysql_test.go
Normal file
86
plugins/inputs/mysql/mysql_test.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/influxdb/telegraf/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMysqlDefaultsToLocal(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
m := &Mysql{
|
||||
Servers: []string{fmt.Sprintf("root@tcp(%s:3306)/", testutil.GetLocalHost())},
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
err := m.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, acc.HasMeasurement("mysql"))
|
||||
}
|
||||
|
||||
func TestMysqlParseDSN(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{
|
||||
"",
|
||||
"127.0.0.1:3306",
|
||||
},
|
||||
{
|
||||
"localhost",
|
||||
"127.0.0.1:3306",
|
||||
},
|
||||
{
|
||||
"127.0.0.1",
|
||||
"127.0.0.1:3306",
|
||||
},
|
||||
{
|
||||
"tcp(192.168.1.1:3306)/",
|
||||
"192.168.1.1:3306",
|
||||
},
|
||||
{
|
||||
"tcp(localhost)/",
|
||||
"localhost",
|
||||
},
|
||||
{
|
||||
"root:passwd@tcp(192.168.1.1:3306)/?tls=false",
|
||||
"192.168.1.1:3306",
|
||||
},
|
||||
{
|
||||
"root@tcp(127.0.0.1:3306)/?tls=false",
|
||||
"127.0.0.1:3306",
|
||||
},
|
||||
{
|
||||
"root:passwd@tcp(localhost:3036)/dbname?allowOldPasswords=1",
|
||||
"localhost:3036",
|
||||
},
|
||||
{
|
||||
"root:foo@bar@tcp(192.1.1.1:3306)/?tls=false",
|
||||
"192.1.1.1:3306",
|
||||
},
|
||||
{
|
||||
"root:f00@b4r@tcp(192.1.1.1:3306)/?tls=false",
|
||||
"192.1.1.1:3306",
|
||||
},
|
||||
{
|
||||
"root:fl!p11@tcp(192.1.1.1:3306)/?tls=false",
|
||||
"192.1.1.1:3306",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
output, _ := parseDSN(test.input)
|
||||
if output != test.output {
|
||||
t.Errorf("Expected %s, got %s\n", test.output, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
85
plugins/inputs/mysql/parse_dsn.go
Normal file
85
plugins/inputs/mysql/parse_dsn.go
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// parseDSN parses the DSN string to a config
|
||||
func parseDSN(dsn string) (string, error) {
|
||||
//var user, passwd string
|
||||
var addr, net string
|
||||
|
||||
// [user[:password]@][net[(addr)]]/dbname[?param1=value1¶mN=valueN]
|
||||
// Find the last '/' (since the password or the net addr might contain a '/')
|
||||
for i := len(dsn) - 1; i >= 0; i-- {
|
||||
if dsn[i] == '/' {
|
||||
var j, k int
|
||||
|
||||
// left part is empty if i <= 0
|
||||
if i > 0 {
|
||||
// [username[:password]@][protocol[(address)]]
|
||||
// Find the last '@' in dsn[:i]
|
||||
for j = i; j >= 0; j-- {
|
||||
if dsn[j] == '@' {
|
||||
// username[:password]
|
||||
// Find the first ':' in dsn[:j]
|
||||
for k = 0; k < j; k++ {
|
||||
if dsn[k] == ':' {
|
||||
//passwd = dsn[k+1 : j]
|
||||
break
|
||||
}
|
||||
}
|
||||
//user = dsn[:k]
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// [protocol[(address)]]
|
||||
// Find the first '(' in dsn[j+1:i]
|
||||
for k = j + 1; k < i; k++ {
|
||||
if dsn[k] == '(' {
|
||||
// dsn[i-1] must be == ')' if an address is specified
|
||||
if dsn[i-1] != ')' {
|
||||
if strings.ContainsRune(dsn[k+1:i], ')') {
|
||||
return "", errors.New("Invalid DSN unescaped")
|
||||
}
|
||||
return "", errors.New("Invalid DSN Addr")
|
||||
}
|
||||
addr = dsn[k+1 : i-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
net = dsn[j+1 : k]
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Set default network if empty
|
||||
if net == "" {
|
||||
net = "tcp"
|
||||
}
|
||||
|
||||
// Set default address if empty
|
||||
if addr == "" {
|
||||
switch net {
|
||||
case "tcp":
|
||||
addr = "127.0.0.1:3306"
|
||||
case "unix":
|
||||
addr = "/tmp/mysql.sock"
|
||||
default:
|
||||
return "", errors.New("Default addr for network '" + net + "' unknown")
|
||||
}
|
||||
}
|
||||
|
||||
return addr, nil
|
||||
}
|
||||
Reference in New Issue
Block a user