github.com/netdata/go.d.plugin@v0.58.1/modules/mysql/mysql.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package mysql
     4  
     5  import (
     6  	"database/sql"
     7  	_ "embed"
     8  	"strings"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/blang/semver/v4"
    13  	"github.com/go-sql-driver/mysql"
    14  	_ "github.com/go-sql-driver/mysql"
    15  
    16  	"github.com/netdata/go.d.plugin/agent/module"
    17  	"github.com/netdata/go.d.plugin/pkg/web"
    18  )
    19  
    20  //go:embed "config_schema.json"
    21  var configSchema string
    22  
    23  func init() {
    24  	module.Register("mysql", module.Creator{
    25  		JobConfigSchema: configSchema,
    26  		Create:          func() module.Module { return New() },
    27  	})
    28  }
    29  
    30  func New() *MySQL {
    31  	return &MySQL{
    32  		Config: Config{
    33  			DSN:     "root@tcp(localhost:3306)/",
    34  			Timeout: web.Duration{Duration: time.Second},
    35  		},
    36  
    37  		charts:                         baseCharts.Copy(),
    38  		addInnoDBOSLogOnce:             &sync.Once{},
    39  		addBinlogOnce:                  &sync.Once{},
    40  		addMyISAMOnce:                  &sync.Once{},
    41  		addInnodbDeadlocksOnce:         &sync.Once{},
    42  		addGaleraOnce:                  &sync.Once{},
    43  		addQCacheOnce:                  &sync.Once{},
    44  		addTableOpenCacheOverflowsOnce: &sync.Once{},
    45  		doSlaveStatus:                  true,
    46  		doUserStatistics:               true,
    47  		collectedReplConns:             make(map[string]bool),
    48  		collectedUsers:                 make(map[string]bool),
    49  
    50  		recheckGlobalVarsEvery: time.Minute * 10,
    51  	}
    52  }
    53  
    54  type Config struct {
    55  	DSN         string       `yaml:"dsn"`
    56  	MyCNF       string       `yaml:"my.cnf"`
    57  	UpdateEvery int          `yaml:"update_every"`
    58  	Timeout     web.Duration `yaml:"timeout"`
    59  }
    60  
    61  type MySQL struct {
    62  	module.Base
    63  	Config `yaml:",inline"`
    64  
    65  	db        *sql.DB
    66  	safeDSN   string
    67  	version   *semver.Version
    68  	isMariaDB bool
    69  	isPercona bool
    70  
    71  	charts *module.Charts
    72  
    73  	addInnoDBOSLogOnce             *sync.Once
    74  	addBinlogOnce                  *sync.Once
    75  	addMyISAMOnce                  *sync.Once
    76  	addInnodbDeadlocksOnce         *sync.Once
    77  	addGaleraOnce                  *sync.Once
    78  	addQCacheOnce                  *sync.Once
    79  	addTableOpenCacheOverflowsOnce *sync.Once
    80  
    81  	doSlaveStatus      bool
    82  	collectedReplConns map[string]bool
    83  	doUserStatistics   bool
    84  	collectedUsers     map[string]bool
    85  
    86  	recheckGlobalVarsTime    time.Time
    87  	recheckGlobalVarsEvery   time.Duration
    88  	varMaxConns              int64
    89  	varTableOpenCache        int64
    90  	varDisabledStorageEngine string
    91  	varLogBin                string
    92  	varPerformanceSchema     string
    93  }
    94  
    95  func (m *MySQL) Init() bool {
    96  	if m.MyCNF != "" {
    97  		dsn, err := dsnFromFile(m.MyCNF)
    98  		if err != nil {
    99  			m.Error(err)
   100  			return false
   101  		}
   102  		m.DSN = dsn
   103  	}
   104  
   105  	if m.DSN == "" {
   106  		m.Error("DSN not set")
   107  		return false
   108  	}
   109  
   110  	cfg, err := mysql.ParseDSN(m.DSN)
   111  	if err != nil {
   112  		m.Errorf("error on parsing DSN: %v", err)
   113  		return false
   114  	}
   115  
   116  	cfg.Passwd = strings.Repeat("*", len(cfg.Passwd))
   117  	m.safeDSN = cfg.FormatDSN()
   118  
   119  	m.Debugf("using DSN [%s]", m.DSN)
   120  	return true
   121  }
   122  
   123  func (m *MySQL) Check() bool {
   124  	return len(m.Collect()) > 0
   125  }
   126  
   127  func (m *MySQL) Charts() *module.Charts {
   128  	return m.charts
   129  }
   130  
   131  func (m *MySQL) Collect() map[string]int64 {
   132  	mx, err := m.collect()
   133  	if err != nil {
   134  		m.Error(err)
   135  	}
   136  
   137  	if len(mx) == 0 {
   138  		return nil
   139  	}
   140  	return mx
   141  }
   142  
   143  func (m *MySQL) Cleanup() {
   144  	if m.db == nil {
   145  		return
   146  	}
   147  	if err := m.db.Close(); err != nil {
   148  		m.Errorf("cleanup: error on closing the mysql database [%s]: %v", m.safeDSN, err)
   149  	}
   150  	m.db = nil
   151  }