chore: Fix linter findings for `revive:exported` in `plugins/inputs/m*` (#16191)
This commit is contained in:
parent
d075815f29
commit
8a7947abbb
|
|
@ -22,7 +22,7 @@ const (
|
||||||
var mailchimpDatacenter = regexp.MustCompile("[a-z]+[0-9]+$")
|
var mailchimpDatacenter = regexp.MustCompile("[a-z]+[0-9]+$")
|
||||||
|
|
||||||
type chimpAPI struct {
|
type chimpAPI struct {
|
||||||
Transport http.RoundTripper
|
transport http.RoundTripper
|
||||||
debug bool
|
debug bool
|
||||||
|
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
|
|
@ -32,30 +32,30 @@ type chimpAPI struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type reportsParams struct {
|
type reportsParams struct {
|
||||||
Count string
|
count string
|
||||||
Offset string
|
offset string
|
||||||
SinceSendTime string
|
sinceSendTime string
|
||||||
BeforeSendTime string
|
beforeSendTime string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *reportsParams) String() string {
|
func (p *reportsParams) String() string {
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
if p.Count != "" {
|
if p.count != "" {
|
||||||
v.Set("count", p.Count)
|
v.Set("count", p.count)
|
||||||
}
|
}
|
||||||
if p.Offset != "" {
|
if p.offset != "" {
|
||||||
v.Set("offset", p.Offset)
|
v.Set("offset", p.offset)
|
||||||
}
|
}
|
||||||
if p.BeforeSendTime != "" {
|
if p.beforeSendTime != "" {
|
||||||
v.Set("before_send_time", p.BeforeSendTime)
|
v.Set("before_send_time", p.beforeSendTime)
|
||||||
}
|
}
|
||||||
if p.SinceSendTime != "" {
|
if p.sinceSendTime != "" {
|
||||||
v.Set("since_send_time", p.SinceSendTime)
|
v.Set("since_send_time", p.sinceSendTime)
|
||||||
}
|
}
|
||||||
return v.Encode()
|
return v.Encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewChimpAPI(apiKey string, log telegraf.Logger) *chimpAPI {
|
func newChimpAPI(apiKey string, log telegraf.Logger) *chimpAPI {
|
||||||
u := &url.URL{}
|
u := &url.URL{}
|
||||||
u.Scheme = "https"
|
u.Scheme = "https"
|
||||||
u.Host = mailchimpDatacenter.FindString(apiKey) + ".api.mailchimp.com"
|
u.Host = mailchimpDatacenter.FindString(apiKey) + ".api.mailchimp.com"
|
||||||
|
|
@ -86,7 +86,7 @@ func chimpErrorCheck(body []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *chimpAPI) GetReports(params reportsParams) (reportsResponse, error) {
|
func (a *chimpAPI) getReports(params reportsParams) (reportsResponse, error) {
|
||||||
a.Lock()
|
a.Lock()
|
||||||
defer a.Unlock()
|
defer a.Unlock()
|
||||||
a.url.Path = reportsEndpoint
|
a.url.Path = reportsEndpoint
|
||||||
|
|
@ -105,7 +105,7 @@ func (a *chimpAPI) GetReports(params reportsParams) (reportsResponse, error) {
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *chimpAPI) GetReport(campaignID string) (report, error) {
|
func (a *chimpAPI) getReport(campaignID string) (report, error) {
|
||||||
a.Lock()
|
a.Lock()
|
||||||
defer a.Unlock()
|
defer a.Unlock()
|
||||||
a.url.Path = fmt.Sprintf(reportsEndpointCampaign, campaignID)
|
a.url.Path = fmt.Sprintf(reportsEndpointCampaign, campaignID)
|
||||||
|
|
@ -126,7 +126,7 @@ func (a *chimpAPI) GetReport(campaignID string) (report, error) {
|
||||||
|
|
||||||
func (a *chimpAPI) runChimp(params reportsParams) ([]byte, error) {
|
func (a *chimpAPI) runChimp(params reportsParams) ([]byte, error) {
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Transport: a.Transport,
|
Transport: a.transport,
|
||||||
Timeout: 4 * time.Second,
|
Timeout: 4 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,12 @@ import (
|
||||||
var sampleConfig string
|
var sampleConfig string
|
||||||
|
|
||||||
type MailChimp struct {
|
type MailChimp struct {
|
||||||
|
APIKey string `toml:"api_key"`
|
||||||
|
DaysOld int `toml:"days_old"`
|
||||||
|
CampaignID string `toml:"campaign_id"`
|
||||||
|
Log telegraf.Logger `toml:"-"`
|
||||||
|
|
||||||
api *chimpAPI
|
api *chimpAPI
|
||||||
|
|
||||||
APIKey string `toml:"api_key"`
|
|
||||||
DaysOld int `toml:"days_old"`
|
|
||||||
CampaignID string `toml:"campaign_id"`
|
|
||||||
|
|
||||||
Log telegraf.Logger `toml:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*MailChimp) SampleConfig() string {
|
func (*MailChimp) SampleConfig() string {
|
||||||
|
|
@ -28,7 +27,7 @@ func (*MailChimp) SampleConfig() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MailChimp) Init() error {
|
func (m *MailChimp) Init() error {
|
||||||
m.api = NewChimpAPI(m.APIKey, m.Log)
|
m.api = newChimpAPI(m.APIKey, m.Log)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -45,8 +44,8 @@ func (m *MailChimp) Gather(acc telegraf.Accumulator) error {
|
||||||
since = now.Add(-d).Format(time.RFC3339)
|
since = now.Add(-d).Format(time.RFC3339)
|
||||||
}
|
}
|
||||||
|
|
||||||
reports, err := m.api.GetReports(reportsParams{
|
reports, err := m.api.getReports(reportsParams{
|
||||||
SinceSendTime: since,
|
sinceSendTime: since,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -57,7 +56,7 @@ func (m *MailChimp) Gather(acc telegraf.Accumulator) error {
|
||||||
gatherReport(acc, report, now)
|
gatherReport(acc, report, now)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
report, err := m.api.GetReport(m.CampaignID)
|
report, err := m.api.getReport(m.CampaignID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,69 +19,69 @@ import (
|
||||||
//go:embed sample.conf
|
//go:embed sample.conf
|
||||||
var sampleConfig string
|
var sampleConfig string
|
||||||
|
|
||||||
// Marklogic configuration toml
|
const (
|
||||||
|
// MarkLogic v2 management api endpoints for hosts status
|
||||||
|
statsPath = "/manage/v2/hosts/"
|
||||||
|
viewFormat = "view=status&format=json"
|
||||||
|
)
|
||||||
|
|
||||||
type Marklogic struct {
|
type Marklogic struct {
|
||||||
URL string `toml:"url"`
|
URL string `toml:"url"`
|
||||||
Hosts []string `toml:"hosts"`
|
Hosts []string `toml:"hosts"`
|
||||||
Username string `toml:"username"`
|
Username string `toml:"username"`
|
||||||
Password string `toml:"password"`
|
Password string `toml:"password"`
|
||||||
Sources []string
|
|
||||||
|
|
||||||
tls.ClientConfig
|
tls.ClientConfig
|
||||||
|
|
||||||
client *http.Client
|
client *http.Client
|
||||||
|
sources []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type MlPointInt struct {
|
type mlPointInt struct {
|
||||||
Value int `json:"value"`
|
Value int `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MlPointFloat struct {
|
type mlPointFloat struct {
|
||||||
Value float64 `json:"value"`
|
Value float64 `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MlPointBool struct {
|
type mlPointBool struct {
|
||||||
Value bool `json:"value"`
|
Value bool `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarkLogic v2 management api endpoints for hosts status
|
type mlHost struct {
|
||||||
const statsPath = "/manage/v2/hosts/"
|
|
||||||
const viewFormat = "view=status&format=json"
|
|
||||||
|
|
||||||
type MlHost struct {
|
|
||||||
HostStatus struct {
|
HostStatus struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
StatusProperties struct {
|
StatusProperties struct {
|
||||||
Online MlPointBool `json:"online"`
|
Online mlPointBool `json:"online"`
|
||||||
LoadProperties struct {
|
LoadProperties struct {
|
||||||
TotalLoad MlPointFloat `json:"total-load"`
|
TotalLoad mlPointFloat `json:"total-load"`
|
||||||
} `json:"load-properties"`
|
} `json:"load-properties"`
|
||||||
RateProperties struct {
|
RateProperties struct {
|
||||||
TotalRate MlPointFloat `json:"total-rate"`
|
TotalRate mlPointFloat `json:"total-rate"`
|
||||||
} `json:"rate-properties"`
|
} `json:"rate-properties"`
|
||||||
StatusDetail struct {
|
StatusDetail struct {
|
||||||
Cpus MlPointInt `json:"cpus"`
|
Cpus mlPointInt `json:"cpus"`
|
||||||
Cores MlPointInt `json:"cores"`
|
Cores mlPointInt `json:"cores"`
|
||||||
TotalCPUStatUser float64 `json:"total-cpu-stat-user"`
|
TotalCPUStatUser float64 `json:"total-cpu-stat-user"`
|
||||||
TotalCPUStatSystem float64 `json:"total-cpu-stat-system"`
|
TotalCPUStatSystem float64 `json:"total-cpu-stat-system"`
|
||||||
TotalCPUStatIdle float64 `json:"total-cpu-stat-idle"`
|
TotalCPUStatIdle float64 `json:"total-cpu-stat-idle"`
|
||||||
TotalCPUStatIowait float64 `json:"total-cpu-stat-iowait"`
|
TotalCPUStatIowait float64 `json:"total-cpu-stat-iowait"`
|
||||||
MemoryProcessSize MlPointInt `json:"memory-process-size"`
|
MemoryProcessSize mlPointInt `json:"memory-process-size"`
|
||||||
MemoryProcessRss MlPointInt `json:"memory-process-rss"`
|
MemoryProcessRss mlPointInt `json:"memory-process-rss"`
|
||||||
MemorySystemTotal MlPointInt `json:"memory-system-total"`
|
MemorySystemTotal mlPointInt `json:"memory-system-total"`
|
||||||
MemorySystemFree MlPointInt `json:"memory-system-free"`
|
MemorySystemFree mlPointInt `json:"memory-system-free"`
|
||||||
MemoryProcessSwapSize MlPointInt `json:"memory-process-swap-size"`
|
MemoryProcessSwapSize mlPointInt `json:"memory-process-swap-size"`
|
||||||
MemorySize MlPointInt `json:"memory-size"`
|
MemorySize mlPointInt `json:"memory-size"`
|
||||||
HostSize MlPointInt `json:"host-size"`
|
HostSize mlPointInt `json:"host-size"`
|
||||||
LogDeviceSpace MlPointInt `json:"log-device-space"`
|
LogDeviceSpace mlPointInt `json:"log-device-space"`
|
||||||
DataDirSpace MlPointInt `json:"data-dir-space"`
|
DataDirSpace mlPointInt `json:"data-dir-space"`
|
||||||
QueryReadBytes MlPointInt `json:"query-read-bytes"`
|
QueryReadBytes mlPointInt `json:"query-read-bytes"`
|
||||||
QueryReadLoad MlPointInt `json:"query-read-load"`
|
QueryReadLoad mlPointInt `json:"query-read-load"`
|
||||||
MergeReadLoad MlPointInt `json:"merge-read-load"`
|
MergeReadLoad mlPointInt `json:"merge-read-load"`
|
||||||
MergeWriteLoad MlPointInt `json:"merge-write-load"`
|
MergeWriteLoad mlPointInt `json:"merge-write-load"`
|
||||||
HTTPServerReceiveBytes MlPointInt `json:"http-server-receive-bytes"`
|
HTTPServerReceiveBytes mlPointInt `json:"http-server-receive-bytes"`
|
||||||
HTTPServerSendBytes MlPointInt `json:"http-server-send-bytes"`
|
HTTPServerSendBytes mlPointInt `json:"http-server-send-bytes"`
|
||||||
} `json:"status-detail"`
|
} `json:"status-detail"`
|
||||||
} `json:"status-properties"`
|
} `json:"status-properties"`
|
||||||
} `json:"host-status"`
|
} `json:"host-status"`
|
||||||
|
|
@ -91,7 +91,6 @@ func (*Marklogic) SampleConfig() string {
|
||||||
return sampleConfig
|
return sampleConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init parse all source URLs and place on the Marklogic struct
|
|
||||||
func (c *Marklogic) Init() error {
|
func (c *Marklogic) Init() error {
|
||||||
if len(c.URL) == 0 {
|
if len(c.URL) == 0 {
|
||||||
c.URL = "http://localhost:8002/"
|
c.URL = "http://localhost:8002/"
|
||||||
|
|
@ -108,12 +107,11 @@ func (c *Marklogic) Init() error {
|
||||||
|
|
||||||
addr.RawQuery = viewFormat
|
addr.RawQuery = viewFormat
|
||||||
u := addr.String()
|
u := addr.String()
|
||||||
c.Sources = append(c.Sources, u)
|
c.sources = append(c.sources, u)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather metrics from HTTP Server.
|
|
||||||
func (c *Marklogic) Gather(accumulator telegraf.Accumulator) error {
|
func (c *Marklogic) Gather(accumulator telegraf.Accumulator) error {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
|
@ -127,7 +125,7 @@ func (c *Marklogic) Gather(accumulator telegraf.Accumulator) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Range over all source URL's appended to the struct
|
// Range over all source URL's appended to the struct
|
||||||
for _, serv := range c.Sources {
|
for _, serv := range c.sources {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(serv string) {
|
go func(serv string) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
@ -143,7 +141,7 @@ func (c *Marklogic) Gather(accumulator telegraf.Accumulator) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Marklogic) fetchAndInsertData(acc telegraf.Accumulator, address string) error {
|
func (c *Marklogic) fetchAndInsertData(acc telegraf.Accumulator, address string) error {
|
||||||
ml := &MlHost{}
|
ml := &mlHost{}
|
||||||
if err := c.gatherJSONData(address, ml); err != nil {
|
if err := c.gatherJSONData(address, ml); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ func TestMarklogic(t *testing.T) {
|
||||||
ml := &Marklogic{
|
ml := &Marklogic{
|
||||||
Hosts: []string{"example1"},
|
Hosts: []string{"example1"},
|
||||||
URL: ts.URL,
|
URL: ts.URL,
|
||||||
// Sources: []string{"http://localhost:8002/manage/v2/hosts/hostname1?view=status&format=json"},
|
// sources: []string{"http://localhost:8002/manage/v2/hosts/hostname1?view=status&format=json"},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a test accumulator
|
// Create a test accumulator
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,6 @@ import (
|
||||||
//go:embed sample.conf
|
//go:embed sample.conf
|
||||||
var sampleConfig string
|
var sampleConfig string
|
||||||
|
|
||||||
// Mcrouter is a mcrouter plugin
|
|
||||||
type Mcrouter struct {
|
|
||||||
Servers []string
|
|
||||||
Timeout config.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
// enum for statType
|
// enum for statType
|
||||||
type statType int
|
type statType int
|
||||||
|
|
||||||
|
|
@ -35,86 +29,90 @@ const (
|
||||||
typeFloat statType = iota
|
typeFloat statType = iota
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultTimeout = 5 * time.Second
|
var (
|
||||||
|
defaultTimeout = 5 * time.Second
|
||||||
|
defaultServerURL = url.URL{
|
||||||
|
Scheme: "tcp",
|
||||||
|
Host: "localhost:11211",
|
||||||
|
}
|
||||||
|
// The list of metrics that should be sent
|
||||||
|
sendMetrics = map[string]statType{
|
||||||
|
"uptime": typeInt,
|
||||||
|
"num_servers": typeInt,
|
||||||
|
"num_servers_new": typeInt,
|
||||||
|
"num_servers_up": typeInt,
|
||||||
|
"num_servers_down": typeInt,
|
||||||
|
"num_servers_closed": typeInt,
|
||||||
|
"num_clients": typeInt,
|
||||||
|
"num_suspect_servers": typeInt,
|
||||||
|
"destination_batches_sum": typeInt,
|
||||||
|
"destination_requests_sum": typeInt,
|
||||||
|
"outstanding_route_get_reqs_queued": typeInt,
|
||||||
|
"outstanding_route_update_reqs_queued": typeInt,
|
||||||
|
"outstanding_route_get_avg_queue_size": typeInt,
|
||||||
|
"outstanding_route_update_avg_queue_size": typeInt,
|
||||||
|
"outstanding_route_get_avg_wait_time_sec": typeInt,
|
||||||
|
"outstanding_route_update_avg_wait_time_sec": typeInt,
|
||||||
|
"retrans_closed_connections": typeInt,
|
||||||
|
"destination_pending_reqs": typeInt,
|
||||||
|
"destination_inflight_reqs": typeInt,
|
||||||
|
"destination_batch_size": typeInt,
|
||||||
|
"asynclog_requests": typeInt,
|
||||||
|
"proxy_reqs_processing": typeInt,
|
||||||
|
"proxy_reqs_waiting": typeInt,
|
||||||
|
"client_queue_notify_period": typeInt,
|
||||||
|
"rusage_system": typeFloat,
|
||||||
|
"rusage_user": typeFloat,
|
||||||
|
"ps_num_minor_faults": typeInt,
|
||||||
|
"ps_num_major_faults": typeInt,
|
||||||
|
"ps_user_time_sec": typeFloat,
|
||||||
|
"ps_system_time_sec": typeFloat,
|
||||||
|
"ps_vsize": typeInt,
|
||||||
|
"ps_rss": typeInt,
|
||||||
|
"fibers_allocated": typeInt,
|
||||||
|
"fibers_pool_size": typeInt,
|
||||||
|
"fibers_stack_high_watermark": typeInt,
|
||||||
|
"successful_client_connections": typeInt,
|
||||||
|
"duration_us": typeInt,
|
||||||
|
"destination_max_pending_reqs": typeInt,
|
||||||
|
"destination_max_inflight_reqs": typeInt,
|
||||||
|
"retrans_per_kbyte_max": typeInt,
|
||||||
|
"cmd_get_count": typeInt,
|
||||||
|
"cmd_delete_out": typeInt,
|
||||||
|
"cmd_lease_get": typeInt,
|
||||||
|
"cmd_set": typeInt,
|
||||||
|
"cmd_get_out_all": typeInt,
|
||||||
|
"cmd_get_out": typeInt,
|
||||||
|
"cmd_lease_set_count": typeInt,
|
||||||
|
"cmd_other_out_all": typeInt,
|
||||||
|
"cmd_lease_get_out": typeInt,
|
||||||
|
"cmd_set_count": typeInt,
|
||||||
|
"cmd_lease_set_out": typeInt,
|
||||||
|
"cmd_delete_count": typeInt,
|
||||||
|
"cmd_other": typeInt,
|
||||||
|
"cmd_delete": typeInt,
|
||||||
|
"cmd_get": typeInt,
|
||||||
|
"cmd_lease_set": typeInt,
|
||||||
|
"cmd_set_out": typeInt,
|
||||||
|
"cmd_lease_get_count": typeInt,
|
||||||
|
"cmd_other_out": typeInt,
|
||||||
|
"cmd_lease_get_out_all": typeInt,
|
||||||
|
"cmd_set_out_all": typeInt,
|
||||||
|
"cmd_other_count": typeInt,
|
||||||
|
"cmd_delete_out_all": typeInt,
|
||||||
|
"cmd_lease_set_out_all": typeInt,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
var defaultServerURL = url.URL{
|
type Mcrouter struct {
|
||||||
Scheme: "tcp",
|
Servers []string `toml:"servers"`
|
||||||
Host: "localhost:11211",
|
Timeout config.Duration `toml:"timeout"`
|
||||||
}
|
|
||||||
|
|
||||||
// The list of metrics that should be sent
|
|
||||||
var sendMetrics = map[string]statType{
|
|
||||||
"uptime": typeInt,
|
|
||||||
"num_servers": typeInt,
|
|
||||||
"num_servers_new": typeInt,
|
|
||||||
"num_servers_up": typeInt,
|
|
||||||
"num_servers_down": typeInt,
|
|
||||||
"num_servers_closed": typeInt,
|
|
||||||
"num_clients": typeInt,
|
|
||||||
"num_suspect_servers": typeInt,
|
|
||||||
"destination_batches_sum": typeInt,
|
|
||||||
"destination_requests_sum": typeInt,
|
|
||||||
"outstanding_route_get_reqs_queued": typeInt,
|
|
||||||
"outstanding_route_update_reqs_queued": typeInt,
|
|
||||||
"outstanding_route_get_avg_queue_size": typeInt,
|
|
||||||
"outstanding_route_update_avg_queue_size": typeInt,
|
|
||||||
"outstanding_route_get_avg_wait_time_sec": typeInt,
|
|
||||||
"outstanding_route_update_avg_wait_time_sec": typeInt,
|
|
||||||
"retrans_closed_connections": typeInt,
|
|
||||||
"destination_pending_reqs": typeInt,
|
|
||||||
"destination_inflight_reqs": typeInt,
|
|
||||||
"destination_batch_size": typeInt,
|
|
||||||
"asynclog_requests": typeInt,
|
|
||||||
"proxy_reqs_processing": typeInt,
|
|
||||||
"proxy_reqs_waiting": typeInt,
|
|
||||||
"client_queue_notify_period": typeInt,
|
|
||||||
"rusage_system": typeFloat,
|
|
||||||
"rusage_user": typeFloat,
|
|
||||||
"ps_num_minor_faults": typeInt,
|
|
||||||
"ps_num_major_faults": typeInt,
|
|
||||||
"ps_user_time_sec": typeFloat,
|
|
||||||
"ps_system_time_sec": typeFloat,
|
|
||||||
"ps_vsize": typeInt,
|
|
||||||
"ps_rss": typeInt,
|
|
||||||
"fibers_allocated": typeInt,
|
|
||||||
"fibers_pool_size": typeInt,
|
|
||||||
"fibers_stack_high_watermark": typeInt,
|
|
||||||
"successful_client_connections": typeInt,
|
|
||||||
"duration_us": typeInt,
|
|
||||||
"destination_max_pending_reqs": typeInt,
|
|
||||||
"destination_max_inflight_reqs": typeInt,
|
|
||||||
"retrans_per_kbyte_max": typeInt,
|
|
||||||
"cmd_get_count": typeInt,
|
|
||||||
"cmd_delete_out": typeInt,
|
|
||||||
"cmd_lease_get": typeInt,
|
|
||||||
"cmd_set": typeInt,
|
|
||||||
"cmd_get_out_all": typeInt,
|
|
||||||
"cmd_get_out": typeInt,
|
|
||||||
"cmd_lease_set_count": typeInt,
|
|
||||||
"cmd_other_out_all": typeInt,
|
|
||||||
"cmd_lease_get_out": typeInt,
|
|
||||||
"cmd_set_count": typeInt,
|
|
||||||
"cmd_lease_set_out": typeInt,
|
|
||||||
"cmd_delete_count": typeInt,
|
|
||||||
"cmd_other": typeInt,
|
|
||||||
"cmd_delete": typeInt,
|
|
||||||
"cmd_get": typeInt,
|
|
||||||
"cmd_lease_set": typeInt,
|
|
||||||
"cmd_set_out": typeInt,
|
|
||||||
"cmd_lease_get_count": typeInt,
|
|
||||||
"cmd_other_out": typeInt,
|
|
||||||
"cmd_lease_get_out_all": typeInt,
|
|
||||||
"cmd_set_out_all": typeInt,
|
|
||||||
"cmd_other_count": typeInt,
|
|
||||||
"cmd_delete_out_all": typeInt,
|
|
||||||
"cmd_lease_set_out_all": typeInt,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Mcrouter) SampleConfig() string {
|
func (*Mcrouter) SampleConfig() string {
|
||||||
return sampleConfig
|
return sampleConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather reads stats from all configured servers accumulates stats
|
|
||||||
func (m *Mcrouter) Gather(acc telegraf.Accumulator) error {
|
func (m *Mcrouter) Gather(acc telegraf.Accumulator) error {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
|
@ -136,8 +134,8 @@ func (m *Mcrouter) Gather(acc telegraf.Accumulator) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseAddress parses an address string into 'host:port' and 'protocol' parts
|
// parseAddress parses an address string into 'host:port' and 'protocol' parts
|
||||||
func (m *Mcrouter) ParseAddress(address string) (parsedAddress, protocol string, err error) {
|
func (m *Mcrouter) parseAddress(address string) (parsedAddress, protocol string, err error) {
|
||||||
var host string
|
var host string
|
||||||
var port string
|
var port string
|
||||||
|
|
||||||
|
|
@ -189,7 +187,7 @@ func (m *Mcrouter) gatherServer(ctx context.Context, address string, acc telegra
|
||||||
var protocol string
|
var protocol string
|
||||||
var dialer net.Dialer
|
var dialer net.Dialer
|
||||||
|
|
||||||
address, protocol, err = m.ParseAddress(address)
|
address, protocol, err = m.parseAddress(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ func TestAddressParsing(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, args := range acceptTests {
|
for _, args := range acceptTests {
|
||||||
address, protocol, err := m.ParseAddress(args[0])
|
address, protocol, err := m.parseAddress(args[0])
|
||||||
|
|
||||||
require.NoError(t, err, args[0])
|
require.NoError(t, err, args[0])
|
||||||
require.Equal(t, args[1], address, args[0])
|
require.Equal(t, args[1], address, args[0])
|
||||||
|
|
@ -40,7 +40,7 @@ func TestAddressParsing(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, addr := range rejectTests {
|
for _, addr := range rejectTests {
|
||||||
address, protocol, err := m.ParseAddress(addr)
|
address, protocol, err := m.parseAddress(addr)
|
||||||
|
|
||||||
require.Error(t, err, addr)
|
require.Error(t, err, addr)
|
||||||
require.Empty(t, address, addr)
|
require.Empty(t, address, addr)
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,10 @@ var (
|
||||||
componentDeviceRE = regexp.MustCompile(`(.*)\[\d+\]`)
|
componentDeviceRE = regexp.MustCompile(`(.*)\[\d+\]`)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Mdstat struct {
|
||||||
|
FileName string `toml:"file_name"`
|
||||||
|
}
|
||||||
|
|
||||||
type statusLine struct {
|
type statusLine struct {
|
||||||
active int64
|
active int64
|
||||||
total int64
|
total int64
|
||||||
|
|
@ -58,10 +62,6 @@ type recoveryLine struct {
|
||||||
speed float64
|
speed float64
|
||||||
}
|
}
|
||||||
|
|
||||||
type MdstatConf struct {
|
|
||||||
FileName string `toml:"file_name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalStatusLine(deviceLine, statusLineStr string) (statusLine, error) {
|
func evalStatusLine(deviceLine, statusLineStr string) (statusLine, error) {
|
||||||
sizeFields := strings.Fields(statusLineStr)
|
sizeFields := strings.Fields(statusLineStr)
|
||||||
if len(sizeFields) < 1 {
|
if len(sizeFields) < 1 {
|
||||||
|
|
@ -173,11 +173,11 @@ func evalComponentDevices(deviceFields []string) string {
|
||||||
return strings.Join(mdComponentDevices, ",")
|
return strings.Join(mdComponentDevices, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*MdstatConf) SampleConfig() string {
|
func (*Mdstat) SampleConfig() string {
|
||||||
return sampleConfig
|
return sampleConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *MdstatConf) Gather(acc telegraf.Accumulator) error {
|
func (k *Mdstat) Gather(acc telegraf.Accumulator) error {
|
||||||
data, err := k.getProcMdstat()
|
data, err := k.getProcMdstat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -267,7 +267,7 @@ func (k *MdstatConf) Gather(acc telegraf.Accumulator) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *MdstatConf) getProcMdstat() ([]byte, error) {
|
func (k *Mdstat) getProcMdstat() ([]byte, error) {
|
||||||
var mdStatFile string
|
var mdStatFile string
|
||||||
if k.FileName == "" {
|
if k.FileName == "" {
|
||||||
mdStatFile = internal.GetProcPath() + "/mdstat"
|
mdStatFile = internal.GetProcPath() + "/mdstat"
|
||||||
|
|
@ -289,5 +289,5 @@ func (k *MdstatConf) getProcMdstat() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("mdstat", func() telegraf.Input { return &MdstatConf{} })
|
inputs.Add("mdstat", func() telegraf.Input { return &Mdstat{} })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,14 @@ type Mdstat struct {
|
||||||
Log telegraf.Logger `toml:"-"`
|
Log telegraf.Logger `toml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*Mdstat) SampleConfig() string { return sampleConfig }
|
||||||
|
|
||||||
func (m *Mdstat) Init() error {
|
func (m *Mdstat) Init() error {
|
||||||
m.Log.Warn("current platform is not supported")
|
m.Log.Warn("Current platform is not supported")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (*Mdstat) SampleConfig() string { return sampleConfig }
|
|
||||||
func (*Mdstat) Gather(_ telegraf.Accumulator) error { return nil }
|
func (*Mdstat) Gather(telegraf.Accumulator) error { return nil }
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("mdstat", func() telegraf.Input {
|
inputs.Add("mdstat", func() telegraf.Input {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import (
|
||||||
func TestFullMdstatProcFile(t *testing.T) {
|
func TestFullMdstatProcFile(t *testing.T) {
|
||||||
filename := makeFakeMDStatFile([]byte(mdStatFileFull))
|
filename := makeFakeMDStatFile([]byte(mdStatFileFull))
|
||||||
defer os.Remove(filename)
|
defer os.Remove(filename)
|
||||||
k := MdstatConf{
|
k := Mdstat{
|
||||||
FileName: filename,
|
FileName: filename,
|
||||||
}
|
}
|
||||||
acc := testutil.Accumulator{}
|
acc := testutil.Accumulator{}
|
||||||
|
|
@ -39,7 +39,7 @@ func TestFullMdstatProcFile(t *testing.T) {
|
||||||
func TestMdstatSyncStart(t *testing.T) {
|
func TestMdstatSyncStart(t *testing.T) {
|
||||||
filename := makeFakeMDStatFile([]byte(mdStatSyncStart))
|
filename := makeFakeMDStatFile([]byte(mdStatSyncStart))
|
||||||
defer os.Remove(filename)
|
defer os.Remove(filename)
|
||||||
k := MdstatConf{
|
k := Mdstat{
|
||||||
FileName: filename,
|
FileName: filename,
|
||||||
}
|
}
|
||||||
acc := testutil.Accumulator{}
|
acc := testutil.Accumulator{}
|
||||||
|
|
@ -65,7 +65,7 @@ func TestFailedDiskMdStatProcFile1(t *testing.T) {
|
||||||
filename := makeFakeMDStatFile([]byte(mdStatFileFailedDisk))
|
filename := makeFakeMDStatFile([]byte(mdStatFileFailedDisk))
|
||||||
defer os.Remove(filename)
|
defer os.Remove(filename)
|
||||||
|
|
||||||
k := MdstatConf{
|
k := Mdstat{
|
||||||
FileName: filename,
|
FileName: filename,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,7 +92,7 @@ func TestEmptyMdStatProcFile1(t *testing.T) {
|
||||||
filename := makeFakeMDStatFile([]byte(mdStatFileEmpty))
|
filename := makeFakeMDStatFile([]byte(mdStatFileEmpty))
|
||||||
defer os.Remove(filename)
|
defer os.Remove(filename)
|
||||||
|
|
||||||
k := MdstatConf{
|
k := Mdstat{
|
||||||
FileName: filename,
|
FileName: filename,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,7 +105,7 @@ func TestInvalidMdStatProcFile1(t *testing.T) {
|
||||||
filename := makeFakeMDStatFile([]byte(mdStatFileInvalid))
|
filename := makeFakeMDStatFile([]byte(mdStatFileInvalid))
|
||||||
defer os.Remove(filename)
|
defer os.Remove(filename)
|
||||||
|
|
||||||
k := MdstatConf{
|
k := Mdstat{
|
||||||
FileName: filename,
|
FileName: filename,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,21 +14,21 @@ import (
|
||||||
//go:embed sample.conf
|
//go:embed sample.conf
|
||||||
var sampleConfig string
|
var sampleConfig string
|
||||||
|
|
||||||
type MemStats struct {
|
type Mem struct {
|
||||||
ps system.PS
|
ps system.PS
|
||||||
platform string
|
platform string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*MemStats) SampleConfig() string {
|
func (*Mem) SampleConfig() string {
|
||||||
return sampleConfig
|
return sampleConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *MemStats) Init() error {
|
func (ms *Mem) Init() error {
|
||||||
ms.platform = runtime.GOOS
|
ms.platform = runtime.GOOS
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *MemStats) Gather(acc telegraf.Accumulator) error {
|
func (ms *Mem) Gather(acc telegraf.Accumulator) error {
|
||||||
vm, err := ms.ps.VMStat()
|
vm, err := ms.ps.VMStat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error getting virtual memory info: %w", err)
|
return fmt.Errorf("error getting virtual memory info: %w", err)
|
||||||
|
|
@ -102,6 +102,6 @@ func (ms *MemStats) Gather(acc telegraf.Accumulator) error {
|
||||||
func init() {
|
func init() {
|
||||||
ps := system.NewSystemPS()
|
ps := system.NewSystemPS()
|
||||||
inputs.Add("mem", func() telegraf.Input {
|
inputs.Add("mem", func() telegraf.Input {
|
||||||
return &MemStats{ps: ps}
|
return &Mem{ps: ps}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/shirou/gopsutil/v4/mem"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs/system"
|
"github.com/influxdata/telegraf/plugins/inputs/system"
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
"github.com/shirou/gopsutil/v4/mem"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMemStats(t *testing.T) {
|
func TestMemStats(t *testing.T) {
|
||||||
|
|
@ -55,7 +56,7 @@ func TestMemStats(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
mps.On("VMStat").Return(vms, nil)
|
mps.On("VMStat").Return(vms, nil)
|
||||||
plugin := &MemStats{ps: &mps}
|
plugin := &Mem{ps: &mps}
|
||||||
|
|
||||||
err = plugin.Init()
|
err = plugin.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,82 @@ import (
|
||||||
//go:embed sample.conf
|
//go:embed sample.conf
|
||||||
var sampleConfig string
|
var sampleConfig string
|
||||||
|
|
||||||
// Memcached is a memcached plugin
|
var (
|
||||||
|
defaultTimeout = 5 * time.Second
|
||||||
|
|
||||||
|
// The list of metrics that should be sent
|
||||||
|
sendMetrics = []string{
|
||||||
|
"accepting_conns",
|
||||||
|
"auth_cmds",
|
||||||
|
"auth_errors",
|
||||||
|
"bytes",
|
||||||
|
"bytes_read",
|
||||||
|
"bytes_written",
|
||||||
|
"cas_badval",
|
||||||
|
"cas_hits",
|
||||||
|
"cas_misses",
|
||||||
|
"cmd_flush",
|
||||||
|
"cmd_get",
|
||||||
|
"cmd_set",
|
||||||
|
"cmd_touch",
|
||||||
|
"conn_yields",
|
||||||
|
"connection_structures",
|
||||||
|
"curr_connections",
|
||||||
|
"curr_items",
|
||||||
|
"decr_hits",
|
||||||
|
"decr_misses",
|
||||||
|
"delete_hits",
|
||||||
|
"delete_misses",
|
||||||
|
"evicted_active",
|
||||||
|
"evicted_unfetched",
|
||||||
|
"evictions",
|
||||||
|
"expired_unfetched",
|
||||||
|
"extstore_compact_lost",
|
||||||
|
"extstore_compact_rescues",
|
||||||
|
"extstore_compact_resc_cold",
|
||||||
|
"extstore_compact_resc_old",
|
||||||
|
"extstore_compact_skipped",
|
||||||
|
"extstore_page_allocs",
|
||||||
|
"extstore_page_evictions",
|
||||||
|
"extstore_page_reclaims",
|
||||||
|
"extstore_pages_free",
|
||||||
|
"extstore_pages_used",
|
||||||
|
"extstore_objects_evicted",
|
||||||
|
"extstore_objects_read",
|
||||||
|
"extstore_objects_written",
|
||||||
|
"extstore_objects_used",
|
||||||
|
"extstore_bytes_evicted",
|
||||||
|
"extstore_bytes_written",
|
||||||
|
"extstore_bytes_read",
|
||||||
|
"extstore_bytes_used",
|
||||||
|
"extstore_bytes_fragmented",
|
||||||
|
"extstore_limit_maxbytes",
|
||||||
|
"extstore_io_queue",
|
||||||
|
"get_expired",
|
||||||
|
"get_flushed",
|
||||||
|
"get_hits",
|
||||||
|
"get_misses",
|
||||||
|
"hash_bytes",
|
||||||
|
"hash_is_expanding",
|
||||||
|
"hash_power_level",
|
||||||
|
"incr_hits",
|
||||||
|
"incr_misses",
|
||||||
|
"limit_maxbytes",
|
||||||
|
"listen_disabled_num",
|
||||||
|
"max_connections",
|
||||||
|
"reclaimed",
|
||||||
|
"rejected_connections",
|
||||||
|
"store_no_memory",
|
||||||
|
"store_too_large",
|
||||||
|
"threads",
|
||||||
|
"total_connections",
|
||||||
|
"total_items",
|
||||||
|
"touch_hits",
|
||||||
|
"touch_misses",
|
||||||
|
"uptime",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
type Memcached struct {
|
type Memcached struct {
|
||||||
Servers []string `toml:"servers"`
|
Servers []string `toml:"servers"`
|
||||||
UnixSockets []string `toml:"unix_sockets"`
|
UnixSockets []string `toml:"unix_sockets"`
|
||||||
|
|
@ -30,85 +105,10 @@ type Memcached struct {
|
||||||
common_tls.ClientConfig
|
common_tls.ClientConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultTimeout = 5 * time.Second
|
|
||||||
|
|
||||||
// The list of metrics that should be sent
|
|
||||||
var sendMetrics = []string{
|
|
||||||
"accepting_conns",
|
|
||||||
"auth_cmds",
|
|
||||||
"auth_errors",
|
|
||||||
"bytes",
|
|
||||||
"bytes_read",
|
|
||||||
"bytes_written",
|
|
||||||
"cas_badval",
|
|
||||||
"cas_hits",
|
|
||||||
"cas_misses",
|
|
||||||
"cmd_flush",
|
|
||||||
"cmd_get",
|
|
||||||
"cmd_set",
|
|
||||||
"cmd_touch",
|
|
||||||
"conn_yields",
|
|
||||||
"connection_structures",
|
|
||||||
"curr_connections",
|
|
||||||
"curr_items",
|
|
||||||
"decr_hits",
|
|
||||||
"decr_misses",
|
|
||||||
"delete_hits",
|
|
||||||
"delete_misses",
|
|
||||||
"evicted_active",
|
|
||||||
"evicted_unfetched",
|
|
||||||
"evictions",
|
|
||||||
"expired_unfetched",
|
|
||||||
"extstore_compact_lost",
|
|
||||||
"extstore_compact_rescues",
|
|
||||||
"extstore_compact_resc_cold",
|
|
||||||
"extstore_compact_resc_old",
|
|
||||||
"extstore_compact_skipped",
|
|
||||||
"extstore_page_allocs",
|
|
||||||
"extstore_page_evictions",
|
|
||||||
"extstore_page_reclaims",
|
|
||||||
"extstore_pages_free",
|
|
||||||
"extstore_pages_used",
|
|
||||||
"extstore_objects_evicted",
|
|
||||||
"extstore_objects_read",
|
|
||||||
"extstore_objects_written",
|
|
||||||
"extstore_objects_used",
|
|
||||||
"extstore_bytes_evicted",
|
|
||||||
"extstore_bytes_written",
|
|
||||||
"extstore_bytes_read",
|
|
||||||
"extstore_bytes_used",
|
|
||||||
"extstore_bytes_fragmented",
|
|
||||||
"extstore_limit_maxbytes",
|
|
||||||
"extstore_io_queue",
|
|
||||||
"get_expired",
|
|
||||||
"get_flushed",
|
|
||||||
"get_hits",
|
|
||||||
"get_misses",
|
|
||||||
"hash_bytes",
|
|
||||||
"hash_is_expanding",
|
|
||||||
"hash_power_level",
|
|
||||||
"incr_hits",
|
|
||||||
"incr_misses",
|
|
||||||
"limit_maxbytes",
|
|
||||||
"listen_disabled_num",
|
|
||||||
"max_connections",
|
|
||||||
"reclaimed",
|
|
||||||
"rejected_connections",
|
|
||||||
"store_no_memory",
|
|
||||||
"store_too_large",
|
|
||||||
"threads",
|
|
||||||
"total_connections",
|
|
||||||
"total_items",
|
|
||||||
"touch_hits",
|
|
||||||
"touch_misses",
|
|
||||||
"uptime",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Memcached) SampleConfig() string {
|
func (*Memcached) SampleConfig() string {
|
||||||
return sampleConfig
|
return sampleConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather reads stats from all configured servers accumulates stats
|
|
||||||
func (m *Memcached) Gather(acc telegraf.Accumulator) error {
|
func (m *Memcached) Gather(acc telegraf.Accumulator) error {
|
||||||
if len(m.Servers) == 0 && len(m.UnixSockets) == 0 {
|
if len(m.Servers) == 0 && len(m.UnixSockets) == 0 {
|
||||||
return m.gatherServer(":11211", false, acc)
|
return m.gatherServer(":11211", false, acc)
|
||||||
|
|
@ -125,11 +125,7 @@ func (m *Memcached) Gather(acc telegraf.Accumulator) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Memcached) gatherServer(
|
func (m *Memcached) gatherServer(address string, unix bool, acc telegraf.Accumulator) error {
|
||||||
address string,
|
|
||||||
unix bool,
|
|
||||||
acc telegraf.Accumulator,
|
|
||||||
) error {
|
|
||||||
var conn net.Conn
|
var conn net.Conn
|
||||||
var err error
|
var err error
|
||||||
var dialer proxy.Dialer
|
var dialer proxy.Dialer
|
||||||
|
|
|
||||||
|
|
@ -23,22 +23,27 @@ import (
|
||||||
//go:embed sample.conf
|
//go:embed sample.conf
|
||||||
var sampleConfig string
|
var sampleConfig string
|
||||||
|
|
||||||
type Role string
|
type role string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MASTER Role = "master"
|
master role = "master"
|
||||||
SLAVE Role = "slave"
|
slave role = "slave"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var allMetrics = map[role][]string{
|
||||||
|
master: {"resources", "master", "system", "agents", "frameworks", "framework_offers", "tasks", "messages", "evqueue", "registrar", "allocator"},
|
||||||
|
slave: {"resources", "agent", "system", "executors", "tasks", "messages"},
|
||||||
|
}
|
||||||
|
|
||||||
type Mesos struct {
|
type Mesos struct {
|
||||||
Timeout int
|
Timeout int `toml:"timeout"`
|
||||||
Masters []string
|
Masters []string `toml:"masters"`
|
||||||
MasterCols []string `toml:"master_collections"`
|
MasterCols []string `toml:"master_collections"`
|
||||||
Slaves []string
|
Slaves []string `toml:"slaves"`
|
||||||
SlaveCols []string `toml:"slave_collections"`
|
SlaveCols []string `toml:"slave_collections"`
|
||||||
tls.ClientConfig
|
tls.ClientConfig
|
||||||
|
|
||||||
Log telegraf.Logger
|
Log telegraf.Logger `toml:"-"`
|
||||||
|
|
||||||
initialized bool
|
initialized bool
|
||||||
client *http.Client
|
client *http.Client
|
||||||
|
|
@ -46,21 +51,52 @@ type Mesos struct {
|
||||||
slaveURLs []*url.URL
|
slaveURLs []*url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
var allMetrics = map[Role][]string{
|
func (*Mesos) SampleConfig() string {
|
||||||
MASTER: {"resources", "master", "system", "agents", "frameworks", "framework_offers", "tasks", "messages", "evqueue", "registrar", "allocator"},
|
return sampleConfig
|
||||||
SLAVE: {"resources", "agent", "system", "executors", "tasks", "messages"},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mesos) parseURL(s string, role Role) (*url.URL, error) {
|
func (m *Mesos) Gather(acc telegraf.Accumulator) error {
|
||||||
|
if !m.initialized {
|
||||||
|
err := m.initialize()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
for _, mstr := range m.masterURLs {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(mstr *url.URL) {
|
||||||
|
acc.AddError(m.gatherMainMetrics(mstr, master, acc))
|
||||||
|
wg.Done()
|
||||||
|
}(mstr)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, slv := range m.slaveURLs {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(slv *url.URL) {
|
||||||
|
acc.AddError(m.gatherMainMetrics(slv, slave, acc))
|
||||||
|
wg.Done()
|
||||||
|
}(slv)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Mesos) parseURL(s string, role role) (*url.URL, error) {
|
||||||
if !strings.HasPrefix(s, "http://") && !strings.HasPrefix(s, "https://") {
|
if !strings.HasPrefix(s, "http://") && !strings.HasPrefix(s, "https://") {
|
||||||
host, port, err := net.SplitHostPort(s)
|
host, port, err := net.SplitHostPort(s)
|
||||||
// no port specified
|
// no port specified
|
||||||
if err != nil {
|
if err != nil {
|
||||||
host = s
|
host = s
|
||||||
switch role {
|
switch role {
|
||||||
case MASTER:
|
case master:
|
||||||
port = "5050"
|
port = "5050"
|
||||||
case SLAVE:
|
case slave:
|
||||||
port = "5051"
|
port = "5051"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -74,11 +110,11 @@ func (m *Mesos) parseURL(s string, role Role) (*url.URL, error) {
|
||||||
|
|
||||||
func (m *Mesos) initialize() error {
|
func (m *Mesos) initialize() error {
|
||||||
if len(m.MasterCols) == 0 {
|
if len(m.MasterCols) == 0 {
|
||||||
m.MasterCols = allMetrics[MASTER]
|
m.MasterCols = allMetrics[master]
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(m.SlaveCols) == 0 {
|
if len(m.SlaveCols) == 0 {
|
||||||
m.SlaveCols = allMetrics[SLAVE]
|
m.SlaveCols = allMetrics[slave]
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.Timeout == 0 {
|
if m.Timeout == 0 {
|
||||||
|
|
@ -89,8 +125,8 @@ func (m *Mesos) initialize() error {
|
||||||
rawQuery := "timeout=" + strconv.Itoa(m.Timeout) + "ms"
|
rawQuery := "timeout=" + strconv.Itoa(m.Timeout) + "ms"
|
||||||
|
|
||||||
m.masterURLs = make([]*url.URL, 0, len(m.Masters))
|
m.masterURLs = make([]*url.URL, 0, len(m.Masters))
|
||||||
for _, master := range m.Masters {
|
for _, mstr := range m.Masters {
|
||||||
u, err := m.parseURL(master, MASTER)
|
u, err := m.parseURL(mstr, master)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -100,8 +136,8 @@ func (m *Mesos) initialize() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
m.slaveURLs = make([]*url.URL, 0, len(m.Slaves))
|
m.slaveURLs = make([]*url.URL, 0, len(m.Slaves))
|
||||||
for _, slave := range m.Slaves {
|
for _, slv := range m.Slaves {
|
||||||
u, err := m.parseURL(slave, SLAVE)
|
u, err := m.parseURL(slv, slave)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -119,43 +155,6 @@ func (m *Mesos) initialize() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Mesos) SampleConfig() string {
|
|
||||||
return sampleConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gather() metrics from given list of Mesos Masters
|
|
||||||
func (m *Mesos) Gather(acc telegraf.Accumulator) error {
|
|
||||||
if !m.initialized {
|
|
||||||
err := m.initialize()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m.initialized = true
|
|
||||||
}
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
|
|
||||||
for _, master := range m.masterURLs {
|
|
||||||
wg.Add(1)
|
|
||||||
go func(master *url.URL) {
|
|
||||||
acc.AddError(m.gatherMainMetrics(master, MASTER, acc))
|
|
||||||
wg.Done()
|
|
||||||
}(master)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, slave := range m.slaveURLs {
|
|
||||||
wg.Add(1)
|
|
||||||
go func(slave *url.URL) {
|
|
||||||
acc.AddError(m.gatherMainMetrics(slave, SLAVE, acc))
|
|
||||||
wg.Done()
|
|
||||||
}(slave)
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Mesos) createHTTPClient() (*http.Client, error) {
|
func (m *Mesos) createHTTPClient() (*http.Client, error) {
|
||||||
tlsCfg, err := m.ClientConfig.TLSConfig()
|
tlsCfg, err := m.ClientConfig.TLSConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -174,7 +173,7 @@ func (m *Mesos) createHTTPClient() (*http.Client, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// metricsDiff() returns set names for removal
|
// metricsDiff() returns set names for removal
|
||||||
func metricsDiff(role Role, w []string) []string {
|
func metricsDiff(role role, w []string) []string {
|
||||||
b := make([]string, 0, len(allMetrics[role]))
|
b := make([]string, 0, len(allMetrics[role]))
|
||||||
s := make(map[string]bool)
|
s := make(map[string]bool)
|
||||||
|
|
||||||
|
|
@ -196,10 +195,10 @@ func metricsDiff(role Role, w []string) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// masterBlocks serves as kind of metrics registry grouping them in sets
|
// masterBlocks serves as kind of metrics registry grouping them in sets
|
||||||
func (m *Mesos) getMetrics(role Role, group string) []string {
|
func (m *Mesos) getMetrics(role role, group string) []string {
|
||||||
metrics := make(map[string][]string)
|
metrics := make(map[string][]string)
|
||||||
|
|
||||||
if role == MASTER {
|
if role == master {
|
||||||
metrics["resources"] = []string{
|
metrics["resources"] = []string{
|
||||||
"master/cpus_percent",
|
"master/cpus_percent",
|
||||||
"master/cpus_used",
|
"master/cpus_used",
|
||||||
|
|
@ -356,7 +355,7 @@ func (m *Mesos) getMetrics(role Role, group string) []string {
|
||||||
"registrar/registry_size_bytes",
|
"registrar/registry_size_bytes",
|
||||||
"registrar/state_store_ms/count",
|
"registrar/state_store_ms/count",
|
||||||
}
|
}
|
||||||
} else if role == SLAVE {
|
} else if role == slave {
|
||||||
metrics["resources"] = []string{
|
metrics["resources"] = []string{
|
||||||
"slave/cpus_percent",
|
"slave/cpus_percent",
|
||||||
"slave/cpus_used",
|
"slave/cpus_used",
|
||||||
|
|
@ -430,7 +429,6 @@ func (m *Mesos) getMetrics(role Role, group string) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, ok := metrics[group]
|
ret, ok := metrics[group]
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
m.Log.Infof("Unknown role %q metrics group: %s", role, group)
|
m.Log.Infof("Unknown role %q metrics group: %s", role, group)
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -439,13 +437,13 @@ func (m *Mesos) getMetrics(role Role, group string) []string {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Mesos) filterMetrics(role Role, metrics *map[string]interface{}) {
|
func (m *Mesos) filterMetrics(role role, metrics *map[string]interface{}) {
|
||||||
var ok bool
|
var ok bool
|
||||||
var selectedMetrics []string
|
var selectedMetrics []string
|
||||||
|
|
||||||
if role == MASTER {
|
if role == master {
|
||||||
selectedMetrics = m.MasterCols
|
selectedMetrics = m.MasterCols
|
||||||
} else if role == SLAVE {
|
} else if role == slave {
|
||||||
selectedMetrics = m.SlaveCols
|
selectedMetrics = m.SlaveCols
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -476,13 +474,6 @@ func (m *Mesos) filterMetrics(role Role, metrics *map[string]interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TaskStats struct for JSON API output /monitor/statistics
|
|
||||||
type TaskStats struct {
|
|
||||||
ExecutorID string `json:"executor_id"`
|
|
||||||
FrameworkID string `json:"framework_id"`
|
|
||||||
Statistics map[string]interface{} `json:"statistics"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func withPath(u *url.URL, path string) *url.URL {
|
func withPath(u *url.URL, path string) *url.URL {
|
||||||
c := *u
|
c := *u
|
||||||
c.Path = path
|
c.Path = path
|
||||||
|
|
@ -498,7 +489,7 @@ func urlTag(u *url.URL) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should not belong to the object
|
// This should not belong to the object
|
||||||
func (m *Mesos) gatherMainMetrics(u *url.URL, role Role, acc telegraf.Accumulator) error {
|
func (m *Mesos) gatherMainMetrics(u *url.URL, role role, acc telegraf.Accumulator) error {
|
||||||
var jsonOut map[string]interface{}
|
var jsonOut map[string]interface{}
|
||||||
|
|
||||||
tags := map[string]string{
|
tags := map[string]string{
|
||||||
|
|
@ -533,7 +524,7 @@ func (m *Mesos) gatherMainMetrics(u *url.URL, role Role, acc telegraf.Accumulato
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if role == MASTER {
|
if role == master {
|
||||||
if jf.Fields["master/elected"] != 0.0 {
|
if jf.Fields["master/elected"] != 0.0 {
|
||||||
tags["state"] = "leader"
|
tags["state"] = "leader"
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -333,11 +333,11 @@ func TestMasterFilter(t *testing.T) {
|
||||||
"messages", "evqueue", "tasks",
|
"messages", "evqueue", "tasks",
|
||||||
}
|
}
|
||||||
|
|
||||||
m.filterMetrics(MASTER, &masterMetrics)
|
m.filterMetrics(master, &masterMetrics)
|
||||||
|
|
||||||
// Assert expected metrics are present.
|
// Assert expected metrics are present.
|
||||||
for _, v := range m.MasterCols {
|
for _, v := range m.MasterCols {
|
||||||
for _, x := range m.getMetrics(MASTER, v) {
|
for _, x := range m.getMetrics(master, v) {
|
||||||
_, ok := masterMetrics[x]
|
_, ok := masterMetrics[x]
|
||||||
require.Truef(t, ok, "Didn't find key %s, it should present.", x)
|
require.Truef(t, ok, "Didn't find key %s, it should present.", x)
|
||||||
}
|
}
|
||||||
|
|
@ -354,7 +354,7 @@ func TestMasterFilter(t *testing.T) {
|
||||||
|
|
||||||
// Assert unexpected metrics are not present.
|
// Assert unexpected metrics are not present.
|
||||||
for _, v := range b {
|
for _, v := range b {
|
||||||
for _, x := range m.getMetrics(MASTER, v) {
|
for _, x := range m.getMetrics(master, v) {
|
||||||
_, ok := masterMetrics[x]
|
_, ok := masterMetrics[x]
|
||||||
require.Falsef(t, ok, "Found key %s, it should be gone.", x)
|
require.Falsef(t, ok, "Found key %s, it should be gone.", x)
|
||||||
}
|
}
|
||||||
|
|
@ -395,16 +395,16 @@ func TestSlaveFilter(t *testing.T) {
|
||||||
"system", "executors", "messages",
|
"system", "executors", "messages",
|
||||||
}
|
}
|
||||||
|
|
||||||
m.filterMetrics(SLAVE, &slaveMetrics)
|
m.filterMetrics(slave, &slaveMetrics)
|
||||||
|
|
||||||
for _, v := range b {
|
for _, v := range b {
|
||||||
for _, x := range m.getMetrics(SLAVE, v) {
|
for _, x := range m.getMetrics(slave, v) {
|
||||||
_, ok := slaveMetrics[x]
|
_, ok := slaveMetrics[x]
|
||||||
require.Falsef(t, ok, "Found key %s, it should be gone.", x)
|
require.Falsef(t, ok, "Found key %s, it should be gone.", x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, v := range m.MasterCols {
|
for _, v := range m.MasterCols {
|
||||||
for _, x := range m.getMetrics(SLAVE, v) {
|
for _, x := range m.getMetrics(slave, v) {
|
||||||
_, ok := slaveMetrics[x]
|
_, ok := slaveMetrics[x]
|
||||||
require.Truef(t, ok, "Didn't find key %s, it should present.", x)
|
require.Truef(t, ok, "Didn't find key %s, it should present.", x)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,16 @@ var (
|
||||||
scoreboardRegex = regexp.MustCompile(`\[(?P<name>[^\]]+)\]: (?P<value>\d+)`)
|
scoreboardRegex = regexp.MustCompile(`\[(?P<name>[^\]]+)\]: (?P<value>\d+)`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Connection is an established connection to the Minecraft server.
|
// connection is an established connection to the Minecraft server.
|
||||||
type Connection interface {
|
type connection interface {
|
||||||
// Execute runs a command.
|
// Execute runs a command.
|
||||||
Execute(command string) (string, error)
|
Execute(command string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connector is used to create connections to the Minecraft server.
|
// conn is used to create connections to the Minecraft server.
|
||||||
type Connector interface {
|
type conn interface {
|
||||||
// Connect establishes a connection to the server.
|
// connect establishes a connection to the server.
|
||||||
Connect() (Connection, error)
|
connect() (connection, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newConnector(hostname, port, password string) *connector {
|
func newConnector(hostname, port, password string) *connector {
|
||||||
|
|
@ -39,7 +39,7 @@ type connector struct {
|
||||||
password string
|
password string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *connector) Connect() (Connection, error) {
|
func (c *connector) connect() (connection, error) {
|
||||||
client, err := rcon.Dial(c.hostname+":"+c.port, c.password)
|
client, err := rcon.Dial(c.hostname+":"+c.port, c.password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -48,17 +48,17 @@ func (c *connector) Connect() (Connection, error) {
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newClient(connector Connector) *client {
|
func newClient(connector conn) *client {
|
||||||
return &client{connector: connector}
|
return &client{connector: connector}
|
||||||
}
|
}
|
||||||
|
|
||||||
type client struct {
|
type client struct {
|
||||||
connector Connector
|
connector conn
|
||||||
conn Connection
|
conn connection
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) Connect() error {
|
func (c *client) connect() error {
|
||||||
conn, err := c.connector.Connect()
|
conn, err := c.connector.connect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -66,9 +66,9 @@ func (c *client) Connect() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) Players() ([]string, error) {
|
func (c *client) players() ([]string, error) {
|
||||||
if c.conn == nil {
|
if c.conn == nil {
|
||||||
err := c.Connect()
|
err := c.connect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -83,9 +83,9 @@ func (c *client) Players() ([]string, error) {
|
||||||
return parsePlayers(resp), nil
|
return parsePlayers(resp), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) Scores(player string) ([]Score, error) {
|
func (c *client) scores(player string) ([]score, error) {
|
||||||
if c.conn == nil {
|
if c.conn == nil {
|
||||||
err := c.Connect()
|
err := c.connect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -127,13 +127,13 @@ func parsePlayers(input string) []string {
|
||||||
return players
|
return players
|
||||||
}
|
}
|
||||||
|
|
||||||
// Score is an individual tracked scoreboard stat.
|
// score is an individual tracked scoreboard stat.
|
||||||
type Score struct {
|
type score struct {
|
||||||
Name string
|
name string
|
||||||
Value int64
|
value int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseScores(input string) []Score {
|
func parseScores(input string) []score {
|
||||||
if strings.Contains(input, "has no scores") {
|
if strings.Contains(input, "has no scores") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -147,19 +147,19 @@ func parseScores(input string) []Score {
|
||||||
}
|
}
|
||||||
|
|
||||||
matches := re.FindAllStringSubmatch(input, -1)
|
matches := re.FindAllStringSubmatch(input, -1)
|
||||||
scores := make([]Score, 0, len(matches))
|
scores := make([]score, 0, len(matches))
|
||||||
for _, match := range matches {
|
for _, match := range matches {
|
||||||
score := Score{}
|
score := score{}
|
||||||
for i, subexp := range re.SubexpNames() {
|
for i, subexp := range re.SubexpNames() {
|
||||||
switch subexp {
|
switch subexp {
|
||||||
case "name":
|
case "name":
|
||||||
score.Name = match[i]
|
score.name = match[i]
|
||||||
case "value":
|
case "value":
|
||||||
value, err := strconv.ParseInt(match[i], 10, 64)
|
value, err := strconv.ParseInt(match[i], 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
score.Value = value
|
score.value = value
|
||||||
default:
|
default:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,19 +6,19 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MockConnection struct {
|
type mockConnection struct {
|
||||||
commands map[string]string
|
commands map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MockConnection) Execute(command string) (string, error) {
|
func (c *mockConnection) Execute(command string) (string, error) {
|
||||||
return c.commands[command], nil
|
return c.commands[command], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type MockConnector struct {
|
type mockConnector struct {
|
||||||
conn *MockConnection
|
conn *mockConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MockConnector) Connect() (Connection, error) {
|
func (c *mockConnector) connect() (connection, error) {
|
||||||
return c.conn, nil
|
return c.conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,12 +92,12 @@ func TestClient_Player(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
connector := &MockConnector{
|
connector := &mockConnector{
|
||||||
conn: &MockConnection{commands: tt.commands},
|
conn: &mockConnection{commands: tt.commands},
|
||||||
}
|
}
|
||||||
|
|
||||||
client := newClient(connector)
|
client := newClient(connector)
|
||||||
actual, err := client.Players()
|
actual, err := client.players()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, tt.expected, actual)
|
require.Equal(t, tt.expected, actual)
|
||||||
|
|
@ -110,7 +110,7 @@ func TestClient_Scores(t *testing.T) {
|
||||||
name string
|
name string
|
||||||
player string
|
player string
|
||||||
commands map[string]string
|
commands map[string]string
|
||||||
expected []Score
|
expected []score
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "minecraft 1.12 player with no scores",
|
name: "minecraft 1.12 player with no scores",
|
||||||
|
|
@ -125,8 +125,8 @@ func TestClient_Scores(t *testing.T) {
|
||||||
commands: map[string]string{
|
commands: map[string]string{
|
||||||
"scoreboard players list Etho": "Showing 1 tracked objective(s) for Etho:- jump: 2 (jump)",
|
"scoreboard players list Etho": "Showing 1 tracked objective(s) for Etho:- jump: 2 (jump)",
|
||||||
},
|
},
|
||||||
expected: []Score{
|
expected: []score{
|
||||||
{Name: "jump", Value: 2},
|
{name: "jump", value: 2},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -135,10 +135,10 @@ func TestClient_Scores(t *testing.T) {
|
||||||
commands: map[string]string{
|
commands: map[string]string{
|
||||||
"scoreboard players list Etho": "Showing 3 tracked objective(s) for Etho:- hopper: 2 (hopper)- dropper: 2 (dropper)- redstone: 1 (redstone)",
|
"scoreboard players list Etho": "Showing 3 tracked objective(s) for Etho:- hopper: 2 (hopper)- dropper: 2 (dropper)- redstone: 1 (redstone)",
|
||||||
},
|
},
|
||||||
expected: []Score{
|
expected: []score{
|
||||||
{Name: "hopper", Value: 2},
|
{name: "hopper", value: 2},
|
||||||
{Name: "dropper", Value: 2},
|
{name: "dropper", value: 2},
|
||||||
{Name: "redstone", Value: 1},
|
{name: "redstone", value: 1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -154,8 +154,8 @@ func TestClient_Scores(t *testing.T) {
|
||||||
commands: map[string]string{
|
commands: map[string]string{
|
||||||
"scoreboard players list Etho": "Etho has 1 scores:[jumps]: 1",
|
"scoreboard players list Etho": "Etho has 1 scores:[jumps]: 1",
|
||||||
},
|
},
|
||||||
expected: []Score{
|
expected: []score{
|
||||||
{Name: "jumps", Value: 1},
|
{name: "jumps", value: 1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -164,21 +164,21 @@ func TestClient_Scores(t *testing.T) {
|
||||||
commands: map[string]string{
|
commands: map[string]string{
|
||||||
"scoreboard players list Etho": "Etho has 3 scores:[hopper]: 2[dropper]: 2[redstone]: 1",
|
"scoreboard players list Etho": "Etho has 3 scores:[hopper]: 2[dropper]: 2[redstone]: 1",
|
||||||
},
|
},
|
||||||
expected: []Score{
|
expected: []score{
|
||||||
{Name: "hopper", Value: 2},
|
{name: "hopper", value: 2},
|
||||||
{Name: "dropper", Value: 2},
|
{name: "dropper", value: 2},
|
||||||
{Name: "redstone", Value: 1},
|
{name: "redstone", value: 1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
connector := &MockConnector{
|
connector := &mockConnector{
|
||||||
conn: &MockConnection{commands: tt.commands},
|
conn: &mockConnection{commands: tt.commands},
|
||||||
}
|
}
|
||||||
|
|
||||||
client := newClient(connector)
|
client := newClient(connector)
|
||||||
actual, err := client.Scores(tt.player)
|
actual, err := client.scores(tt.player)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, tt.expected, actual)
|
require.Equal(t, tt.expected, actual)
|
||||||
|
|
|
||||||
|
|
@ -11,25 +11,24 @@ import (
|
||||||
//go:embed sample.conf
|
//go:embed sample.conf
|
||||||
var sampleConfig string
|
var sampleConfig string
|
||||||
|
|
||||||
// Client is a client for the Minecraft server.
|
|
||||||
type Client interface {
|
|
||||||
// Connect establishes a connection to the server.
|
|
||||||
Connect() error
|
|
||||||
|
|
||||||
// Players returns the players on the scoreboard.
|
|
||||||
Players() ([]string, error)
|
|
||||||
|
|
||||||
// Scores return the objective scores for a player.
|
|
||||||
Scores(player string) ([]Score, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Minecraft is the plugin type.
|
|
||||||
type Minecraft struct {
|
type Minecraft struct {
|
||||||
Server string `toml:"server"`
|
Server string `toml:"server"`
|
||||||
Port string `toml:"port"`
|
Port string `toml:"port"`
|
||||||
Password string `toml:"password"`
|
Password string `toml:"password"`
|
||||||
|
|
||||||
client Client
|
client cli
|
||||||
|
}
|
||||||
|
|
||||||
|
// cli is a client for the Minecraft server.
|
||||||
|
type cli interface {
|
||||||
|
// connect establishes a connection to the server.
|
||||||
|
connect() error
|
||||||
|
|
||||||
|
// players returns the players on the scoreboard.
|
||||||
|
players() ([]string, error)
|
||||||
|
|
||||||
|
// scores returns the objective scores for a player.
|
||||||
|
scores(player string) ([]score, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Minecraft) SampleConfig() string {
|
func (*Minecraft) SampleConfig() string {
|
||||||
|
|
@ -42,13 +41,13 @@ func (s *Minecraft) Gather(acc telegraf.Accumulator) error {
|
||||||
s.client = newClient(connector)
|
s.client = newClient(connector)
|
||||||
}
|
}
|
||||||
|
|
||||||
players, err := s.client.Players()
|
players, err := s.client.players()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, player := range players {
|
for _, player := range players {
|
||||||
scores, err := s.client.Scores(player)
|
scores, err := s.client.scores(player)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -62,7 +61,7 @@ func (s *Minecraft) Gather(acc telegraf.Accumulator) error {
|
||||||
|
|
||||||
var fields = make(map[string]interface{}, len(scores))
|
var fields = make(map[string]interface{}, len(scores))
|
||||||
for _, score := range scores {
|
for _, score := range scores {
|
||||||
fields[score.Name] = score.Value
|
fields[score.name] = score.value
|
||||||
}
|
}
|
||||||
|
|
||||||
acc.AddFields("minecraft", fields, tags)
|
acc.AddFields("minecraft", fields, tags)
|
||||||
|
|
|
||||||
|
|
@ -9,22 +9,22 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MockClient struct {
|
type mockClient struct {
|
||||||
ConnectF func() error
|
connectF func() error
|
||||||
PlayersF func() ([]string, error)
|
playersF func() ([]string, error)
|
||||||
ScoresF func(player string) ([]Score, error)
|
scoresF func(player string) ([]score, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MockClient) Connect() error {
|
func (c *mockClient) connect() error {
|
||||||
return c.ConnectF()
|
return c.connectF()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MockClient) Players() ([]string, error) {
|
func (c *mockClient) players() ([]string, error) {
|
||||||
return c.PlayersF()
|
return c.playersF()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MockClient) Scores(player string) ([]Score, error) {
|
func (c *mockClient) scores(player string) ([]score, error) {
|
||||||
return c.ScoresF(player)
|
return c.scoresF(player)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather(t *testing.T) {
|
func TestGather(t *testing.T) {
|
||||||
|
|
@ -32,31 +32,31 @@ func TestGather(t *testing.T) {
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
client *MockClient
|
client *mockClient
|
||||||
metrics []telegraf.Metric
|
metrics []telegraf.Metric
|
||||||
err error
|
err error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no players",
|
name: "no players",
|
||||||
client: &MockClient{
|
client: &mockClient{
|
||||||
ConnectF: func() error {
|
connectF: func() error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
PlayersF: func() ([]string, error) {
|
playersF: func() ([]string, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "one player without scores",
|
name: "one player without scores",
|
||||||
client: &MockClient{
|
client: &mockClient{
|
||||||
ConnectF: func() error {
|
connectF: func() error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
PlayersF: func() ([]string, error) {
|
playersF: func() ([]string, error) {
|
||||||
return []string{"Etho"}, nil
|
return []string{"Etho"}, nil
|
||||||
},
|
},
|
||||||
ScoresF: func(player string) ([]Score, error) {
|
scoresF: func(player string) ([]score, error) {
|
||||||
switch player {
|
switch player {
|
||||||
case "Etho":
|
case "Etho":
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
@ -68,17 +68,17 @@ func TestGather(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "one player with scores",
|
name: "one player with scores",
|
||||||
client: &MockClient{
|
client: &mockClient{
|
||||||
ConnectF: func() error {
|
connectF: func() error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
PlayersF: func() ([]string, error) {
|
playersF: func() ([]string, error) {
|
||||||
return []string{"Etho"}, nil
|
return []string{"Etho"}, nil
|
||||||
},
|
},
|
||||||
ScoresF: func(player string) ([]Score, error) {
|
scoresF: func(player string) ([]score, error) {
|
||||||
switch player {
|
switch player {
|
||||||
case "Etho":
|
case "Etho":
|
||||||
return []Score{{Name: "jumps", Value: 42}}, nil
|
return []score{{name: "jumps", value: 42}}, nil
|
||||||
default:
|
default:
|
||||||
panic("unknown player")
|
panic("unknown player")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,22 +48,22 @@ type sineWave struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type step struct {
|
type step struct {
|
||||||
latest float64
|
|
||||||
|
|
||||||
Name string `toml:"name"`
|
Name string `toml:"name"`
|
||||||
Start float64 `toml:"start"`
|
Start float64 `toml:"start"`
|
||||||
Step float64 `toml:"step"`
|
Step float64 `toml:"step"`
|
||||||
|
|
||||||
Min float64 `toml:"min" deprecated:"1.28.2;1.35.0;use 'start' instead"`
|
Min float64 `toml:"min" deprecated:"1.28.2;1.35.0;use 'start' instead"`
|
||||||
Max float64 `toml:"max" deprecated:"1.28.2;1.35.0;use 'step' instead"`
|
Max float64 `toml:"max" deprecated:"1.28.2;1.35.0;use 'step' instead"`
|
||||||
|
|
||||||
|
latest float64
|
||||||
}
|
}
|
||||||
|
|
||||||
type stock struct {
|
type stock struct {
|
||||||
latest float64
|
|
||||||
|
|
||||||
Name string `toml:"name"`
|
Name string `toml:"name"`
|
||||||
Price float64 `toml:"price"`
|
Price float64 `toml:"price"`
|
||||||
Volatility float64 `toml:"volatility"`
|
Volatility float64 `toml:"volatility"`
|
||||||
|
|
||||||
|
latest float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Mock) SampleConfig() string {
|
func (*Mock) SampleConfig() string {
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@ const (
|
||||||
maxQuantityHoldingRegisters = uint16(125)
|
maxQuantityHoldingRegisters = uint16(125)
|
||||||
)
|
)
|
||||||
|
|
||||||
type Configuration interface {
|
type configuration interface {
|
||||||
Check() error
|
check() error
|
||||||
Process() (map[byte]requestSet, error)
|
process() (map[byte]requestSet, error)
|
||||||
SampleConfigPart() string
|
sampleConfigPart() string
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeDuplicates(elements []uint16) []uint16 {
|
func removeDuplicates(elements []uint16) []uint16 {
|
||||||
|
|
|
||||||
|
|
@ -32,21 +32,21 @@ type metricDefinition struct {
|
||||||
Tags map[string]string `toml:"tags"`
|
Tags map[string]string `toml:"tags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigurationPerMetric struct {
|
type configurationPerMetric struct {
|
||||||
Optimization string `toml:"optimization"`
|
Optimization string `toml:"optimization"`
|
||||||
MaxExtraRegisters uint16 `toml:"optimization_max_register_fill"`
|
MaxExtraRegisters uint16 `toml:"optimization_max_register_fill"`
|
||||||
Metrics []metricDefinition `toml:"metric"`
|
Metrics []metricDefinition `toml:"metric"`
|
||||||
|
|
||||||
workarounds ModbusWorkarounds
|
workarounds workarounds
|
||||||
excludeRegisterType bool
|
excludeRegisterType bool
|
||||||
logger telegraf.Logger
|
logger telegraf.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationPerMetric) SampleConfigPart() string {
|
func (c *configurationPerMetric) sampleConfigPart() string {
|
||||||
return sampleConfigPartPerMetric
|
return sampleConfigPartPerMetric
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationPerMetric) Check() error {
|
func (c *configurationPerMetric) check() error {
|
||||||
switch c.workarounds.StringRegisterLocation {
|
switch c.workarounds.StringRegisterLocation {
|
||||||
case "", "both", "lower", "upper":
|
case "", "both", "lower", "upper":
|
||||||
// Do nothing as those are valid
|
// Do nothing as those are valid
|
||||||
|
|
@ -178,7 +178,7 @@ func (c *ConfigurationPerMetric) Check() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationPerMetric) Process() (map[byte]requestSet, error) {
|
func (c *configurationPerMetric) process() (map[byte]requestSet, error) {
|
||||||
collection := make(map[byte]map[string][]field)
|
collection := make(map[byte]map[string][]field)
|
||||||
|
|
||||||
// Collect the requested registers across metrics and transform them into
|
// Collect the requested registers across metrics and transform them into
|
||||||
|
|
@ -206,40 +206,40 @@ func (c *ConfigurationPerMetric) Process() (map[byte]requestSet, error) {
|
||||||
result := make(map[byte]requestSet)
|
result := make(map[byte]requestSet)
|
||||||
|
|
||||||
params := groupingParams{
|
params := groupingParams{
|
||||||
Optimization: c.Optimization,
|
optimization: c.Optimization,
|
||||||
MaxExtraRegisters: c.MaxExtraRegisters,
|
maxExtraRegisters: c.MaxExtraRegisters,
|
||||||
Log: c.logger,
|
log: c.logger,
|
||||||
}
|
}
|
||||||
for sid, scollection := range collection {
|
for sid, scollection := range collection {
|
||||||
var set requestSet
|
var set requestSet
|
||||||
for registerType, fields := range scollection {
|
for registerType, fields := range scollection {
|
||||||
switch registerType {
|
switch registerType {
|
||||||
case "coil":
|
case "coil":
|
||||||
params.MaxBatchSize = maxQuantityCoils
|
params.maxBatchSize = maxQuantityCoils
|
||||||
if c.workarounds.OnRequestPerField {
|
if c.workarounds.OnRequestPerField {
|
||||||
params.MaxBatchSize = 1
|
params.maxBatchSize = 1
|
||||||
}
|
}
|
||||||
params.EnforceFromZero = c.workarounds.ReadCoilsStartingAtZero
|
params.enforceFromZero = c.workarounds.ReadCoilsStartingAtZero
|
||||||
requests := groupFieldsToRequests(fields, params)
|
requests := groupFieldsToRequests(fields, params)
|
||||||
set.coil = append(set.coil, requests...)
|
set.coil = append(set.coil, requests...)
|
||||||
case "discrete":
|
case "discrete":
|
||||||
params.MaxBatchSize = maxQuantityDiscreteInput
|
params.maxBatchSize = maxQuantityDiscreteInput
|
||||||
if c.workarounds.OnRequestPerField {
|
if c.workarounds.OnRequestPerField {
|
||||||
params.MaxBatchSize = 1
|
params.maxBatchSize = 1
|
||||||
}
|
}
|
||||||
requests := groupFieldsToRequests(fields, params)
|
requests := groupFieldsToRequests(fields, params)
|
||||||
set.discrete = append(set.discrete, requests...)
|
set.discrete = append(set.discrete, requests...)
|
||||||
case "holding":
|
case "holding":
|
||||||
params.MaxBatchSize = maxQuantityHoldingRegisters
|
params.maxBatchSize = maxQuantityHoldingRegisters
|
||||||
if c.workarounds.OnRequestPerField {
|
if c.workarounds.OnRequestPerField {
|
||||||
params.MaxBatchSize = 1
|
params.maxBatchSize = 1
|
||||||
}
|
}
|
||||||
requests := groupFieldsToRequests(fields, params)
|
requests := groupFieldsToRequests(fields, params)
|
||||||
set.holding = append(set.holding, requests...)
|
set.holding = append(set.holding, requests...)
|
||||||
case "input":
|
case "input":
|
||||||
params.MaxBatchSize = maxQuantityInputRegisters
|
params.maxBatchSize = maxQuantityInputRegisters
|
||||||
if c.workarounds.OnRequestPerField {
|
if c.workarounds.OnRequestPerField {
|
||||||
params.MaxBatchSize = 1
|
params.maxBatchSize = 1
|
||||||
}
|
}
|
||||||
requests := groupFieldsToRequests(fields, params)
|
requests := groupFieldsToRequests(fields, params)
|
||||||
set.input = append(set.input, requests...)
|
set.input = append(set.input, requests...)
|
||||||
|
|
@ -247,7 +247,7 @@ func (c *ConfigurationPerMetric) Process() (map[byte]requestSet, error) {
|
||||||
return nil, fmt.Errorf("unknown register type %q", registerType)
|
return nil, fmt.Errorf("unknown register type %q", registerType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !set.Empty() {
|
if !set.empty() {
|
||||||
result[sid] = set
|
result[sid] = set
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -255,7 +255,7 @@ func (c *ConfigurationPerMetric) Process() (map[byte]requestSet, error) {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationPerMetric) newField(def metricFieldDefinition, mdef metricDefinition) (field, error) {
|
func (c *configurationPerMetric) newField(def metricFieldDefinition, mdef metricDefinition) (field, error) {
|
||||||
typed := def.RegisterType == "holding" || def.RegisterType == "input"
|
typed := def.RegisterType == "holding" || def.RegisterType == "input"
|
||||||
|
|
||||||
fieldLength := uint16(1)
|
fieldLength := uint16(1)
|
||||||
|
|
@ -339,7 +339,7 @@ func (c *ConfigurationPerMetric) newField(def metricFieldDefinition, mdef metric
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationPerMetric) fieldID(seed maphash.Seed, def metricDefinition, field metricFieldDefinition) uint64 {
|
func (c *configurationPerMetric) fieldID(seed maphash.Seed, def metricDefinition, field metricFieldDefinition) uint64 {
|
||||||
var mh maphash.Hash
|
var mh maphash.Hash
|
||||||
mh.SetSeed(seed)
|
mh.SetSeed(seed)
|
||||||
|
|
||||||
|
|
@ -354,7 +354,7 @@ func (c *ConfigurationPerMetric) fieldID(seed maphash.Seed, def metricDefinition
|
||||||
mh.WriteString(field.Name)
|
mh.WriteString(field.Name)
|
||||||
mh.WriteByte(0)
|
mh.WriteByte(0)
|
||||||
|
|
||||||
// Tags
|
// tags
|
||||||
for k, v := range def.Tags {
|
for k, v := range def.Tags {
|
||||||
mh.WriteString(k)
|
mh.WriteString(k)
|
||||||
mh.WriteByte('=')
|
mh.WriteByte('=')
|
||||||
|
|
@ -366,7 +366,7 @@ func (c *ConfigurationPerMetric) fieldID(seed maphash.Seed, def metricDefinition
|
||||||
return mh.Sum64()
|
return mh.Sum64()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationPerMetric) determineOutputDatatype(input string) (string, error) {
|
func (c *configurationPerMetric) determineOutputDatatype(input string) (string, error) {
|
||||||
// Handle our special types
|
// Handle our special types
|
||||||
switch input {
|
switch input {
|
||||||
case "INT8L", "INT8H", "INT16", "INT32", "INT64":
|
case "INT8L", "INT8H", "INT16", "INT32", "INT64":
|
||||||
|
|
@ -381,7 +381,7 @@ func (c *ConfigurationPerMetric) determineOutputDatatype(input string) (string,
|
||||||
return "unknown", fmt.Errorf("invalid input datatype %q for determining output", input)
|
return "unknown", fmt.Errorf("invalid input datatype %q for determining output", input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationPerMetric) determineFieldLength(input string, length uint16) (uint16, error) {
|
func (c *configurationPerMetric) determineFieldLength(input string, length uint16) (uint16, error) {
|
||||||
// Handle our special types
|
// Handle our special types
|
||||||
switch input {
|
switch input {
|
||||||
case "BIT", "INT8L", "INT8H", "UINT8L", "UINT8H":
|
case "BIT", "INT8L", "INT8H", "UINT8L", "UINT8H":
|
||||||
|
|
|
||||||
|
|
@ -371,7 +371,7 @@ func TestMetricAddressOverflow(t *testing.T) {
|
||||||
Controller: "tcp://localhost:1502",
|
Controller: "tcp://localhost:1502",
|
||||||
ConfigurationType: "metric",
|
ConfigurationType: "metric",
|
||||||
Log: logger,
|
Log: logger,
|
||||||
Workarounds: ModbusWorkarounds{ReadCoilsStartingAtZero: true},
|
Workarounds: workarounds{ReadCoilsStartingAtZero: true},
|
||||||
}
|
}
|
||||||
plugin.Metrics = []metricDefinition{
|
plugin.Metrics = []metricDefinition{
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -21,21 +21,21 @@ type fieldDefinition struct {
|
||||||
Bit uint8 `toml:"bit"`
|
Bit uint8 `toml:"bit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigurationOriginal struct {
|
type configurationOriginal struct {
|
||||||
SlaveID byte `toml:"slave_id"`
|
SlaveID byte `toml:"slave_id"`
|
||||||
DiscreteInputs []fieldDefinition `toml:"discrete_inputs"`
|
DiscreteInputs []fieldDefinition `toml:"discrete_inputs"`
|
||||||
Coils []fieldDefinition `toml:"coils"`
|
Coils []fieldDefinition `toml:"coils"`
|
||||||
HoldingRegisters []fieldDefinition `toml:"holding_registers"`
|
HoldingRegisters []fieldDefinition `toml:"holding_registers"`
|
||||||
InputRegisters []fieldDefinition `toml:"input_registers"`
|
InputRegisters []fieldDefinition `toml:"input_registers"`
|
||||||
workarounds ModbusWorkarounds
|
workarounds workarounds
|
||||||
logger telegraf.Logger
|
logger telegraf.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationOriginal) SampleConfigPart() string {
|
func (c *configurationOriginal) sampleConfigPart() string {
|
||||||
return sampleConfigPartPerRegister
|
return sampleConfigPartPerRegister
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationOriginal) Check() error {
|
func (c *configurationOriginal) check() error {
|
||||||
switch c.workarounds.StringRegisterLocation {
|
switch c.workarounds.StringRegisterLocation {
|
||||||
case "", "both", "lower", "upper":
|
case "", "both", "lower", "upper":
|
||||||
// Do nothing as those are valid
|
// Do nothing as those are valid
|
||||||
|
|
@ -58,7 +58,7 @@ func (c *ConfigurationOriginal) Check() error {
|
||||||
return c.validateFieldDefinitions(c.InputRegisters, cInputRegisters)
|
return c.validateFieldDefinitions(c.InputRegisters, cInputRegisters)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationOriginal) Process() (map[byte]requestSet, error) {
|
func (c *configurationOriginal) process() (map[byte]requestSet, error) {
|
||||||
maxQuantity := uint16(1)
|
maxQuantity := uint16(1)
|
||||||
if !c.workarounds.OnRequestPerField {
|
if !c.workarounds.OnRequestPerField {
|
||||||
maxQuantity = maxQuantityCoils
|
maxQuantity = maxQuantityCoils
|
||||||
|
|
@ -102,22 +102,22 @@ func (c *ConfigurationOriginal) Process() (map[byte]requestSet, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationOriginal) initRequests(fieldDefs []fieldDefinition, maxQuantity uint16, typed bool) ([]request, error) {
|
func (c *configurationOriginal) initRequests(fieldDefs []fieldDefinition, maxQuantity uint16, typed bool) ([]request, error) {
|
||||||
fields, err := c.initFields(fieldDefs, typed)
|
fields, err := c.initFields(fieldDefs, typed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
params := groupingParams{
|
params := groupingParams{
|
||||||
MaxBatchSize: maxQuantity,
|
maxBatchSize: maxQuantity,
|
||||||
Optimization: "none",
|
optimization: "none",
|
||||||
EnforceFromZero: c.workarounds.ReadCoilsStartingAtZero,
|
enforceFromZero: c.workarounds.ReadCoilsStartingAtZero,
|
||||||
Log: c.logger,
|
log: c.logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
return groupFieldsToRequests(fields, params), nil
|
return groupFieldsToRequests(fields, params), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationOriginal) initFields(fieldDefs []fieldDefinition, typed bool) ([]field, error) {
|
func (c *configurationOriginal) initFields(fieldDefs []fieldDefinition, typed bool) ([]field, error) {
|
||||||
// Construct the fields from the field definitions
|
// Construct the fields from the field definitions
|
||||||
fields := make([]field, 0, len(fieldDefs))
|
fields := make([]field, 0, len(fieldDefs))
|
||||||
for _, def := range fieldDefs {
|
for _, def := range fieldDefs {
|
||||||
|
|
@ -131,7 +131,7 @@ func (c *ConfigurationOriginal) initFields(fieldDefs []fieldDefinition, typed bo
|
||||||
return fields, nil
|
return fields, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationOriginal) newFieldFromDefinition(def fieldDefinition, typed bool) (field, error) {
|
func (c *configurationOriginal) newFieldFromDefinition(def fieldDefinition, typed bool) (field, error) {
|
||||||
// Check if the addresses are consecutive
|
// Check if the addresses are consecutive
|
||||||
expected := def.Address[0]
|
expected := def.Address[0]
|
||||||
for _, current := range def.Address[1:] {
|
for _, current := range def.Address[1:] {
|
||||||
|
|
@ -182,7 +182,7 @@ func (c *ConfigurationOriginal) newFieldFromDefinition(def fieldDefinition, type
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationOriginal) validateFieldDefinitions(fieldDefs []fieldDefinition, registerType string) error {
|
func (c *configurationOriginal) validateFieldDefinitions(fieldDefs []fieldDefinition, registerType string) error {
|
||||||
nameEncountered := make(map[string]bool, len(fieldDefs))
|
nameEncountered := make(map[string]bool, len(fieldDefs))
|
||||||
for _, item := range fieldDefs {
|
for _, item := range fieldDefs {
|
||||||
// check empty name
|
// check empty name
|
||||||
|
|
@ -276,7 +276,7 @@ func (c *ConfigurationOriginal) validateFieldDefinitions(fieldDefs []fieldDefini
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationOriginal) normalizeInputDatatype(dataType string, words int) (string, error) {
|
func (c *configurationOriginal) normalizeInputDatatype(dataType string, words int) (string, error) {
|
||||||
if dataType == "FLOAT32" {
|
if dataType == "FLOAT32" {
|
||||||
config.PrintOptionValueDeprecationNotice("input.modbus", "data_type", "FLOAT32", telegraf.DeprecationInfo{
|
config.PrintOptionValueDeprecationNotice("input.modbus", "data_type", "FLOAT32", telegraf.DeprecationInfo{
|
||||||
Since: "1.16.0",
|
Since: "1.16.0",
|
||||||
|
|
@ -323,7 +323,7 @@ func (c *ConfigurationOriginal) normalizeInputDatatype(dataType string, words in
|
||||||
return normalizeInputDatatype(dataType)
|
return normalizeInputDatatype(dataType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationOriginal) normalizeOutputDatatype(dataType string) (string, error) {
|
func (c *configurationOriginal) normalizeOutputDatatype(dataType string) (string, error) {
|
||||||
// Handle our special types
|
// Handle our special types
|
||||||
switch dataType {
|
switch dataType {
|
||||||
case "FIXED", "FLOAT32", "UFIXED":
|
case "FIXED", "FLOAT32", "UFIXED":
|
||||||
|
|
@ -332,7 +332,7 @@ func (c *ConfigurationOriginal) normalizeOutputDatatype(dataType string) (string
|
||||||
return normalizeOutputDatatype("native")
|
return normalizeOutputDatatype("native")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationOriginal) normalizeByteOrder(byteOrder string) (string, error) {
|
func (c *configurationOriginal) normalizeByteOrder(byteOrder string) (string, error) {
|
||||||
// Handle our special types
|
// Handle our special types
|
||||||
switch byteOrder {
|
switch byteOrder {
|
||||||
case "AB", "ABCDEFGH":
|
case "AB", "ABCDEFGH":
|
||||||
|
|
|
||||||
|
|
@ -37,19 +37,19 @@ type requestDefinition struct {
|
||||||
Tags map[string]string `toml:"tags"`
|
Tags map[string]string `toml:"tags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigurationPerRequest struct {
|
type configurationPerRequest struct {
|
||||||
Requests []requestDefinition `toml:"request"`
|
Requests []requestDefinition `toml:"request"`
|
||||||
|
|
||||||
workarounds ModbusWorkarounds
|
workarounds workarounds
|
||||||
excludeRegisterType bool
|
excludeRegisterType bool
|
||||||
logger telegraf.Logger
|
logger telegraf.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationPerRequest) SampleConfigPart() string {
|
func (c *configurationPerRequest) sampleConfigPart() string {
|
||||||
return sampleConfigPartPerRequest
|
return sampleConfigPartPerRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationPerRequest) Check() error {
|
func (c *configurationPerRequest) check() error {
|
||||||
switch c.workarounds.StringRegisterLocation {
|
switch c.workarounds.StringRegisterLocation {
|
||||||
case "", "both", "lower", "upper":
|
case "", "both", "lower", "upper":
|
||||||
// Do nothing as those are valid
|
// Do nothing as those are valid
|
||||||
|
|
@ -213,7 +213,7 @@ func (c *ConfigurationPerRequest) Check() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationPerRequest) Process() (map[byte]requestSet, error) {
|
func (c *configurationPerRequest) process() (map[byte]requestSet, error) {
|
||||||
result := make(map[byte]requestSet, len(c.Requests))
|
result := make(map[byte]requestSet, len(c.Requests))
|
||||||
for _, def := range c.Requests {
|
for _, def := range c.Requests {
|
||||||
// Set default
|
// Set default
|
||||||
|
|
@ -235,45 +235,45 @@ func (c *ConfigurationPerRequest) Process() (map[byte]requestSet, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
params := groupingParams{
|
params := groupingParams{
|
||||||
MaxExtraRegisters: def.MaxExtraRegisters,
|
maxExtraRegisters: def.MaxExtraRegisters,
|
||||||
Optimization: def.Optimization,
|
optimization: def.Optimization,
|
||||||
Tags: def.Tags,
|
tags: def.Tags,
|
||||||
Log: c.logger,
|
log: c.logger,
|
||||||
}
|
}
|
||||||
switch def.RegisterType {
|
switch def.RegisterType {
|
||||||
case "coil":
|
case "coil":
|
||||||
params.MaxBatchSize = maxQuantityCoils
|
params.maxBatchSize = maxQuantityCoils
|
||||||
if c.workarounds.OnRequestPerField {
|
if c.workarounds.OnRequestPerField {
|
||||||
params.MaxBatchSize = 1
|
params.maxBatchSize = 1
|
||||||
}
|
}
|
||||||
params.EnforceFromZero = c.workarounds.ReadCoilsStartingAtZero
|
params.enforceFromZero = c.workarounds.ReadCoilsStartingAtZero
|
||||||
requests := groupFieldsToRequests(fields, params)
|
requests := groupFieldsToRequests(fields, params)
|
||||||
set.coil = append(set.coil, requests...)
|
set.coil = append(set.coil, requests...)
|
||||||
case "discrete":
|
case "discrete":
|
||||||
params.MaxBatchSize = maxQuantityDiscreteInput
|
params.maxBatchSize = maxQuantityDiscreteInput
|
||||||
if c.workarounds.OnRequestPerField {
|
if c.workarounds.OnRequestPerField {
|
||||||
params.MaxBatchSize = 1
|
params.maxBatchSize = 1
|
||||||
}
|
}
|
||||||
requests := groupFieldsToRequests(fields, params)
|
requests := groupFieldsToRequests(fields, params)
|
||||||
set.discrete = append(set.discrete, requests...)
|
set.discrete = append(set.discrete, requests...)
|
||||||
case "holding":
|
case "holding":
|
||||||
params.MaxBatchSize = maxQuantityHoldingRegisters
|
params.maxBatchSize = maxQuantityHoldingRegisters
|
||||||
if c.workarounds.OnRequestPerField {
|
if c.workarounds.OnRequestPerField {
|
||||||
params.MaxBatchSize = 1
|
params.maxBatchSize = 1
|
||||||
}
|
}
|
||||||
requests := groupFieldsToRequests(fields, params)
|
requests := groupFieldsToRequests(fields, params)
|
||||||
set.holding = append(set.holding, requests...)
|
set.holding = append(set.holding, requests...)
|
||||||
case "input":
|
case "input":
|
||||||
params.MaxBatchSize = maxQuantityInputRegisters
|
params.maxBatchSize = maxQuantityInputRegisters
|
||||||
if c.workarounds.OnRequestPerField {
|
if c.workarounds.OnRequestPerField {
|
||||||
params.MaxBatchSize = 1
|
params.maxBatchSize = 1
|
||||||
}
|
}
|
||||||
requests := groupFieldsToRequests(fields, params)
|
requests := groupFieldsToRequests(fields, params)
|
||||||
set.input = append(set.input, requests...)
|
set.input = append(set.input, requests...)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown register type %q", def.RegisterType)
|
return nil, fmt.Errorf("unknown register type %q", def.RegisterType)
|
||||||
}
|
}
|
||||||
if !set.Empty() {
|
if !set.empty() {
|
||||||
result[def.SlaveID] = set
|
result[def.SlaveID] = set
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -281,7 +281,7 @@ func (c *ConfigurationPerRequest) Process() (map[byte]requestSet, error) {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationPerRequest) initFields(fieldDefs []requestFieldDefinition, typed bool, byteOrder string) ([]field, error) {
|
func (c *configurationPerRequest) initFields(fieldDefs []requestFieldDefinition, typed bool, byteOrder string) ([]field, error) {
|
||||||
// Construct the fields from the field definitions
|
// Construct the fields from the field definitions
|
||||||
fields := make([]field, 0, len(fieldDefs))
|
fields := make([]field, 0, len(fieldDefs))
|
||||||
for _, def := range fieldDefs {
|
for _, def := range fieldDefs {
|
||||||
|
|
@ -295,7 +295,7 @@ func (c *ConfigurationPerRequest) initFields(fieldDefs []requestFieldDefinition,
|
||||||
return fields, nil
|
return fields, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationPerRequest) newFieldFromDefinition(def requestFieldDefinition, typed bool, byteOrder string) (field, error) {
|
func (c *configurationPerRequest) newFieldFromDefinition(def requestFieldDefinition, typed bool, byteOrder string) (field, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
fieldLength := uint16(1)
|
fieldLength := uint16(1)
|
||||||
|
|
@ -379,7 +379,7 @@ func (c *ConfigurationPerRequest) newFieldFromDefinition(def requestFieldDefinit
|
||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationPerRequest) fieldID(seed maphash.Seed, def requestDefinition, field requestFieldDefinition) uint64 {
|
func (c *configurationPerRequest) fieldID(seed maphash.Seed, def requestDefinition, field requestFieldDefinition) uint64 {
|
||||||
var mh maphash.Hash
|
var mh maphash.Hash
|
||||||
mh.SetSeed(seed)
|
mh.SetSeed(seed)
|
||||||
|
|
||||||
|
|
@ -394,7 +394,7 @@ func (c *ConfigurationPerRequest) fieldID(seed maphash.Seed, def requestDefiniti
|
||||||
mh.WriteString(field.Name)
|
mh.WriteString(field.Name)
|
||||||
mh.WriteByte(0)
|
mh.WriteByte(0)
|
||||||
|
|
||||||
// Tags
|
// tags
|
||||||
for k, v := range def.Tags {
|
for k, v := range def.Tags {
|
||||||
mh.WriteString(k)
|
mh.WriteString(k)
|
||||||
mh.WriteByte('=')
|
mh.WriteByte('=')
|
||||||
|
|
@ -406,7 +406,7 @@ func (c *ConfigurationPerRequest) fieldID(seed maphash.Seed, def requestDefiniti
|
||||||
return mh.Sum64()
|
return mh.Sum64()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationPerRequest) determineOutputDatatype(input string) (string, error) {
|
func (c *configurationPerRequest) determineOutputDatatype(input string) (string, error) {
|
||||||
// Handle our special types
|
// Handle our special types
|
||||||
switch input {
|
switch input {
|
||||||
case "INT8L", "INT8H", "INT16", "INT32", "INT64":
|
case "INT8L", "INT8H", "INT16", "INT32", "INT64":
|
||||||
|
|
@ -421,7 +421,7 @@ func (c *ConfigurationPerRequest) determineOutputDatatype(input string) (string,
|
||||||
return "unknown", fmt.Errorf("invalid input datatype %q for determining output", input)
|
return "unknown", fmt.Errorf("invalid input datatype %q for determining output", input)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigurationPerRequest) determineFieldLength(input string, length uint16) (uint16, error) {
|
func (c *configurationPerRequest) determineFieldLength(input string, length uint16) (uint16, error) {
|
||||||
// Handle our special types
|
// Handle our special types
|
||||||
switch input {
|
switch input {
|
||||||
case "BIT", "INT8L", "INT8H", "UINT8L", "UINT8H":
|
case "BIT", "INT8L", "INT8H", "UINT8L", "UINT8H":
|
||||||
|
|
|
||||||
|
|
@ -3177,7 +3177,7 @@ func TestRequestWorkaroundsOneRequestPerField(t *testing.T) {
|
||||||
Controller: "tcp://localhost:1502",
|
Controller: "tcp://localhost:1502",
|
||||||
ConfigurationType: "request",
|
ConfigurationType: "request",
|
||||||
Log: testutil.Logger{},
|
Log: testutil.Logger{},
|
||||||
Workarounds: ModbusWorkarounds{OnRequestPerField: true},
|
Workarounds: workarounds{OnRequestPerField: true},
|
||||||
}
|
}
|
||||||
plugin.Requests = []requestDefinition{
|
plugin.Requests = []requestDefinition{
|
||||||
{
|
{
|
||||||
|
|
@ -3223,7 +3223,7 @@ func TestRequestWorkaroundsReadCoilsStartingAtZeroRequest(t *testing.T) {
|
||||||
Controller: "tcp://localhost:1502",
|
Controller: "tcp://localhost:1502",
|
||||||
ConfigurationType: "request",
|
ConfigurationType: "request",
|
||||||
Log: testutil.Logger{},
|
Log: testutil.Logger{},
|
||||||
Workarounds: ModbusWorkarounds{ReadCoilsStartingAtZero: true},
|
Workarounds: workarounds{ReadCoilsStartingAtZero: true},
|
||||||
}
|
}
|
||||||
plugin.SlaveID = 1
|
plugin.SlaveID = 1
|
||||||
plugin.Requests = []requestDefinition{
|
plugin.Requests = []requestDefinition{
|
||||||
|
|
@ -3262,7 +3262,7 @@ func TestRequestOverlap(t *testing.T) {
|
||||||
Controller: "tcp://localhost:1502",
|
Controller: "tcp://localhost:1502",
|
||||||
ConfigurationType: "request",
|
ConfigurationType: "request",
|
||||||
Log: logger,
|
Log: logger,
|
||||||
Workarounds: ModbusWorkarounds{ReadCoilsStartingAtZero: true},
|
Workarounds: workarounds{ReadCoilsStartingAtZero: true},
|
||||||
}
|
}
|
||||||
plugin.Requests = []requestDefinition{
|
plugin.Requests = []requestDefinition{
|
||||||
{
|
{
|
||||||
|
|
@ -3320,7 +3320,7 @@ func TestRequestAddressOverflow(t *testing.T) {
|
||||||
Controller: "tcp://localhost:1502",
|
Controller: "tcp://localhost:1502",
|
||||||
ConfigurationType: "request",
|
ConfigurationType: "request",
|
||||||
Log: logger,
|
Log: logger,
|
||||||
Workarounds: ModbusWorkarounds{ReadCoilsStartingAtZero: true},
|
Workarounds: workarounds{ReadCoilsStartingAtZero: true},
|
||||||
}
|
}
|
||||||
plugin.Requests = []requestDefinition{
|
plugin.Requests = []requestDefinition{
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,45 @@ var sampleConfigEnd string
|
||||||
|
|
||||||
var errAddressOverflow = errors.New("address overflow")
|
var errAddressOverflow = errors.New("address overflow")
|
||||||
|
|
||||||
type ModbusWorkarounds struct {
|
const (
|
||||||
|
cDiscreteInputs = "discrete_input"
|
||||||
|
cCoils = "coil"
|
||||||
|
cHoldingRegisters = "holding_register"
|
||||||
|
cInputRegisters = "input_register"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Modbus struct {
|
||||||
|
Name string `toml:"name"`
|
||||||
|
Controller string `toml:"controller"`
|
||||||
|
TransmissionMode string `toml:"transmission_mode"`
|
||||||
|
BaudRate int `toml:"baud_rate"`
|
||||||
|
DataBits int `toml:"data_bits"`
|
||||||
|
Parity string `toml:"parity"`
|
||||||
|
StopBits int `toml:"stop_bits"`
|
||||||
|
RS485 *rs485Config `toml:"rs485"`
|
||||||
|
Timeout config.Duration `toml:"timeout"`
|
||||||
|
Retries int `toml:"busy_retries"`
|
||||||
|
RetriesWaitTime config.Duration `toml:"busy_retries_wait"`
|
||||||
|
DebugConnection bool `toml:"debug_connection" deprecated:"1.35.0;use 'log_level' 'trace' instead"`
|
||||||
|
Workarounds workarounds `toml:"workarounds"`
|
||||||
|
ConfigurationType string `toml:"configuration_type"`
|
||||||
|
ExcludeRegisterTypeTag bool `toml:"exclude_register_type_tag"`
|
||||||
|
Log telegraf.Logger `toml:"-"`
|
||||||
|
|
||||||
|
// configuration type specific settings
|
||||||
|
configurationOriginal
|
||||||
|
configurationPerRequest
|
||||||
|
configurationPerMetric
|
||||||
|
|
||||||
|
// Connection handling
|
||||||
|
client mb.Client
|
||||||
|
handler mb.ClientHandler
|
||||||
|
isConnected bool
|
||||||
|
// Request handling
|
||||||
|
requests map[byte]requestSet
|
||||||
|
}
|
||||||
|
|
||||||
|
type workarounds struct {
|
||||||
AfterConnectPause config.Duration `toml:"pause_after_connect"`
|
AfterConnectPause config.Duration `toml:"pause_after_connect"`
|
||||||
PollPause config.Duration `toml:"pause_between_requests"`
|
PollPause config.Duration `toml:"pause_between_requests"`
|
||||||
CloseAfterGather bool `toml:"close_connection_after_gather"`
|
CloseAfterGather bool `toml:"close_connection_after_gather"`
|
||||||
|
|
@ -37,7 +75,7 @@ type ModbusWorkarounds struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// According to github.com/grid-x/serial
|
// According to github.com/grid-x/serial
|
||||||
type RS485Config struct {
|
type rs485Config struct {
|
||||||
DelayRtsBeforeSend config.Duration `toml:"delay_rts_before_send"`
|
DelayRtsBeforeSend config.Duration `toml:"delay_rts_before_send"`
|
||||||
DelayRtsAfterSend config.Duration `toml:"delay_rts_after_send"`
|
DelayRtsAfterSend config.Duration `toml:"delay_rts_after_send"`
|
||||||
RtsHighDuringSend bool `toml:"rts_high_during_send"`
|
RtsHighDuringSend bool `toml:"rts_high_during_send"`
|
||||||
|
|
@ -45,38 +83,6 @@ type RS485Config struct {
|
||||||
RxDuringTx bool `toml:"rx_during_tx"`
|
RxDuringTx bool `toml:"rx_during_tx"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modbus holds all data relevant to the plugin
|
|
||||||
type Modbus struct {
|
|
||||||
Name string `toml:"name"`
|
|
||||||
Controller string `toml:"controller"`
|
|
||||||
TransmissionMode string `toml:"transmission_mode"`
|
|
||||||
BaudRate int `toml:"baud_rate"`
|
|
||||||
DataBits int `toml:"data_bits"`
|
|
||||||
Parity string `toml:"parity"`
|
|
||||||
StopBits int `toml:"stop_bits"`
|
|
||||||
RS485 *RS485Config `toml:"rs485"`
|
|
||||||
Timeout config.Duration `toml:"timeout"`
|
|
||||||
Retries int `toml:"busy_retries"`
|
|
||||||
RetriesWaitTime config.Duration `toml:"busy_retries_wait"`
|
|
||||||
DebugConnection bool `toml:"debug_connection" deprecated:"1.35.0;use 'log_level' 'trace' instead"`
|
|
||||||
Workarounds ModbusWorkarounds `toml:"workarounds"`
|
|
||||||
ConfigurationType string `toml:"configuration_type"`
|
|
||||||
ExcludeRegisterTypeTag bool `toml:"exclude_register_type_tag"`
|
|
||||||
Log telegraf.Logger `toml:"-"`
|
|
||||||
|
|
||||||
// Configuration type specific settings
|
|
||||||
ConfigurationOriginal
|
|
||||||
ConfigurationPerRequest
|
|
||||||
ConfigurationPerMetric
|
|
||||||
|
|
||||||
// Connection handling
|
|
||||||
client mb.Client
|
|
||||||
handler mb.ClientHandler
|
|
||||||
isConnected bool
|
|
||||||
// Request handling
|
|
||||||
requests map[byte]requestSet
|
|
||||||
}
|
|
||||||
|
|
||||||
type fieldConverterFunc func(bytes []byte) interface{}
|
type fieldConverterFunc func(bytes []byte) interface{}
|
||||||
|
|
||||||
type requestSet struct {
|
type requestSet struct {
|
||||||
|
|
@ -86,7 +92,7 @@ type requestSet struct {
|
||||||
input []request
|
input []request
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r requestSet) Empty() bool {
|
func (r requestSet) empty() bool {
|
||||||
l := len(r.coil)
|
l := len(r.coil)
|
||||||
l += len(r.discrete)
|
l += len(r.discrete)
|
||||||
l += len(r.holding)
|
l += len(r.holding)
|
||||||
|
|
@ -105,24 +111,16 @@ type field struct {
|
||||||
tags map[string]string
|
tags map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
cDiscreteInputs = "discrete_input"
|
|
||||||
cCoils = "coil"
|
|
||||||
cHoldingRegisters = "holding_register"
|
|
||||||
cInputRegisters = "input_register"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SampleConfig returns a basic configuration for the plugin
|
|
||||||
func (m *Modbus) SampleConfig() string {
|
func (m *Modbus) SampleConfig() string {
|
||||||
configs := []Configuration{
|
configs := []configuration{
|
||||||
&m.ConfigurationOriginal,
|
&m.configurationOriginal,
|
||||||
&m.ConfigurationPerRequest,
|
&m.configurationPerRequest,
|
||||||
&m.ConfigurationPerMetric,
|
&m.configurationPerMetric,
|
||||||
}
|
}
|
||||||
|
|
||||||
totalConfig := sampleConfigStart
|
totalConfig := sampleConfigStart
|
||||||
for _, c := range configs {
|
for _, c := range configs {
|
||||||
totalConfig += c.SampleConfigPart() + "\n"
|
totalConfig += c.sampleConfigPart() + "\n"
|
||||||
}
|
}
|
||||||
totalConfig += "\n"
|
totalConfig += "\n"
|
||||||
totalConfig += sampleConfigEnd
|
totalConfig += sampleConfigEnd
|
||||||
|
|
@ -140,32 +138,32 @@ func (m *Modbus) Init() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the configuration style
|
// Determine the configuration style
|
||||||
var cfg Configuration
|
var cfg configuration
|
||||||
switch m.ConfigurationType {
|
switch m.ConfigurationType {
|
||||||
case "", "register":
|
case "", "register":
|
||||||
m.ConfigurationOriginal.workarounds = m.Workarounds
|
m.configurationOriginal.workarounds = m.Workarounds
|
||||||
m.ConfigurationOriginal.logger = m.Log
|
m.configurationOriginal.logger = m.Log
|
||||||
cfg = &m.ConfigurationOriginal
|
cfg = &m.configurationOriginal
|
||||||
case "request":
|
case "request":
|
||||||
m.ConfigurationPerRequest.workarounds = m.Workarounds
|
m.configurationPerRequest.workarounds = m.Workarounds
|
||||||
m.ConfigurationPerRequest.excludeRegisterType = m.ExcludeRegisterTypeTag
|
m.configurationPerRequest.excludeRegisterType = m.ExcludeRegisterTypeTag
|
||||||
m.ConfigurationPerRequest.logger = m.Log
|
m.configurationPerRequest.logger = m.Log
|
||||||
cfg = &m.ConfigurationPerRequest
|
cfg = &m.configurationPerRequest
|
||||||
case "metric":
|
case "metric":
|
||||||
m.ConfigurationPerMetric.workarounds = m.Workarounds
|
m.configurationPerMetric.workarounds = m.Workarounds
|
||||||
m.ConfigurationPerMetric.excludeRegisterType = m.ExcludeRegisterTypeTag
|
m.configurationPerMetric.excludeRegisterType = m.ExcludeRegisterTypeTag
|
||||||
m.ConfigurationPerMetric.logger = m.Log
|
m.configurationPerMetric.logger = m.Log
|
||||||
cfg = &m.ConfigurationPerMetric
|
cfg = &m.configurationPerMetric
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown configuration type %q in device %q", m.ConfigurationType, m.Name)
|
return fmt.Errorf("unknown configuration type %q in device %q", m.ConfigurationType, m.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check and process the configuration
|
// Check and process the configuration
|
||||||
if err := cfg.Check(); err != nil {
|
if err := cfg.check(); err != nil {
|
||||||
return fmt.Errorf("configuration invalid for device %q: %w", m.Name, err)
|
return fmt.Errorf("configuration invalid for device %q: %w", m.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := cfg.Process()
|
r, err := cfg.process()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot process configuration for device %q: %w", m.Name, err)
|
return fmt.Errorf("cannot process configuration for device %q: %w", m.Name, err)
|
||||||
}
|
}
|
||||||
|
|
@ -219,7 +217,6 @@ func (m *Modbus) Init() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather implements the telegraf plugin interface method for data accumulation
|
|
||||||
func (m *Modbus) Gather(acc telegraf.Accumulator) error {
|
func (m *Modbus) Gather(acc telegraf.Accumulator) error {
|
||||||
if !m.isConnected {
|
if !m.isConnected {
|
||||||
if err := m.connect(); err != nil {
|
if err := m.connect(); err != nil {
|
||||||
|
|
@ -558,7 +555,7 @@ func (m *Modbus) collectFields(grouper *metric.SeriesGrouper, timestamp time.Tim
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement the logger interface of the modbus client
|
// Printf implements the logger interface of the modbus client
|
||||||
func (m *Modbus) Printf(format string, v ...interface{}) {
|
func (m *Modbus) Printf(format string, v ...interface{}) {
|
||||||
m.Log.Tracef(format, v...)
|
m.Log.Tracef(format, v...)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -491,7 +491,7 @@ func TestRegisterWorkaroundsOneRequestPerField(t *testing.T) {
|
||||||
Controller: "tcp://localhost:1502",
|
Controller: "tcp://localhost:1502",
|
||||||
ConfigurationType: "register",
|
ConfigurationType: "register",
|
||||||
Log: testutil.Logger{Quiet: true},
|
Log: testutil.Logger{Quiet: true},
|
||||||
Workarounds: ModbusWorkarounds{OnRequestPerField: true},
|
Workarounds: workarounds{OnRequestPerField: true},
|
||||||
}
|
}
|
||||||
plugin.SlaveID = 1
|
plugin.SlaveID = 1
|
||||||
plugin.HoldingRegisters = []fieldDefinition{
|
plugin.HoldingRegisters = []fieldDefinition{
|
||||||
|
|
@ -541,7 +541,7 @@ func TestRequestsWorkaroundsReadCoilsStartingAtZeroRegister(t *testing.T) {
|
||||||
Controller: "tcp://localhost:1502",
|
Controller: "tcp://localhost:1502",
|
||||||
ConfigurationType: "register",
|
ConfigurationType: "register",
|
||||||
Log: testutil.Logger{Quiet: true},
|
Log: testutil.Logger{Quiet: true},
|
||||||
Workarounds: ModbusWorkarounds{ReadCoilsStartingAtZero: true},
|
Workarounds: workarounds{ReadCoilsStartingAtZero: true},
|
||||||
}
|
}
|
||||||
plugin.SlaveID = 1
|
plugin.SlaveID = 1
|
||||||
plugin.Coils = []fieldDefinition{
|
plugin.Coils = []fieldDefinition{
|
||||||
|
|
@ -688,8 +688,8 @@ func TestWorkaroundsStringRegisterLocation(t *testing.T) {
|
||||||
Controller: "tcp://localhost:1502",
|
Controller: "tcp://localhost:1502",
|
||||||
ConfigurationType: "request",
|
ConfigurationType: "request",
|
||||||
Log: testutil.Logger{Quiet: true},
|
Log: testutil.Logger{Quiet: true},
|
||||||
Workarounds: ModbusWorkarounds{StringRegisterLocation: tt.location},
|
Workarounds: workarounds{StringRegisterLocation: tt.location},
|
||||||
ConfigurationPerRequest: ConfigurationPerRequest{
|
configurationPerRequest: configurationPerRequest{
|
||||||
Requests: []requestDefinition{
|
Requests: []requestDefinition{
|
||||||
{
|
{
|
||||||
SlaveID: 1,
|
SlaveID: 1,
|
||||||
|
|
@ -738,7 +738,7 @@ func TestWorkaroundsStringRegisterLocationInvalid(t *testing.T) {
|
||||||
Controller: "tcp://localhost:1502",
|
Controller: "tcp://localhost:1502",
|
||||||
ConfigurationType: "request",
|
ConfigurationType: "request",
|
||||||
Log: testutil.Logger{Quiet: true},
|
Log: testutil.Logger{Quiet: true},
|
||||||
Workarounds: ModbusWorkarounds{StringRegisterLocation: "foo"},
|
Workarounds: workarounds{StringRegisterLocation: "foo"},
|
||||||
}
|
}
|
||||||
require.ErrorContains(t, plugin.Init(), `invalid 'string_register_location'`)
|
require.ErrorContains(t, plugin.Init(), `invalid 'string_register_location'`)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ func optimizeGroup(g request, maxBatchSize uint16) []request {
|
||||||
return requests
|
return requests
|
||||||
}
|
}
|
||||||
|
|
||||||
func optimitzeGroupWithinLimits(g request, params groupingParams) []request {
|
func optimizeGroupWithinLimits(g request, params groupingParams) []request {
|
||||||
if len(g.fields) == 0 {
|
if len(g.fields) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -153,14 +153,14 @@ func optimitzeGroupWithinLimits(g request, params groupingParams) []request {
|
||||||
// Check if we need to interrupt the current chunk and require a new one
|
// Check if we need to interrupt the current chunk and require a new one
|
||||||
holeSize := g.fields[i].address - (g.fields[i-1].address + g.fields[i-1].length)
|
holeSize := g.fields[i].address - (g.fields[i-1].address + g.fields[i-1].length)
|
||||||
if g.fields[i].address < g.fields[i-1].address+g.fields[i-1].length {
|
if g.fields[i].address < g.fields[i-1].address+g.fields[i-1].length {
|
||||||
params.Log.Warnf(
|
params.log.Warnf(
|
||||||
"Request at %d with length %d overlaps with next request at %d",
|
"Request at %d with length %d overlaps with next request at %d",
|
||||||
g.fields[i-1].address, g.fields[i-1].length, g.fields[i].address,
|
g.fields[i-1].address, g.fields[i-1].length, g.fields[i].address,
|
||||||
)
|
)
|
||||||
holeSize = 0
|
holeSize = 0
|
||||||
}
|
}
|
||||||
needInterrupt := holeSize > params.MaxExtraRegisters // too far apart
|
needInterrupt := holeSize > params.maxExtraRegisters // too far apart
|
||||||
needInterrupt = needInterrupt || currentRequest.length+holeSize+g.fields[i].length > params.MaxBatchSize // too large
|
needInterrupt = needInterrupt || currentRequest.length+holeSize+g.fields[i].length > params.maxBatchSize // too large
|
||||||
if !needInterrupt {
|
if !needInterrupt {
|
||||||
// Still safe to add the field to the current request
|
// Still safe to add the field to the current request
|
||||||
currentRequest.length = g.fields[i].address + g.fields[i].length - currentRequest.address
|
currentRequest.length = g.fields[i].address + g.fields[i].length - currentRequest.address
|
||||||
|
|
@ -181,18 +181,16 @@ func optimitzeGroupWithinLimits(g request, params groupingParams) []request {
|
||||||
|
|
||||||
type groupingParams struct {
|
type groupingParams struct {
|
||||||
// Maximum size of a request in registers
|
// Maximum size of a request in registers
|
||||||
MaxBatchSize uint16
|
maxBatchSize uint16
|
||||||
// Optimization to use for grouping register groups to requests.
|
// optimization to use for grouping register groups to requests, Also put potential optimization parameters here
|
||||||
// Also put potential optimization parameters here
|
optimization string
|
||||||
Optimization string
|
maxExtraRegisters uint16
|
||||||
MaxExtraRegisters uint16
|
// Will force reads to start at zero (if possible) while respecting the max-batch size.
|
||||||
// Will force reads to start at zero (if possible) while respecting
|
enforceFromZero bool
|
||||||
// the max-batch size.
|
// tags to add for the requests
|
||||||
EnforceFromZero bool
|
tags map[string]string
|
||||||
// Tags to add for the requests
|
// log facility to inform the user
|
||||||
Tags map[string]string
|
log telegraf.Logger
|
||||||
// Log facility to inform the user
|
|
||||||
Log telegraf.Logger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func groupFieldsToRequests(fields []field, params groupingParams) []request {
|
func groupFieldsToRequests(fields []field, params groupingParams) []request {
|
||||||
|
|
@ -216,9 +214,9 @@ func groupFieldsToRequests(fields []field, params groupingParams) []request {
|
||||||
for _, f := range fields {
|
for _, f := range fields {
|
||||||
// Add tags from higher up
|
// Add tags from higher up
|
||||||
if f.tags == nil {
|
if f.tags == nil {
|
||||||
f.tags = make(map[string]string, len(params.Tags))
|
f.tags = make(map[string]string, len(params.tags))
|
||||||
}
|
}
|
||||||
for k, v := range params.Tags {
|
for k, v := range params.tags {
|
||||||
f.tags[k] = v
|
f.tags[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -253,18 +251,18 @@ func groupFieldsToRequests(fields []field, params groupingParams) []request {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enforce the first read to start at zero if the option is set
|
// Enforce the first read to start at zero if the option is set
|
||||||
if params.EnforceFromZero {
|
if params.enforceFromZero {
|
||||||
groups[0].length += groups[0].address
|
groups[0].length += groups[0].address
|
||||||
groups[0].address = 0
|
groups[0].address = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
var requests []request
|
var requests []request
|
||||||
switch params.Optimization {
|
switch params.optimization {
|
||||||
case "shrink":
|
case "shrink":
|
||||||
// Shrink request by striping leading and trailing fields with an omit flag set
|
// Shrink request by striping leading and trailing fields with an omit flag set
|
||||||
for _, g := range groups {
|
for _, g := range groups {
|
||||||
if len(g.fields) > 0 {
|
if len(g.fields) > 0 {
|
||||||
requests = append(requests, shrinkGroup(g, params.MaxBatchSize)...)
|
requests = append(requests, shrinkGroup(g, params.maxBatchSize)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "rearrange":
|
case "rearrange":
|
||||||
|
|
@ -272,7 +270,7 @@ func groupFieldsToRequests(fields []field, params groupingParams) []request {
|
||||||
// registers while keeping the number of requests
|
// registers while keeping the number of requests
|
||||||
for _, g := range groups {
|
for _, g := range groups {
|
||||||
if len(g.fields) > 0 {
|
if len(g.fields) > 0 {
|
||||||
requests = append(requests, optimizeGroup(g, params.MaxBatchSize)...)
|
requests = append(requests, optimizeGroup(g, params.maxBatchSize)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "aggressive":
|
case "aggressive":
|
||||||
|
|
@ -284,7 +282,7 @@ func groupFieldsToRequests(fields []field, params groupingParams) []request {
|
||||||
total.fields = append(total.fields, g.fields...)
|
total.fields = append(total.fields, g.fields...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
requests = optimizeGroup(total, params.MaxBatchSize)
|
requests = optimizeGroup(total, params.maxBatchSize)
|
||||||
case "max_insert":
|
case "max_insert":
|
||||||
// Similar to aggressive but keeps the number of touched registers below a threshold
|
// Similar to aggressive but keeps the number of touched registers below a threshold
|
||||||
var total request
|
var total request
|
||||||
|
|
@ -293,12 +291,12 @@ func groupFieldsToRequests(fields []field, params groupingParams) []request {
|
||||||
total.fields = append(total.fields, g.fields...)
|
total.fields = append(total.fields, g.fields...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
requests = optimitzeGroupWithinLimits(total, params)
|
requests = optimizeGroupWithinLimits(total, params)
|
||||||
default:
|
default:
|
||||||
// no optimization
|
// no optimization
|
||||||
for _, g := range groups {
|
for _, g := range groups {
|
||||||
if len(g.fields) > 0 {
|
if len(g.fields) > 0 {
|
||||||
requests = append(requests, splitMaxBatchSize(g, params.MaxBatchSize)...)
|
requests = append(requests, splitMaxBatchSize(g, params.maxBatchSize)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,26 +26,26 @@ import (
|
||||||
//go:embed sample.conf
|
//go:embed sample.conf
|
||||||
var sampleConfig string
|
var sampleConfig string
|
||||||
|
|
||||||
var DisconnectedServersBehaviors = []string{"error", "skip"}
|
var disconnectedServersBehaviors = []string{"error", "skip"}
|
||||||
|
|
||||||
type MongoDB struct {
|
type MongoDB struct {
|
||||||
Servers []string
|
Servers []string `toml:"servers"`
|
||||||
Ssl Ssl
|
GatherClusterStatus bool `toml:"gather_cluster_status"`
|
||||||
GatherClusterStatus bool
|
GatherPerdbStats bool `toml:"gather_perdb_stats"`
|
||||||
GatherPerdbStats bool
|
GatherColStats bool `toml:"gather_col_stats"`
|
||||||
GatherColStats bool
|
GatherTopStat bool `toml:"gather_top_stat"`
|
||||||
GatherTopStat bool
|
DisconnectedServersBehavior string `toml:"disconnected_servers_behavior"`
|
||||||
DisconnectedServersBehavior string
|
ColStatsDbs []string `toml:"col_stats_dbs"`
|
||||||
ColStatsDbs []string
|
|
||||||
common_tls.ClientConfig
|
common_tls.ClientConfig
|
||||||
|
Ssl ssl
|
||||||
|
|
||||||
Log telegraf.Logger `toml:"-"`
|
Log telegraf.Logger `toml:"-"`
|
||||||
|
|
||||||
clients []*Server
|
clients []*server
|
||||||
tlsConfig *tls.Config
|
tlsConfig *tls.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
type Ssl struct {
|
type ssl struct {
|
||||||
Enabled bool `toml:"ssl_enabled" deprecated:"1.3.0;1.35.0;use 'tls_*' options instead"`
|
Enabled bool `toml:"ssl_enabled" deprecated:"1.3.0;1.35.0;use 'tls_*' options instead"`
|
||||||
CaCerts []string `toml:"cacerts" deprecated:"1.3.0;1.35.0;use 'tls_ca' instead"`
|
CaCerts []string `toml:"cacerts" deprecated:"1.3.0;1.35.0;use 'tls_ca' instead"`
|
||||||
}
|
}
|
||||||
|
|
@ -59,7 +59,7 @@ func (m *MongoDB) Init() error {
|
||||||
m.DisconnectedServersBehavior = "error"
|
m.DisconnectedServersBehavior = "error"
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := choice.Check(m.DisconnectedServersBehavior, DisconnectedServersBehaviors); err != nil {
|
if err := choice.Check(m.DisconnectedServersBehavior, disconnectedServersBehaviors); err != nil {
|
||||||
return fmt.Errorf("disconnected_servers_behavior: %w", err)
|
return fmt.Errorf("disconnected_servers_behavior: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,6 +105,41 @@ func (m *MongoDB) Start(telegraf.Accumulator) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MongoDB) Gather(acc telegraf.Accumulator) error {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for _, client := range m.clients {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(srv *server) {
|
||||||
|
defer wg.Done()
|
||||||
|
if m.DisconnectedServersBehavior == "skip" {
|
||||||
|
if err := srv.ping(); err != nil {
|
||||||
|
m.Log.Debugf("Failed to ping server: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := srv.gatherData(acc, m.GatherClusterStatus, m.GatherPerdbStats, m.GatherColStats, m.GatherTopStat, m.ColStatsDbs)
|
||||||
|
if err != nil {
|
||||||
|
m.Log.Errorf("Failed to gather data: %s", err)
|
||||||
|
}
|
||||||
|
}(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop disconnects mongo connections when stop or reload
|
||||||
|
func (m *MongoDB) Stop() {
|
||||||
|
for _, server := range m.clients {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
if err := server.client.Disconnect(ctx); err != nil {
|
||||||
|
m.Log.Errorf("Disconnecting from %q failed: %v", server.hostname, err)
|
||||||
|
}
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (m *MongoDB) setupConnection(connURL string) error {
|
func (m *MongoDB) setupConnection(connURL string) error {
|
||||||
if !strings.HasPrefix(connURL, "mongodb://") && !strings.HasPrefix(connURL, "mongodb+srv://") {
|
if !strings.HasPrefix(connURL, "mongodb://") && !strings.HasPrefix(connURL, "mongodb+srv://") {
|
||||||
// Preserve backwards compatibility for hostnames without a
|
// Preserve backwards compatibility for hostnames without a
|
||||||
|
|
@ -143,52 +178,15 @@ func (m *MongoDB) setupConnection(connURL string) error {
|
||||||
m.Log.Errorf("Unable to ping MongoDB: %s", err)
|
m.Log.Errorf("Unable to ping MongoDB: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
server := &Server{
|
server := &server{
|
||||||
client: client,
|
client: client,
|
||||||
hostname: u.Host,
|
hostname: u.Host,
|
||||||
Log: m.Log,
|
log: m.Log,
|
||||||
}
|
}
|
||||||
m.clients = append(m.clients, server)
|
m.clients = append(m.clients, server)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop disconnect mongo connections when stop or reload
|
|
||||||
func (m *MongoDB) Stop() {
|
|
||||||
for _, server := range m.clients {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
||||||
if err := server.client.Disconnect(ctx); err != nil {
|
|
||||||
m.Log.Errorf("Disconnecting from %q failed: %v", server.hostname, err)
|
|
||||||
}
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reads stats from all configured servers accumulates stats.
|
|
||||||
// Returns one of the errors encountered while gather stats (if any).
|
|
||||||
func (m *MongoDB) Gather(acc telegraf.Accumulator) error {
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
for _, client := range m.clients {
|
|
||||||
wg.Add(1)
|
|
||||||
go func(srv *Server) {
|
|
||||||
defer wg.Done()
|
|
||||||
if m.DisconnectedServersBehavior == "skip" {
|
|
||||||
if err := srv.ping(); err != nil {
|
|
||||||
m.Log.Debugf("Failed to ping server: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := srv.gatherData(acc, m.GatherClusterStatus, m.GatherPerdbStats, m.GatherColStats, m.GatherTopStat, m.ColStatsDbs)
|
|
||||||
if err != nil {
|
|
||||||
m.Log.Errorf("Failed to gather data: %s", err)
|
|
||||||
}
|
|
||||||
}(client)
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("mongodb", func() telegraf.Input {
|
inputs.Add("mongodb", func() telegraf.Input {
|
||||||
return &MongoDB{
|
return &MongoDB{
|
||||||
|
|
|
||||||
|
|
@ -8,29 +8,29 @@ import (
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MongodbData struct {
|
type mongodbData struct {
|
||||||
StatLine *statLine
|
StatLine *statLine
|
||||||
Fields map[string]interface{}
|
Fields map[string]interface{}
|
||||||
Tags map[string]string
|
Tags map[string]string
|
||||||
DbData []DbData
|
DbData []bbData
|
||||||
ColData []ColData
|
ColData []colData
|
||||||
ShardHostData []DbData
|
ShardHostData []bbData
|
||||||
TopStatsData []DbData
|
TopStatsData []bbData
|
||||||
}
|
}
|
||||||
|
|
||||||
type DbData struct {
|
type bbData struct {
|
||||||
Name string
|
Name string
|
||||||
Fields map[string]interface{}
|
Fields map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ColData struct {
|
type colData struct {
|
||||||
Name string
|
Name string
|
||||||
DbName string
|
DbName string
|
||||||
Fields map[string]interface{}
|
Fields map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMongodbData(statLine *statLine, tags map[string]string) *MongodbData {
|
func newMongodbData(statLine *statLine, tags map[string]string) *mongodbData {
|
||||||
return &MongodbData{
|
return &mongodbData{
|
||||||
StatLine: statLine,
|
StatLine: statLine,
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
Fields: make(map[string]interface{}),
|
Fields: make(map[string]interface{}),
|
||||||
|
|
@ -297,11 +297,11 @@ var topDataStats = map[string]string{
|
||||||
"commands_count": "CommandsCount",
|
"commands_count": "CommandsCount",
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MongodbData) AddDbStats() {
|
func (d *mongodbData) addDbStats() {
|
||||||
for i := range d.StatLine.DbStatsLines {
|
for i := range d.StatLine.DbStatsLines {
|
||||||
dbstat := d.StatLine.DbStatsLines[i]
|
dbstat := d.StatLine.DbStatsLines[i]
|
||||||
dbStatLine := reflect.ValueOf(&dbstat).Elem()
|
dbStatLine := reflect.ValueOf(&dbstat).Elem()
|
||||||
newDbData := &DbData{
|
newDbData := &bbData{
|
||||||
Name: dbstat.Name,
|
Name: dbstat.Name,
|
||||||
Fields: make(map[string]interface{}),
|
Fields: make(map[string]interface{}),
|
||||||
}
|
}
|
||||||
|
|
@ -314,11 +314,11 @@ func (d *MongodbData) AddDbStats() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MongodbData) AddColStats() {
|
func (d *mongodbData) addColStats() {
|
||||||
for i := range d.StatLine.ColStatsLines {
|
for i := range d.StatLine.ColStatsLines {
|
||||||
colstat := d.StatLine.ColStatsLines[i]
|
colstat := d.StatLine.ColStatsLines[i]
|
||||||
colStatLine := reflect.ValueOf(&colstat).Elem()
|
colStatLine := reflect.ValueOf(&colstat).Elem()
|
||||||
newColData := &ColData{
|
newColData := &colData{
|
||||||
Name: colstat.Name,
|
Name: colstat.Name,
|
||||||
DbName: colstat.DbName,
|
DbName: colstat.DbName,
|
||||||
Fields: make(map[string]interface{}),
|
Fields: make(map[string]interface{}),
|
||||||
|
|
@ -332,11 +332,11 @@ func (d *MongodbData) AddColStats() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MongodbData) AddShardHostStats() {
|
func (d *mongodbData) addShardHostStats() {
|
||||||
for host := range d.StatLine.ShardHostStatsLines {
|
for host := range d.StatLine.ShardHostStatsLines {
|
||||||
hostStat := d.StatLine.ShardHostStatsLines[host]
|
hostStat := d.StatLine.ShardHostStatsLines[host]
|
||||||
hostStatLine := reflect.ValueOf(&hostStat).Elem()
|
hostStatLine := reflect.ValueOf(&hostStat).Elem()
|
||||||
newDbData := &DbData{
|
newDbData := &bbData{
|
||||||
Name: host,
|
Name: host,
|
||||||
Fields: make(map[string]interface{}),
|
Fields: make(map[string]interface{}),
|
||||||
}
|
}
|
||||||
|
|
@ -349,11 +349,11 @@ func (d *MongodbData) AddShardHostStats() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MongodbData) AddTopStats() {
|
func (d *mongodbData) addTopStats() {
|
||||||
for i := range d.StatLine.TopStatLines {
|
for i := range d.StatLine.TopStatLines {
|
||||||
topStat := d.StatLine.TopStatLines[i]
|
topStat := d.StatLine.TopStatLines[i]
|
||||||
topStatLine := reflect.ValueOf(&topStat).Elem()
|
topStatLine := reflect.ValueOf(&topStat).Elem()
|
||||||
newTopStatData := &DbData{
|
newTopStatData := &bbData{
|
||||||
Name: topStat.CollectionName,
|
Name: topStat.CollectionName,
|
||||||
Fields: make(map[string]interface{}),
|
Fields: make(map[string]interface{}),
|
||||||
}
|
}
|
||||||
|
|
@ -366,7 +366,7 @@ func (d *MongodbData) AddTopStats() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MongodbData) AddDefaultStats() {
|
func (d *mongodbData) addDefaultStats() {
|
||||||
statLine := reflect.ValueOf(d.StatLine).Elem()
|
statLine := reflect.ValueOf(d.StatLine).Elem()
|
||||||
d.addStat(statLine, defaultStats)
|
d.addStat(statLine, defaultStats)
|
||||||
if d.StatLine.NodeType != "" {
|
if d.StatLine.NodeType != "" {
|
||||||
|
|
@ -414,18 +414,18 @@ func (d *MongodbData) AddDefaultStats() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MongodbData) addStat(statLine reflect.Value, stats map[string]string) {
|
func (d *mongodbData) addStat(statLine reflect.Value, stats map[string]string) {
|
||||||
for key, value := range stats {
|
for key, value := range stats {
|
||||||
val := statLine.FieldByName(value).Interface()
|
val := statLine.FieldByName(value).Interface()
|
||||||
d.add(key, val)
|
d.add(key, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MongodbData) add(key string, val interface{}) {
|
func (d *mongodbData) add(key string, val interface{}) {
|
||||||
d.Fields[key] = val
|
d.Fields[key] = val
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MongodbData) flush(acc telegraf.Accumulator) {
|
func (d *mongodbData) flush(acc telegraf.Accumulator) {
|
||||||
acc.AddFields(
|
acc.AddFields(
|
||||||
"mongodb",
|
"mongodb",
|
||||||
d.Fields,
|
d.Fields,
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import (
|
||||||
var tags = make(map[string]string)
|
var tags = make(map[string]string)
|
||||||
|
|
||||||
func TestAddNonReplStats(t *testing.T) {
|
func TestAddNonReplStats(t *testing.T) {
|
||||||
d := NewMongodbData(
|
d := newMongodbData(
|
||||||
&statLine{
|
&statLine{
|
||||||
StorageEngine: "",
|
StorageEngine: "",
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
|
|
@ -62,7 +62,7 @@ func TestAddNonReplStats(t *testing.T) {
|
||||||
)
|
)
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
d.AddDefaultStats()
|
d.addDefaultStats()
|
||||||
d.flush(&acc)
|
d.flush(&acc)
|
||||||
|
|
||||||
for key := range defaultStats {
|
for key := range defaultStats {
|
||||||
|
|
@ -71,7 +71,7 @@ func TestAddNonReplStats(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddReplStats(t *testing.T) {
|
func TestAddReplStats(t *testing.T) {
|
||||||
d := NewMongodbData(
|
d := newMongodbData(
|
||||||
&statLine{
|
&statLine{
|
||||||
StorageEngine: "mmapv1",
|
StorageEngine: "mmapv1",
|
||||||
Mapped: 0,
|
Mapped: 0,
|
||||||
|
|
@ -83,7 +83,7 @@ func TestAddReplStats(t *testing.T) {
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
d.AddDefaultStats()
|
d.addDefaultStats()
|
||||||
d.flush(&acc)
|
d.flush(&acc)
|
||||||
|
|
||||||
for key := range mmapStats {
|
for key := range mmapStats {
|
||||||
|
|
@ -92,7 +92,7 @@ func TestAddReplStats(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddWiredTigerStats(t *testing.T) {
|
func TestAddWiredTigerStats(t *testing.T) {
|
||||||
d := NewMongodbData(
|
d := newMongodbData(
|
||||||
&statLine{
|
&statLine{
|
||||||
StorageEngine: "wiredTiger",
|
StorageEngine: "wiredTiger",
|
||||||
CacheDirtyPercent: 0,
|
CacheDirtyPercent: 0,
|
||||||
|
|
@ -124,7 +124,7 @@ func TestAddWiredTigerStats(t *testing.T) {
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
d.AddDefaultStats()
|
d.addDefaultStats()
|
||||||
d.flush(&acc)
|
d.flush(&acc)
|
||||||
|
|
||||||
for key := range wiredTigerStats {
|
for key := range wiredTigerStats {
|
||||||
|
|
@ -139,7 +139,7 @@ func TestAddWiredTigerStats(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddShardStats(t *testing.T) {
|
func TestAddShardStats(t *testing.T) {
|
||||||
d := NewMongodbData(
|
d := newMongodbData(
|
||||||
&statLine{
|
&statLine{
|
||||||
TotalInUse: 0,
|
TotalInUse: 0,
|
||||||
TotalAvailable: 0,
|
TotalAvailable: 0,
|
||||||
|
|
@ -151,7 +151,7 @@ func TestAddShardStats(t *testing.T) {
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
d.AddDefaultStats()
|
d.addDefaultStats()
|
||||||
d.flush(&acc)
|
d.flush(&acc)
|
||||||
|
|
||||||
for key := range defaultShardStats {
|
for key := range defaultShardStats {
|
||||||
|
|
@ -160,7 +160,7 @@ func TestAddShardStats(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddLatencyStats(t *testing.T) {
|
func TestAddLatencyStats(t *testing.T) {
|
||||||
d := NewMongodbData(
|
d := newMongodbData(
|
||||||
&statLine{
|
&statLine{
|
||||||
CommandOpsCnt: 73,
|
CommandOpsCnt: 73,
|
||||||
CommandLatency: 364,
|
CommandLatency: 364,
|
||||||
|
|
@ -174,7 +174,7 @@ func TestAddLatencyStats(t *testing.T) {
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
d.AddDefaultStats()
|
d.addDefaultStats()
|
||||||
d.flush(&acc)
|
d.flush(&acc)
|
||||||
|
|
||||||
for key := range defaultLatencyStats {
|
for key := range defaultLatencyStats {
|
||||||
|
|
@ -183,7 +183,7 @@ func TestAddLatencyStats(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddAssertsStats(t *testing.T) {
|
func TestAddAssertsStats(t *testing.T) {
|
||||||
d := NewMongodbData(
|
d := newMongodbData(
|
||||||
&statLine{
|
&statLine{
|
||||||
Regular: 3,
|
Regular: 3,
|
||||||
Warning: 9,
|
Warning: 9,
|
||||||
|
|
@ -196,7 +196,7 @@ func TestAddAssertsStats(t *testing.T) {
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
d.AddDefaultStats()
|
d.addDefaultStats()
|
||||||
d.flush(&acc)
|
d.flush(&acc)
|
||||||
|
|
||||||
for key := range defaultAssertsStats {
|
for key := range defaultAssertsStats {
|
||||||
|
|
@ -205,7 +205,7 @@ func TestAddAssertsStats(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddCommandsStats(t *testing.T) {
|
func TestAddCommandsStats(t *testing.T) {
|
||||||
d := NewMongodbData(
|
d := newMongodbData(
|
||||||
&statLine{
|
&statLine{
|
||||||
AggregateCommandTotal: 12,
|
AggregateCommandTotal: 12,
|
||||||
AggregateCommandFailed: 2,
|
AggregateCommandFailed: 2,
|
||||||
|
|
@ -231,7 +231,7 @@ func TestAddCommandsStats(t *testing.T) {
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
d.AddDefaultStats()
|
d.addDefaultStats()
|
||||||
d.flush(&acc)
|
d.flush(&acc)
|
||||||
|
|
||||||
for key := range defaultCommandsStats {
|
for key := range defaultCommandsStats {
|
||||||
|
|
@ -240,7 +240,7 @@ func TestAddCommandsStats(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddTCMallocStats(t *testing.T) {
|
func TestAddTCMallocStats(t *testing.T) {
|
||||||
d := NewMongodbData(
|
d := newMongodbData(
|
||||||
&statLine{
|
&statLine{
|
||||||
TCMallocCurrentAllocatedBytes: 5877253096,
|
TCMallocCurrentAllocatedBytes: 5877253096,
|
||||||
TCMallocHeapSize: 8067108864,
|
TCMallocHeapSize: 8067108864,
|
||||||
|
|
@ -267,7 +267,7 @@ func TestAddTCMallocStats(t *testing.T) {
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
d.AddDefaultStats()
|
d.addDefaultStats()
|
||||||
d.flush(&acc)
|
d.flush(&acc)
|
||||||
|
|
||||||
for key := range defaultTCMallocStats {
|
for key := range defaultTCMallocStats {
|
||||||
|
|
@ -276,7 +276,7 @@ func TestAddTCMallocStats(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddStorageStats(t *testing.T) {
|
func TestAddStorageStats(t *testing.T) {
|
||||||
d := NewMongodbData(
|
d := newMongodbData(
|
||||||
&statLine{
|
&statLine{
|
||||||
StorageFreelistSearchBucketExhausted: 0,
|
StorageFreelistSearchBucketExhausted: 0,
|
||||||
StorageFreelistSearchRequests: 0,
|
StorageFreelistSearchRequests: 0,
|
||||||
|
|
@ -287,7 +287,7 @@ func TestAddStorageStats(t *testing.T) {
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
d.AddDefaultStats()
|
d.addDefaultStats()
|
||||||
d.flush(&acc)
|
d.flush(&acc)
|
||||||
|
|
||||||
for key := range defaultStorageStats {
|
for key := range defaultStorageStats {
|
||||||
|
|
@ -307,7 +307,7 @@ func TestAddShardHostStats(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d := NewMongodbData(
|
d := newMongodbData(
|
||||||
&statLine{
|
&statLine{
|
||||||
ShardHostStatsLines: hostStatLines,
|
ShardHostStatsLines: hostStatLines,
|
||||||
},
|
},
|
||||||
|
|
@ -315,7 +315,7 @@ func TestAddShardHostStats(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
d.AddShardHostStats()
|
d.addShardHostStats()
|
||||||
d.flush(&acc)
|
d.flush(&acc)
|
||||||
|
|
||||||
hostsFound := make([]string, 0, len(hostStatLines))
|
hostsFound := make([]string, 0, len(hostStatLines))
|
||||||
|
|
@ -333,7 +333,7 @@ func TestAddShardHostStats(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStateTag(t *testing.T) {
|
func TestStateTag(t *testing.T) {
|
||||||
d := NewMongodbData(
|
d := newMongodbData(
|
||||||
&statLine{
|
&statLine{
|
||||||
StorageEngine: "",
|
StorageEngine: "",
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
|
|
@ -353,7 +353,7 @@ func TestStateTag(t *testing.T) {
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
d.AddDefaultStats()
|
d.addDefaultStats()
|
||||||
d.flush(&acc)
|
d.flush(&acc)
|
||||||
fields := map[string]interface{}{
|
fields := map[string]interface{}{
|
||||||
"active_reads": int64(0),
|
"active_reads": int64(0),
|
||||||
|
|
@ -524,7 +524,7 @@ func TestAddTopStats(t *testing.T) {
|
||||||
topStatLines = append(topStatLines, topStatLine)
|
topStatLines = append(topStatLines, topStatLine)
|
||||||
}
|
}
|
||||||
|
|
||||||
d := NewMongodbData(
|
d := newMongodbData(
|
||||||
&statLine{
|
&statLine{
|
||||||
TopStatLines: topStatLines,
|
TopStatLines: topStatLines,
|
||||||
},
|
},
|
||||||
|
|
@ -532,7 +532,7 @@ func TestAddTopStats(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
d.AddTopStats()
|
d.addTopStats()
|
||||||
d.flush(&acc)
|
d.flush(&acc)
|
||||||
|
|
||||||
for range topStatLines {
|
for range topStatLines {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -16,44 +17,44 @@ import (
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type server struct {
|
||||||
client *mongo.Client
|
client *mongo.Client
|
||||||
hostname string
|
hostname string
|
||||||
lastResult *mongoStatus
|
lastResult *mongoStatus
|
||||||
|
|
||||||
Log telegraf.Logger
|
log telegraf.Logger
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) getDefaultTags() map[string]string {
|
|
||||||
tags := make(map[string]string)
|
|
||||||
tags["hostname"] = s.hostname
|
|
||||||
return tags
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type oplogEntry struct {
|
type oplogEntry struct {
|
||||||
Timestamp primitive.Timestamp `bson:"ts"`
|
Timestamp primitive.Timestamp `bson:"ts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsAuthorization(err error) bool {
|
func isAuthorization(err error) bool {
|
||||||
return strings.Contains(err.Error(), "not authorized")
|
return strings.Contains(err.Error(), "not authorized")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ping() error {
|
func (s *server) getDefaultTags() map[string]string {
|
||||||
|
tags := make(map[string]string)
|
||||||
|
tags["hostname"] = s.hostname
|
||||||
|
return tags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) ping() error {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
return s.client.Ping(ctx, nil)
|
return s.client.Ping(ctx, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) authLog(err error) {
|
func (s *server) authLog(err error) {
|
||||||
if IsAuthorization(err) {
|
if isAuthorization(err) {
|
||||||
s.Log.Debug(err.Error())
|
s.log.Debug(err.Error())
|
||||||
} else {
|
} else {
|
||||||
s.Log.Error(err.Error())
|
s.log.Error(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) runCommand(database string, cmd, result interface{}) error {
|
func (s *server) runCommand(database string, cmd, result interface{}) error {
|
||||||
r := s.client.Database(database).RunCommand(context.Background(), cmd)
|
r := s.client.Database(database).RunCommand(context.Background(), cmd)
|
||||||
if r.Err() != nil {
|
if r.Err() != nil {
|
||||||
return r.Err()
|
return r.Err()
|
||||||
|
|
@ -61,7 +62,7 @@ func (s *Server) runCommand(database string, cmd, result interface{}) error {
|
||||||
return r.Decode(result)
|
return r.Decode(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) gatherServerStatus() (*serverStatus, error) {
|
func (s *server) gatherServerStatus() (*serverStatus, error) {
|
||||||
serverStatus := &serverStatus{}
|
serverStatus := &serverStatus{}
|
||||||
err := s.runCommand("admin", bson.D{
|
err := s.runCommand("admin", bson.D{
|
||||||
{
|
{
|
||||||
|
|
@ -79,7 +80,7 @@ func (s *Server) gatherServerStatus() (*serverStatus, error) {
|
||||||
return serverStatus, nil
|
return serverStatus, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) gatherReplSetStatus() (*replSetStatus, error) {
|
func (s *server) gatherReplSetStatus() (*replSetStatus, error) {
|
||||||
replSetStatus := &replSetStatus{}
|
replSetStatus := &replSetStatus{}
|
||||||
err := s.runCommand("admin", bson.D{
|
err := s.runCommand("admin", bson.D{
|
||||||
{
|
{
|
||||||
|
|
@ -93,7 +94,7 @@ func (s *Server) gatherReplSetStatus() (*replSetStatus, error) {
|
||||||
return replSetStatus, nil
|
return replSetStatus, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) gatherTopStatData() (*topStats, error) {
|
func (s *server) gatherTopStatData() (*topStats, error) {
|
||||||
var dest map[string]interface{}
|
var dest map[string]interface{}
|
||||||
err := s.runCommand("admin", bson.D{
|
err := s.runCommand("admin", bson.D{
|
||||||
{
|
{
|
||||||
|
|
@ -124,7 +125,7 @@ func (s *Server) gatherTopStatData() (*topStats, error) {
|
||||||
return &topStats{Totals: topInfo}, nil
|
return &topStats{Totals: topInfo}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) gatherClusterStatus() (*clusterStatus, error) {
|
func (s *server) gatherClusterStatus() (*clusterStatus, error) {
|
||||||
chunkCount, err := s.client.Database("config").Collection("chunks").CountDocuments(context.Background(), bson.M{"jumbo": true})
|
chunkCount, err := s.client.Database("config").Collection("chunks").CountDocuments(context.Background(), bson.M{"jumbo": true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -148,7 +149,7 @@ func poolStatsCommand(version string) (string, error) {
|
||||||
return "shardConnPoolStats", nil
|
return "shardConnPoolStats", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) gatherShardConnPoolStats(version string) (*shardStats, error) {
|
func (s *server) gatherShardConnPoolStats(version string) (*shardStats, error) {
|
||||||
command, err := poolStatsCommand(version)
|
command, err := poolStatsCommand(version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -167,7 +168,7 @@ func (s *Server) gatherShardConnPoolStats(version string) (*shardStats, error) {
|
||||||
return shardStats, nil
|
return shardStats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) gatherDBStats(name string) (*db, error) {
|
func (s *server) gatherDBStats(name string) (*db, error) {
|
||||||
stats := &dbStatsData{}
|
stats := &dbStatsData{}
|
||||||
err := s.runCommand(name, bson.D{
|
err := s.runCommand(name, bson.D{
|
||||||
{
|
{
|
||||||
|
|
@ -185,7 +186,7 @@ func (s *Server) gatherDBStats(name string) (*db, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) getOplogReplLag(collection string) (*oplogStats, error) {
|
func (s *server) getOplogReplLag(collection string) (*oplogStats, error) {
|
||||||
query := bson.M{"ts": bson.M{"$exists": true}}
|
query := bson.M{"ts": bson.M{"$exists": true}}
|
||||||
|
|
||||||
var first oplogEntry
|
var first oplogEntry
|
||||||
|
|
@ -219,7 +220,7 @@ func (s *Server) getOplogReplLag(collection string) (*oplogStats, error) {
|
||||||
// The "oplog.$main" collection is created on the master node of a
|
// The "oplog.$main" collection is created on the master node of a
|
||||||
// master-slave replicated deployment. As of MongoDB 3.2, master-slave
|
// master-slave replicated deployment. As of MongoDB 3.2, master-slave
|
||||||
// replication has been deprecated.
|
// replication has been deprecated.
|
||||||
func (s *Server) gatherOplogStats() (*oplogStats, error) {
|
func (s *server) gatherOplogStats() (*oplogStats, error) {
|
||||||
stats, err := s.getOplogReplLag("oplog.rs")
|
stats, err := s.getOplogReplLag("oplog.rs")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return stats, nil
|
return stats, nil
|
||||||
|
|
@ -228,7 +229,7 @@ func (s *Server) gatherOplogStats() (*oplogStats, error) {
|
||||||
return s.getOplogReplLag("oplog.$main")
|
return s.getOplogReplLag("oplog.$main")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) gatherCollectionStats(colStatsDbs []string) (*colStats, error) {
|
func (s *server) gatherCollectionStats(colStatsDbs []string) (*colStats, error) {
|
||||||
names, err := s.client.ListDatabaseNames(context.Background(), bson.D{})
|
names, err := s.client.ListDatabaseNames(context.Background(), bson.D{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -236,14 +237,14 @@ func (s *Server) gatherCollectionStats(colStatsDbs []string) (*colStats, error)
|
||||||
|
|
||||||
results := &colStats{}
|
results := &colStats{}
|
||||||
for _, dbName := range names {
|
for _, dbName := range names {
|
||||||
if stringInSlice(dbName, colStatsDbs) || len(colStatsDbs) == 0 {
|
if slices.Contains(colStatsDbs, dbName) || len(colStatsDbs) == 0 {
|
||||||
// skip views as they fail on collStats below
|
// skip views as they fail on collStats below
|
||||||
filter := bson.M{"type": bson.M{"$in": bson.A{"collection", "timeseries"}}}
|
filter := bson.M{"type": bson.M{"$in": bson.A{"collection", "timeseries"}}}
|
||||||
|
|
||||||
var colls []string
|
var colls []string
|
||||||
colls, err = s.client.Database(dbName).ListCollectionNames(context.Background(), filter)
|
colls, err = s.client.Database(dbName).ListCollectionNames(context.Background(), filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Error getting collection names: %s", err.Error())
|
s.log.Errorf("Error getting collection names: %s", err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, colName := range colls {
|
for _, colName := range colls {
|
||||||
|
|
@ -270,7 +271,7 @@ func (s *Server) gatherCollectionStats(colStatsDbs []string) (*colStats, error)
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) gatherData(acc telegraf.Accumulator, gatherClusterStatus, gatherDbStats, gatherColStats, gatherTopStat bool, colStatsDbs []string) error {
|
func (s *server) gatherData(acc telegraf.Accumulator, gatherClusterStatus, gatherDbStats, gatherColStats, gatherTopStat bool, colStatsDbs []string) error {
|
||||||
serverStatus, err := s.gatherServerStatus()
|
serverStatus, err := s.gatherServerStatus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -280,7 +281,7 @@ func (s *Server) gatherData(acc telegraf.Accumulator, gatherClusterStatus, gathe
|
||||||
// member of a replica set.
|
// member of a replica set.
|
||||||
replSetStatus, err := s.gatherReplSetStatus()
|
replSetStatus, err := s.gatherReplSetStatus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Debugf("Unable to gather replica set status: %s", err.Error())
|
s.log.Debugf("Unable to gather replica set status: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather the oplog if we are a member of a replica set. Non-replica set
|
// Gather the oplog if we are a member of a replica set. Non-replica set
|
||||||
|
|
@ -297,7 +298,7 @@ func (s *Server) gatherData(acc telegraf.Accumulator, gatherClusterStatus, gathe
|
||||||
if gatherClusterStatus {
|
if gatherClusterStatus {
|
||||||
status, err := s.gatherClusterStatus()
|
status, err := s.gatherClusterStatus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Debugf("Unable to gather cluster status: %s", err.Error())
|
s.log.Debugf("Unable to gather cluster status: %s", err.Error())
|
||||||
}
|
}
|
||||||
clusterStatus = status
|
clusterStatus = status
|
||||||
}
|
}
|
||||||
|
|
@ -326,7 +327,7 @@ func (s *Server) gatherData(acc telegraf.Accumulator, gatherClusterStatus, gathe
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
db, err := s.gatherDBStats(name)
|
db, err := s.gatherDBStats(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Debugf("Error getting db stats from %q: %s", name, err.Error())
|
s.log.Debugf("Error getting db stats from %q: %s", name, err.Error())
|
||||||
}
|
}
|
||||||
dbStats.Dbs = append(dbStats.Dbs, *db)
|
dbStats.Dbs = append(dbStats.Dbs, *db)
|
||||||
}
|
}
|
||||||
|
|
@ -336,7 +337,7 @@ func (s *Server) gatherData(acc telegraf.Accumulator, gatherClusterStatus, gathe
|
||||||
if gatherTopStat {
|
if gatherTopStat {
|
||||||
topStats, err := s.gatherTopStatData()
|
topStats, err := s.gatherTopStatData()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Debugf("Unable to gather top stat data: %s", err.Error())
|
s.log.Debugf("Unable to gather top stat data: %s", err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
topStatData = topStats
|
topStatData = topStats
|
||||||
|
|
@ -360,27 +361,18 @@ func (s *Server) gatherData(acc telegraf.Accumulator, gatherClusterStatus, gathe
|
||||||
if durationInSeconds == 0 {
|
if durationInSeconds == 0 {
|
||||||
durationInSeconds = 1
|
durationInSeconds = 1
|
||||||
}
|
}
|
||||||
data := NewMongodbData(
|
data := newMongodbData(
|
||||||
NewStatLine(*s.lastResult, *result, s.hostname, true, durationInSeconds),
|
newStatLine(*s.lastResult, *result, s.hostname, durationInSeconds),
|
||||||
s.getDefaultTags(),
|
s.getDefaultTags(),
|
||||||
)
|
)
|
||||||
data.AddDefaultStats()
|
data.addDefaultStats()
|
||||||
data.AddDbStats()
|
data.addDbStats()
|
||||||
data.AddColStats()
|
data.addColStats()
|
||||||
data.AddShardHostStats()
|
data.addShardHostStats()
|
||||||
data.AddTopStats()
|
data.addTopStats()
|
||||||
data.flush(acc)
|
data.flush(acc)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.lastResult = result
|
s.lastResult = result
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func stringInSlice(a string, list []string) bool {
|
|
||||||
for _, b := range list {
|
|
||||||
if b == a {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,15 @@ import (
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ServicePort = "27017"
|
var servicePort = "27017"
|
||||||
var unreachableMongoEndpoint = "mongodb://user:pass@127.0.0.1:27017/nop"
|
var unreachableMongoEndpoint = "mongodb://user:pass@127.0.0.1:27017/nop"
|
||||||
|
|
||||||
func createTestServer(t *testing.T) *testutil.Container {
|
func createTestServer(t *testing.T) *testutil.Container {
|
||||||
container := testutil.Container{
|
container := testutil.Container{
|
||||||
Image: "mongo",
|
Image: "mongo",
|
||||||
ExposedPorts: []string{ServicePort},
|
ExposedPorts: []string{servicePort},
|
||||||
WaitingFor: wait.ForAll(
|
WaitingFor: wait.ForAll(
|
||||||
wait.NewHTTPStrategy("/").WithPort(nat.Port(ServicePort)),
|
wait.NewHTTPStrategy("/").WithPort(nat.Port(servicePort)),
|
||||||
wait.ForLog("Waiting for connections"),
|
wait.ForLog("Waiting for connections"),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
@ -40,7 +40,7 @@ func TestGetDefaultTagsIntegration(t *testing.T) {
|
||||||
m := &MongoDB{
|
m := &MongoDB{
|
||||||
Log: testutil.Logger{},
|
Log: testutil.Logger{},
|
||||||
Servers: []string{
|
Servers: []string{
|
||||||
fmt.Sprintf("mongodb://%s:%s", container.Address, container.Ports[ServicePort]),
|
fmt.Sprintf("mongodb://%s:%s", container.Address, container.Ports[servicePort]),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := m.Init()
|
err := m.Init()
|
||||||
|
|
@ -76,7 +76,7 @@ func TestAddDefaultStatsIntegration(t *testing.T) {
|
||||||
m := &MongoDB{
|
m := &MongoDB{
|
||||||
Log: testutil.Logger{},
|
Log: testutil.Logger{},
|
||||||
Servers: []string{
|
Servers: []string{
|
||||||
fmt.Sprintf("mongodb://%s:%s", container.Address, container.Ports[ServicePort]),
|
fmt.Sprintf("mongodb://%s:%s", container.Address, container.Ports[servicePort]),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := m.Init()
|
err := m.Init()
|
||||||
|
|
|
||||||
|
|
@ -12,18 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MongosProcess = "mongos"
|
mongosProcess = "mongos"
|
||||||
)
|
|
||||||
|
|
||||||
// Flags to determine cases when to activate/deactivate columns for output.
|
|
||||||
const (
|
|
||||||
Always = 1 << iota // always activate the column
|
|
||||||
Discover // only active when mongostat is in discover mode
|
|
||||||
Repl // only active if one of the nodes being monitored is in a replset
|
|
||||||
Locks // only active if node is capable of calculating lock info
|
|
||||||
AllOnly // only active if mongostat was run with --all option
|
|
||||||
MMAPOnly // only active if node has mmap-specific fields
|
|
||||||
WTOnly // only active if node has wiredtiger-specific fields
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type mongoStatus struct {
|
type mongoStatus struct {
|
||||||
|
|
@ -557,48 +546,6 @@ type storageStats struct {
|
||||||
FreelistSearchScanned int64 `bson:"freelist.search.scanned"`
|
FreelistSearchScanned int64 `bson:"freelist.search.scanned"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// statHeader describes a single column for mongostat's terminal output, its formatting, and in which modes it should be displayed.
|
|
||||||
type statHeader struct {
|
|
||||||
// The text to appear in the column's header cell
|
|
||||||
HeaderText string
|
|
||||||
|
|
||||||
// Bitmask containing flags to determine if this header is active or not
|
|
||||||
ActivateFlags int
|
|
||||||
}
|
|
||||||
|
|
||||||
// StatHeaders are the complete set of data metrics supported by mongostat.
|
|
||||||
var StatHeaders = []statHeader{
|
|
||||||
{"", Always}, // placeholder for hostname column (blank header text)
|
|
||||||
{"insert", Always},
|
|
||||||
{"query", Always},
|
|
||||||
{"update", Always},
|
|
||||||
{"delete", Always},
|
|
||||||
{"getmore", Always},
|
|
||||||
{"command", Always},
|
|
||||||
{"% dirty", WTOnly},
|
|
||||||
{"% used", WTOnly},
|
|
||||||
{"flushes", Always},
|
|
||||||
{"mapped", MMAPOnly},
|
|
||||||
{"vsize", Always},
|
|
||||||
{"res", Always},
|
|
||||||
{"non-mapped", MMAPOnly | AllOnly},
|
|
||||||
{"faults", MMAPOnly},
|
|
||||||
{"lr|lw %", MMAPOnly | AllOnly},
|
|
||||||
{"lrt|lwt", MMAPOnly | AllOnly},
|
|
||||||
{" locked db", Locks},
|
|
||||||
{"qr|qw", Always},
|
|
||||||
{"ar|aw", Always},
|
|
||||||
{"netIn", Always},
|
|
||||||
{"netOut", Always},
|
|
||||||
{"conn", Always},
|
|
||||||
{"set", Repl},
|
|
||||||
{"repl", Repl},
|
|
||||||
{"time", Always},
|
|
||||||
}
|
|
||||||
|
|
||||||
// NamespacedLocks stores information on the lockStatus of namespaces.
|
|
||||||
type NamespacedLocks map[string]lockStatus
|
|
||||||
|
|
||||||
// lockUsage stores information related to a namespace's lock usage.
|
// lockUsage stores information related to a namespace's lock usage.
|
||||||
type lockUsage struct {
|
type lockUsage struct {
|
||||||
Namespace string
|
Namespace string
|
||||||
|
|
@ -931,8 +878,8 @@ func diff(newVal, oldVal, sampleTime int64) (avg, newValue int64) {
|
||||||
return d / sampleTime, newVal
|
return d / sampleTime, newVal
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStatLine constructs a statLine object from two mongoStatus objects.
|
// newStatLine constructs a statLine object from two mongoStatus objects.
|
||||||
func NewStatLine(oldMongo, newMongo mongoStatus, key string, all bool, sampleSecs int64) *statLine {
|
func newStatLine(oldMongo, newMongo mongoStatus, key string, sampleSecs int64) *statLine {
|
||||||
oldStat := *oldMongo.ServerStatus
|
oldStat := *oldMongo.ServerStatus
|
||||||
newStat := *newMongo.ServerStatus
|
newStat := *newMongo.ServerStatus
|
||||||
|
|
||||||
|
|
@ -1179,7 +1126,7 @@ func NewStatLine(oldMongo, newMongo mongoStatus, key string, all bool, sampleSec
|
||||||
|
|
||||||
returnVal.Time = newMongo.SampleTime
|
returnVal.Time = newMongo.SampleTime
|
||||||
returnVal.IsMongos =
|
returnVal.IsMongos =
|
||||||
newStat.ShardCursorType != nil || strings.HasPrefix(newStat.Process, MongosProcess)
|
newStat.ShardCursorType != nil || strings.HasPrefix(newStat.Process, mongosProcess)
|
||||||
|
|
||||||
// BEGIN code modification
|
// BEGIN code modification
|
||||||
if oldStat.Mem.Supported.(bool) {
|
if oldStat.Mem.Supported.(bool) {
|
||||||
|
|
@ -1190,7 +1137,7 @@ func NewStatLine(oldMongo, newMongo mongoStatus, key string, all bool, sampleSec
|
||||||
returnVal.Virtual = newStat.Mem.Virtual
|
returnVal.Virtual = newStat.Mem.Virtual
|
||||||
returnVal.Resident = newStat.Mem.Resident
|
returnVal.Resident = newStat.Mem.Resident
|
||||||
|
|
||||||
if !returnVal.IsMongos && all {
|
if !returnVal.IsMongos {
|
||||||
returnVal.NonMapped = newStat.Mem.Virtual - newStat.Mem.Mapped
|
returnVal.NonMapped = newStat.Mem.Virtual - newStat.Mem.Mapped
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLatencyStats(t *testing.T) {
|
func TestLatencyStats(t *testing.T) {
|
||||||
sl := NewStatLine(
|
sl := newStatLine(
|
||||||
mongoStatus{
|
mongoStatus{
|
||||||
ServerStatus: &serverStatus{
|
ServerStatus: &serverStatus{
|
||||||
Connections: &connectionStats{},
|
Connections: &connectionStats{},
|
||||||
|
|
@ -49,7 +49,6 @@ func TestLatencyStats(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"foo",
|
"foo",
|
||||||
true,
|
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -62,7 +61,7 @@ func TestLatencyStats(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLatencyStatsDiffZero(t *testing.T) {
|
func TestLatencyStatsDiffZero(t *testing.T) {
|
||||||
sl := NewStatLine(
|
sl := newStatLine(
|
||||||
mongoStatus{
|
mongoStatus{
|
||||||
ServerStatus: &serverStatus{
|
ServerStatus: &serverStatus{
|
||||||
Connections: &connectionStats{},
|
Connections: &connectionStats{},
|
||||||
|
|
@ -118,7 +117,6 @@ func TestLatencyStatsDiffZero(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"foo",
|
"foo",
|
||||||
true,
|
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -131,7 +129,7 @@ func TestLatencyStatsDiffZero(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLatencyStatsDiff(t *testing.T) {
|
func TestLatencyStatsDiff(t *testing.T) {
|
||||||
sl := NewStatLine(
|
sl := newStatLine(
|
||||||
mongoStatus{
|
mongoStatus{
|
||||||
ServerStatus: &serverStatus{
|
ServerStatus: &serverStatus{
|
||||||
Connections: &connectionStats{},
|
Connections: &connectionStats{},
|
||||||
|
|
@ -187,7 +185,6 @@ func TestLatencyStatsDiff(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"foo",
|
"foo",
|
||||||
true,
|
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -200,7 +197,7 @@ func TestLatencyStatsDiff(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocksStatsNilWhenLocksMissingInOldStat(t *testing.T) {
|
func TestLocksStatsNilWhenLocksMissingInOldStat(t *testing.T) {
|
||||||
sl := NewStatLine(
|
sl := newStatLine(
|
||||||
mongoStatus{
|
mongoStatus{
|
||||||
ServerStatus: &serverStatus{
|
ServerStatus: &serverStatus{
|
||||||
Connections: &connectionStats{},
|
Connections: &connectionStats{},
|
||||||
|
|
@ -223,7 +220,6 @@ func TestLocksStatsNilWhenLocksMissingInOldStat(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"foo",
|
"foo",
|
||||||
true,
|
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -231,7 +227,7 @@ func TestLocksStatsNilWhenLocksMissingInOldStat(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocksStatsNilWhenGlobalLockStatsMissingInOldStat(t *testing.T) {
|
func TestLocksStatsNilWhenGlobalLockStatsMissingInOldStat(t *testing.T) {
|
||||||
sl := NewStatLine(
|
sl := newStatLine(
|
||||||
mongoStatus{
|
mongoStatus{
|
||||||
ServerStatus: &serverStatus{
|
ServerStatus: &serverStatus{
|
||||||
Connections: &connectionStats{},
|
Connections: &connectionStats{},
|
||||||
|
|
@ -255,7 +251,6 @@ func TestLocksStatsNilWhenGlobalLockStatsMissingInOldStat(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"foo",
|
"foo",
|
||||||
true,
|
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -263,7 +258,7 @@ func TestLocksStatsNilWhenGlobalLockStatsMissingInOldStat(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocksStatsNilWhenGlobalLockStatsEmptyInOldStat(t *testing.T) {
|
func TestLocksStatsNilWhenGlobalLockStatsEmptyInOldStat(t *testing.T) {
|
||||||
sl := NewStatLine(
|
sl := newStatLine(
|
||||||
mongoStatus{
|
mongoStatus{
|
||||||
ServerStatus: &serverStatus{
|
ServerStatus: &serverStatus{
|
||||||
Connections: &connectionStats{},
|
Connections: &connectionStats{},
|
||||||
|
|
@ -289,7 +284,6 @@ func TestLocksStatsNilWhenGlobalLockStatsEmptyInOldStat(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"foo",
|
"foo",
|
||||||
true,
|
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -297,7 +291,7 @@ func TestLocksStatsNilWhenGlobalLockStatsEmptyInOldStat(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocksStatsNilWhenCollectionLockStatsMissingInOldStat(t *testing.T) {
|
func TestLocksStatsNilWhenCollectionLockStatsMissingInOldStat(t *testing.T) {
|
||||||
sl := NewStatLine(
|
sl := newStatLine(
|
||||||
mongoStatus{
|
mongoStatus{
|
||||||
ServerStatus: &serverStatus{
|
ServerStatus: &serverStatus{
|
||||||
Connections: &connectionStats{},
|
Connections: &connectionStats{},
|
||||||
|
|
@ -325,7 +319,6 @@ func TestLocksStatsNilWhenCollectionLockStatsMissingInOldStat(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"foo",
|
"foo",
|
||||||
true,
|
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -333,7 +326,7 @@ func TestLocksStatsNilWhenCollectionLockStatsMissingInOldStat(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocksStatsNilWhenCollectionLockStatsEmptyInOldStat(t *testing.T) {
|
func TestLocksStatsNilWhenCollectionLockStatsEmptyInOldStat(t *testing.T) {
|
||||||
sl := NewStatLine(
|
sl := newStatLine(
|
||||||
mongoStatus{
|
mongoStatus{
|
||||||
ServerStatus: &serverStatus{
|
ServerStatus: &serverStatus{
|
||||||
Connections: &connectionStats{},
|
Connections: &connectionStats{},
|
||||||
|
|
@ -362,7 +355,6 @@ func TestLocksStatsNilWhenCollectionLockStatsEmptyInOldStat(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"foo",
|
"foo",
|
||||||
true,
|
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -370,7 +362,7 @@ func TestLocksStatsNilWhenCollectionLockStatsEmptyInOldStat(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocksStatsNilWhenLocksMissingInNewStat(t *testing.T) {
|
func TestLocksStatsNilWhenLocksMissingInNewStat(t *testing.T) {
|
||||||
sl := NewStatLine(
|
sl := newStatLine(
|
||||||
mongoStatus{
|
mongoStatus{
|
||||||
ServerStatus: &serverStatus{
|
ServerStatus: &serverStatus{
|
||||||
Connections: &connectionStats{},
|
Connections: &connectionStats{},
|
||||||
|
|
@ -393,7 +385,6 @@ func TestLocksStatsNilWhenLocksMissingInNewStat(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"foo",
|
"foo",
|
||||||
true,
|
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -401,7 +392,7 @@ func TestLocksStatsNilWhenLocksMissingInNewStat(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocksStatsNilWhenGlobalLockStatsMissingInNewStat(t *testing.T) {
|
func TestLocksStatsNilWhenGlobalLockStatsMissingInNewStat(t *testing.T) {
|
||||||
sl := NewStatLine(
|
sl := newStatLine(
|
||||||
mongoStatus{
|
mongoStatus{
|
||||||
ServerStatus: &serverStatus{
|
ServerStatus: &serverStatus{
|
||||||
Connections: &connectionStats{},
|
Connections: &connectionStats{},
|
||||||
|
|
@ -425,7 +416,6 @@ func TestLocksStatsNilWhenGlobalLockStatsMissingInNewStat(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"foo",
|
"foo",
|
||||||
true,
|
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -433,7 +423,7 @@ func TestLocksStatsNilWhenGlobalLockStatsMissingInNewStat(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocksStatsNilWhenGlobalLockStatsEmptyInNewStat(t *testing.T) {
|
func TestLocksStatsNilWhenGlobalLockStatsEmptyInNewStat(t *testing.T) {
|
||||||
sl := NewStatLine(
|
sl := newStatLine(
|
||||||
mongoStatus{
|
mongoStatus{
|
||||||
ServerStatus: &serverStatus{
|
ServerStatus: &serverStatus{
|
||||||
Connections: &connectionStats{},
|
Connections: &connectionStats{},
|
||||||
|
|
@ -459,7 +449,6 @@ func TestLocksStatsNilWhenGlobalLockStatsEmptyInNewStat(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"foo",
|
"foo",
|
||||||
true,
|
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -467,7 +456,7 @@ func TestLocksStatsNilWhenGlobalLockStatsEmptyInNewStat(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocksStatsNilWhenCollectionLockStatsMissingInNewStat(t *testing.T) {
|
func TestLocksStatsNilWhenCollectionLockStatsMissingInNewStat(t *testing.T) {
|
||||||
sl := NewStatLine(
|
sl := newStatLine(
|
||||||
mongoStatus{
|
mongoStatus{
|
||||||
ServerStatus: &serverStatus{
|
ServerStatus: &serverStatus{
|
||||||
Connections: &connectionStats{},
|
Connections: &connectionStats{},
|
||||||
|
|
@ -495,7 +484,6 @@ func TestLocksStatsNilWhenCollectionLockStatsMissingInNewStat(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"foo",
|
"foo",
|
||||||
true,
|
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -503,7 +491,7 @@ func TestLocksStatsNilWhenCollectionLockStatsMissingInNewStat(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocksStatsNilWhenCollectionLockStatsEmptyInNewStat(t *testing.T) {
|
func TestLocksStatsNilWhenCollectionLockStatsEmptyInNewStat(t *testing.T) {
|
||||||
sl := NewStatLine(
|
sl := newStatLine(
|
||||||
mongoStatus{
|
mongoStatus{
|
||||||
ServerStatus: &serverStatus{
|
ServerStatus: &serverStatus{
|
||||||
Connections: &connectionStats{},
|
Connections: &connectionStats{},
|
||||||
|
|
@ -532,7 +520,6 @@ func TestLocksStatsNilWhenCollectionLockStatsEmptyInNewStat(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"foo",
|
"foo",
|
||||||
true,
|
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -540,7 +527,7 @@ func TestLocksStatsNilWhenCollectionLockStatsEmptyInNewStat(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocksStatsPopulated(t *testing.T) {
|
func TestLocksStatsPopulated(t *testing.T) {
|
||||||
sl := NewStatLine(
|
sl := newStatLine(
|
||||||
mongoStatus{
|
mongoStatus{
|
||||||
ServerStatus: &serverStatus{
|
ServerStatus: &serverStatus{
|
||||||
Connections: &connectionStats{},
|
Connections: &connectionStats{},
|
||||||
|
|
@ -596,7 +583,6 @@ func TestLocksStatsPopulated(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"foo",
|
"foo",
|
||||||
true,
|
|
||||||
60,
|
60,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ import (
|
||||||
//go:embed sample.conf
|
//go:embed sample.conf
|
||||||
var sampleConfig string
|
var sampleConfig string
|
||||||
|
|
||||||
|
var pendingActions = []string{"ignore", "alert", "restart", "stop", "exec", "unmonitor", "start", "monitor"}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
fileSystem = "0"
|
fileSystem = "0"
|
||||||
directory = "1"
|
directory = "1"
|
||||||
|
|
@ -31,7 +33,14 @@ const (
|
||||||
network = "8"
|
network = "8"
|
||||||
)
|
)
|
||||||
|
|
||||||
var pendingActions = []string{"ignore", "alert", "restart", "stop", "exec", "unmonitor", "start", "monitor"}
|
type Monit struct {
|
||||||
|
Address string `toml:"address"`
|
||||||
|
Username string `toml:"username"`
|
||||||
|
Password string `toml:"password"`
|
||||||
|
Timeout config.Duration `toml:"timeout"`
|
||||||
|
client http.Client
|
||||||
|
tls.ClientConfig
|
||||||
|
}
|
||||||
|
|
||||||
type status struct {
|
type status struct {
|
||||||
Server server `xml:"server"`
|
Server server `xml:"server"`
|
||||||
|
|
@ -179,15 +188,6 @@ type system struct {
|
||||||
} `xml:"swap"`
|
} `xml:"swap"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Monit struct {
|
|
||||||
Address string `toml:"address"`
|
|
||||||
Username string `toml:"username"`
|
|
||||||
Password string `toml:"password"`
|
|
||||||
client http.Client
|
|
||||||
tls.ClientConfig
|
|
||||||
Timeout config.Duration `toml:"timeout"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Monit) SampleConfig() string {
|
func (*Monit) SampleConfig() string {
|
||||||
return sampleConfig
|
return sampleConfig
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,32 +24,18 @@ import (
|
||||||
//go:embed sample.conf
|
//go:embed sample.conf
|
||||||
var sampleConfig string
|
var sampleConfig string
|
||||||
|
|
||||||
var once sync.Once
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
once sync.Once
|
||||||
// 30 Seconds is the default used by paho.mqtt.golang
|
// 30 Seconds is the default used by paho.mqtt.golang
|
||||||
defaultConnectionTimeout = config.Duration(30 * time.Second)
|
defaultConnectionTimeout = config.Duration(30 * time.Second)
|
||||||
defaultMaxUndeliveredMessages = 1000
|
defaultMaxUndeliveredMessages = 1000
|
||||||
)
|
)
|
||||||
|
|
||||||
type empty struct{}
|
|
||||||
type semaphore chan empty
|
|
||||||
|
|
||||||
type Client interface {
|
|
||||||
Connect() mqtt.Token
|
|
||||||
SubscribeMultiple(filters map[string]byte, callback mqtt.MessageHandler) mqtt.Token
|
|
||||||
AddRoute(topic string, callback mqtt.MessageHandler)
|
|
||||||
Disconnect(quiesce uint)
|
|
||||||
IsConnected() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClientFactory func(o *mqtt.ClientOptions) Client
|
|
||||||
|
|
||||||
type MQTTConsumer struct {
|
type MQTTConsumer struct {
|
||||||
Servers []string `toml:"servers"`
|
Servers []string `toml:"servers"`
|
||||||
Topics []string `toml:"topics"`
|
Topics []string `toml:"topics"`
|
||||||
TopicTag *string `toml:"topic_tag"`
|
TopicTag *string `toml:"topic_tag"`
|
||||||
TopicParserConfig []TopicParsingConfig `toml:"topic_parsing"`
|
TopicParserConfig []topicParsingConfig `toml:"topic_parsing"`
|
||||||
Username config.Secret `toml:"username"`
|
Username config.Secret `toml:"username"`
|
||||||
Password config.Secret `toml:"password"`
|
Password config.Secret `toml:"password"`
|
||||||
QoS int `toml:"qos"`
|
QoS int `toml:"qos"`
|
||||||
|
|
@ -64,15 +50,15 @@ type MQTTConsumer struct {
|
||||||
tls.ClientConfig
|
tls.ClientConfig
|
||||||
|
|
||||||
parser telegraf.Parser
|
parser telegraf.Parser
|
||||||
clientFactory ClientFactory
|
clientFactory clientFactory
|
||||||
client Client
|
client client
|
||||||
opts *mqtt.ClientOptions
|
opts *mqtt.ClientOptions
|
||||||
acc telegraf.TrackingAccumulator
|
acc telegraf.TrackingAccumulator
|
||||||
sem semaphore
|
sem semaphore
|
||||||
messages map[telegraf.TrackingID]mqtt.Message
|
messages map[telegraf.TrackingID]mqtt.Message
|
||||||
messagesMutex sync.Mutex
|
messagesMutex sync.Mutex
|
||||||
topicTagParse string
|
topicTagParse string
|
||||||
topicParsers []*TopicParser
|
topicParsers []*topicParser
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
payloadSize selfstat.Stat
|
payloadSize selfstat.Stat
|
||||||
|
|
@ -80,13 +66,22 @@ type MQTTConsumer struct {
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type client interface {
|
||||||
|
Connect() mqtt.Token
|
||||||
|
SubscribeMultiple(filters map[string]byte, callback mqtt.MessageHandler) mqtt.Token
|
||||||
|
AddRoute(topic string, callback mqtt.MessageHandler)
|
||||||
|
Disconnect(quiesce uint)
|
||||||
|
IsConnected() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type empty struct{}
|
||||||
|
type semaphore chan empty
|
||||||
|
type clientFactory func(o *mqtt.ClientOptions) client
|
||||||
|
|
||||||
func (*MQTTConsumer) SampleConfig() string {
|
func (*MQTTConsumer) SampleConfig() string {
|
||||||
return sampleConfig
|
return sampleConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MQTTConsumer) SetParser(parser telegraf.Parser) {
|
|
||||||
m.parser = parser
|
|
||||||
}
|
|
||||||
func (m *MQTTConsumer) Init() error {
|
func (m *MQTTConsumer) Init() error {
|
||||||
if m.ClientTrace {
|
if m.ClientTrace {
|
||||||
log := &mqttLogger{m.Log}
|
log := &mqttLogger{m.Log}
|
||||||
|
|
@ -116,9 +111,9 @@ func (m *MQTTConsumer) Init() error {
|
||||||
m.opts = opts
|
m.opts = opts
|
||||||
m.messages = make(map[telegraf.TrackingID]mqtt.Message)
|
m.messages = make(map[telegraf.TrackingID]mqtt.Message)
|
||||||
|
|
||||||
m.topicParsers = make([]*TopicParser, 0, len(m.TopicParserConfig))
|
m.topicParsers = make([]*topicParser, 0, len(m.TopicParserConfig))
|
||||||
for _, cfg := range m.TopicParserConfig {
|
for _, cfg := range m.TopicParserConfig {
|
||||||
p, err := cfg.NewParser()
|
p, err := cfg.newParser()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("config error topic parsing: %w", err)
|
return fmt.Errorf("config error topic parsing: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -129,6 +124,11 @@ func (m *MQTTConsumer) Init() error {
|
||||||
m.messagesRecv = selfstat.Register("mqtt_consumer", "messages_received", make(map[string]string))
|
m.messagesRecv = selfstat.Register("mqtt_consumer", "messages_received", make(map[string]string))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MQTTConsumer) SetParser(parser telegraf.Parser) {
|
||||||
|
m.parser = parser
|
||||||
|
}
|
||||||
|
|
||||||
func (m *MQTTConsumer) Start(acc telegraf.Accumulator) error {
|
func (m *MQTTConsumer) Start(acc telegraf.Accumulator) error {
|
||||||
m.acc = acc.WithTracking(m.MaxUndeliveredMessages)
|
m.acc = acc.WithTracking(m.MaxUndeliveredMessages)
|
||||||
m.sem = make(semaphore, m.MaxUndeliveredMessages)
|
m.sem = make(semaphore, m.MaxUndeliveredMessages)
|
||||||
|
|
@ -149,6 +149,26 @@ func (m *MQTTConsumer) Start(acc telegraf.Accumulator) error {
|
||||||
|
|
||||||
return m.connect()
|
return m.connect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MQTTConsumer) Gather(_ telegraf.Accumulator) error {
|
||||||
|
if !m.client.IsConnected() {
|
||||||
|
m.Log.Debugf("Connecting %v", m.Servers)
|
||||||
|
return m.connect()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MQTTConsumer) Stop() {
|
||||||
|
if m.client.IsConnected() {
|
||||||
|
m.Log.Debugf("Disconnecting %v", m.Servers)
|
||||||
|
m.client.Disconnect(200)
|
||||||
|
m.Log.Debugf("Disconnected %v", m.Servers)
|
||||||
|
}
|
||||||
|
if m.cancel != nil {
|
||||||
|
m.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (m *MQTTConsumer) connect() error {
|
func (m *MQTTConsumer) connect() error {
|
||||||
m.client = m.clientFactory(m.opts)
|
m.client = m.clientFactory(m.opts)
|
||||||
// AddRoute sets up the function for handling messages. These need to be
|
// AddRoute sets up the function for handling messages. These need to be
|
||||||
|
|
@ -196,6 +216,7 @@ func (m *MQTTConsumer) connect() error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MQTTConsumer) onConnectionLost(_ mqtt.Client, err error) {
|
func (m *MQTTConsumer) onConnectionLost(_ mqtt.Client, err error) {
|
||||||
// Should already be disconnected, but make doubly sure
|
// Should already be disconnected, but make doubly sure
|
||||||
m.client.Disconnect(5)
|
m.client.Disconnect(5)
|
||||||
|
|
@ -250,7 +271,7 @@ func (m *MQTTConsumer) onMessage(_ mqtt.Client, msg mqtt.Message) {
|
||||||
metric.AddTag(m.topicTagParse, msg.Topic())
|
metric.AddTag(m.topicTagParse, msg.Topic())
|
||||||
}
|
}
|
||||||
for _, p := range m.topicParsers {
|
for _, p := range m.topicParsers {
|
||||||
if err := p.Parse(metric, msg.Topic()); err != nil {
|
if err := p.parse(metric, msg.Topic()); err != nil {
|
||||||
if m.PersistentSession {
|
if m.PersistentSession {
|
||||||
msg.Ack()
|
msg.Ack()
|
||||||
}
|
}
|
||||||
|
|
@ -265,23 +286,7 @@ func (m *MQTTConsumer) onMessage(_ mqtt.Client, msg mqtt.Message) {
|
||||||
m.messages[id] = msg
|
m.messages[id] = msg
|
||||||
m.messagesMutex.Unlock()
|
m.messagesMutex.Unlock()
|
||||||
}
|
}
|
||||||
func (m *MQTTConsumer) Stop() {
|
|
||||||
if m.client.IsConnected() {
|
|
||||||
m.Log.Debugf("Disconnecting %v", m.Servers)
|
|
||||||
m.client.Disconnect(200)
|
|
||||||
m.Log.Debugf("Disconnected %v", m.Servers)
|
|
||||||
}
|
|
||||||
if m.cancel != nil {
|
|
||||||
m.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (m *MQTTConsumer) Gather(_ telegraf.Accumulator) error {
|
|
||||||
if !m.client.IsConnected() {
|
|
||||||
m.Log.Debugf("Connecting %v", m.Servers)
|
|
||||||
return m.connect()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (m *MQTTConsumer) createOpts() (*mqtt.ClientOptions, error) {
|
func (m *MQTTConsumer) createOpts() (*mqtt.ClientOptions, error) {
|
||||||
opts := mqtt.NewClientOptions()
|
opts := mqtt.NewClientOptions()
|
||||||
opts.ConnectTimeout = time.Duration(m.ConnectionTimeout)
|
opts.ConnectTimeout = time.Duration(m.ConnectionTimeout)
|
||||||
|
|
@ -342,7 +347,7 @@ func (m *MQTTConsumer) createOpts() (*mqtt.ClientOptions, error) {
|
||||||
return opts, nil
|
return opts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(factory ClientFactory) *MQTTConsumer {
|
func newMQTTConsumer(factory clientFactory) *MQTTConsumer {
|
||||||
return &MQTTConsumer{
|
return &MQTTConsumer{
|
||||||
Servers: []string{"tcp://127.0.0.1:1883"},
|
Servers: []string{"tcp://127.0.0.1:1883"},
|
||||||
MaxUndeliveredMessages: defaultMaxUndeliveredMessages,
|
MaxUndeliveredMessages: defaultMaxUndeliveredMessages,
|
||||||
|
|
@ -354,7 +359,7 @@ func New(factory ClientFactory) *MQTTConsumer {
|
||||||
}
|
}
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("mqtt_consumer", func() telegraf.Input {
|
inputs.Add("mqtt_consumer", func() telegraf.Input {
|
||||||
return New(func(o *mqtt.ClientOptions) Client {
|
return newMQTTConsumer(func(o *mqtt.ClientOptions) client {
|
||||||
return mqtt.NewClient(o)
|
return mqtt.NewClient(o)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,11 @@ import (
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FakeClient struct {
|
type fakeClient struct {
|
||||||
ConnectF func() mqtt.Token
|
connectF func() mqtt.Token
|
||||||
SubscribeMultipleF func() mqtt.Token
|
subscribeMultipleF func() mqtt.Token
|
||||||
AddRouteF func(callback mqtt.MessageHandler)
|
addRouteF func(callback mqtt.MessageHandler)
|
||||||
DisconnectF func()
|
disconnectF func()
|
||||||
|
|
||||||
connectCallCount int
|
connectCallCount int
|
||||||
subscribeCallCount int
|
subscribeCallCount int
|
||||||
|
|
@ -32,75 +32,75 @@ type FakeClient struct {
|
||||||
connected bool
|
connected bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FakeClient) Connect() mqtt.Token {
|
func (c *fakeClient) Connect() mqtt.Token {
|
||||||
c.connectCallCount++
|
c.connectCallCount++
|
||||||
token := c.ConnectF()
|
token := c.connectF()
|
||||||
c.connected = token.Error() == nil
|
c.connected = token.Error() == nil
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FakeClient) SubscribeMultiple(map[string]byte, mqtt.MessageHandler) mqtt.Token {
|
func (c *fakeClient) SubscribeMultiple(map[string]byte, mqtt.MessageHandler) mqtt.Token {
|
||||||
c.subscribeCallCount++
|
c.subscribeCallCount++
|
||||||
return c.SubscribeMultipleF()
|
return c.subscribeMultipleF()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FakeClient) AddRoute(_ string, callback mqtt.MessageHandler) {
|
func (c *fakeClient) AddRoute(_ string, callback mqtt.MessageHandler) {
|
||||||
c.addRouteCallCount++
|
c.addRouteCallCount++
|
||||||
c.AddRouteF(callback)
|
c.addRouteF(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FakeClient) Disconnect(uint) {
|
func (c *fakeClient) Disconnect(uint) {
|
||||||
c.disconnectCallCount++
|
c.disconnectCallCount++
|
||||||
c.DisconnectF()
|
c.disconnectF()
|
||||||
c.connected = false
|
c.connected = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FakeClient) IsConnected() bool {
|
func (c *fakeClient) IsConnected() bool {
|
||||||
return c.connected
|
return c.connected
|
||||||
}
|
}
|
||||||
|
|
||||||
type FakeParser struct{}
|
type fakeParser struct{}
|
||||||
|
|
||||||
// FakeParser satisfies telegraf.Parser
|
// fakeParser satisfies telegraf.Parser
|
||||||
var _ telegraf.Parser = &FakeParser{}
|
var _ telegraf.Parser = &fakeParser{}
|
||||||
|
|
||||||
func (p *FakeParser) Parse(_ []byte) ([]telegraf.Metric, error) {
|
func (p *fakeParser) Parse(_ []byte) ([]telegraf.Metric, error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *FakeParser) ParseLine(_ string) (telegraf.Metric, error) {
|
func (p *fakeParser) ParseLine(_ string) (telegraf.Metric, error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *FakeParser) SetDefaultTags(_ map[string]string) {
|
func (p *fakeParser) SetDefaultTags(_ map[string]string) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
type FakeToken struct {
|
type fakeToken struct {
|
||||||
sessionPresent bool
|
sessionPresent bool
|
||||||
complete chan struct{}
|
complete chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FakeToken satisfies mqtt.Token
|
// fakeToken satisfies mqtt.Token
|
||||||
var _ mqtt.Token = &FakeToken{}
|
var _ mqtt.Token = &fakeToken{}
|
||||||
|
|
||||||
func (t *FakeToken) Wait() bool {
|
func (t *fakeToken) Wait() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *FakeToken) WaitTimeout(time.Duration) bool {
|
func (t *fakeToken) WaitTimeout(time.Duration) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *FakeToken) Error() error {
|
func (t *fakeToken) Error() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *FakeToken) SessionPresent() bool {
|
func (t *fakeToken) SessionPresent() bool {
|
||||||
return t.sessionPresent
|
return t.sessionPresent
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *FakeToken) Done() <-chan struct{} {
|
func (t *fakeToken) Done() <-chan struct{} {
|
||||||
return t.complete
|
return t.complete
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,24 +108,24 @@ func (t *FakeToken) Done() <-chan struct{} {
|
||||||
func TestLifecycleSanity(t *testing.T) {
|
func TestLifecycleSanity(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
plugin := New(func(*mqtt.ClientOptions) Client {
|
plugin := newMQTTConsumer(func(*mqtt.ClientOptions) client {
|
||||||
return &FakeClient{
|
return &fakeClient{
|
||||||
ConnectF: func() mqtt.Token {
|
connectF: func() mqtt.Token {
|
||||||
return &FakeToken{}
|
return &fakeToken{}
|
||||||
},
|
},
|
||||||
AddRouteF: func(mqtt.MessageHandler) {
|
addRouteF: func(mqtt.MessageHandler) {
|
||||||
},
|
},
|
||||||
SubscribeMultipleF: func() mqtt.Token {
|
subscribeMultipleF: func() mqtt.Token {
|
||||||
return &FakeToken{}
|
return &fakeToken{}
|
||||||
},
|
},
|
||||||
DisconnectF: func() {
|
disconnectF: func() {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
plugin.Log = testutil.Logger{}
|
plugin.Log = testutil.Logger{}
|
||||||
plugin.Servers = []string{"tcp://127.0.0.1"}
|
plugin.Servers = []string{"tcp://127.0.0.1"}
|
||||||
|
|
||||||
parser := &FakeParser{}
|
parser := &fakeParser{}
|
||||||
plugin.SetParser(parser)
|
plugin.SetParser(parser)
|
||||||
|
|
||||||
require.NoError(t, plugin.Init())
|
require.NoError(t, plugin.Init())
|
||||||
|
|
@ -138,12 +138,12 @@ func TestLifecycleSanity(t *testing.T) {
|
||||||
func TestRandomClientID(t *testing.T) {
|
func TestRandomClientID(t *testing.T) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
m1 := New(nil)
|
m1 := newMQTTConsumer(nil)
|
||||||
m1.Log = testutil.Logger{}
|
m1.Log = testutil.Logger{}
|
||||||
err = m1.Init()
|
err = m1.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
m2 := New(nil)
|
m2 := newMQTTConsumer(nil)
|
||||||
m2.Log = testutil.Logger{}
|
m2.Log = testutil.Logger{}
|
||||||
err = m2.Init()
|
err = m2.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
@ -153,7 +153,7 @@ func TestRandomClientID(t *testing.T) {
|
||||||
|
|
||||||
// PersistentSession requires ClientID
|
// PersistentSession requires ClientID
|
||||||
func TestPersistentClientIDFail(t *testing.T) {
|
func TestPersistentClientIDFail(t *testing.T) {
|
||||||
plugin := New(nil)
|
plugin := newMQTTConsumer(nil)
|
||||||
plugin.Log = testutil.Logger{}
|
plugin.Log = testutil.Logger{}
|
||||||
plugin.PersistentSession = true
|
plugin.PersistentSession = true
|
||||||
|
|
||||||
|
|
@ -161,36 +161,36 @@ func TestPersistentClientIDFail(t *testing.T) {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Message struct {
|
type message struct {
|
||||||
topic string
|
topic string
|
||||||
qos byte
|
qos byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) Duplicate() bool {
|
func (m *message) Duplicate() bool {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) Qos() byte {
|
func (m *message) Qos() byte {
|
||||||
return m.qos
|
return m.qos
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) Retained() bool {
|
func (m *message) Retained() bool {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) Topic() string {
|
func (m *message) Topic() string {
|
||||||
return m.topic
|
return m.topic
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) MessageID() uint16 {
|
func (m *message) MessageID() uint16 {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) Payload() []byte {
|
func (m *message) Payload() []byte {
|
||||||
return []byte("cpu time_idle=42i")
|
return []byte("cpu time_idle=42i")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) Ack() {
|
func (m *message) Ack() {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,7 +200,7 @@ func TestTopicTag(t *testing.T) {
|
||||||
topic string
|
topic string
|
||||||
topicTag func() *string
|
topicTag func() *string
|
||||||
expectedError string
|
expectedError string
|
||||||
topicParsing []TopicParsingConfig
|
topicParsing []topicParsingConfig
|
||||||
expected []telegraf.Metric
|
expected []telegraf.Metric
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
|
@ -267,7 +267,7 @@ func TestTopicTag(t *testing.T) {
|
||||||
tag := ""
|
tag := ""
|
||||||
return &tag
|
return &tag
|
||||||
},
|
},
|
||||||
topicParsing: []TopicParsingConfig{
|
topicParsing: []topicParsingConfig{
|
||||||
{
|
{
|
||||||
Topic: "telegraf/123/test",
|
Topic: "telegraf/123/test",
|
||||||
Measurement: "_/_/measurement",
|
Measurement: "_/_/measurement",
|
||||||
|
|
@ -299,7 +299,7 @@ func TestTopicTag(t *testing.T) {
|
||||||
tag := ""
|
tag := ""
|
||||||
return &tag
|
return &tag
|
||||||
},
|
},
|
||||||
topicParsing: []TopicParsingConfig{
|
topicParsing: []topicParsingConfig{
|
||||||
{
|
{
|
||||||
Topic: "telegraf/+/test/hello",
|
Topic: "telegraf/+/test/hello",
|
||||||
Measurement: "_/_/measurement/_",
|
Measurement: "_/_/measurement/_",
|
||||||
|
|
@ -333,7 +333,7 @@ func TestTopicTag(t *testing.T) {
|
||||||
return &tag
|
return &tag
|
||||||
},
|
},
|
||||||
expectedError: "config error topic parsing: fields length does not equal topic length",
|
expectedError: "config error topic parsing: fields length does not equal topic length",
|
||||||
topicParsing: []TopicParsingConfig{
|
topicParsing: []topicParsingConfig{
|
||||||
{
|
{
|
||||||
Topic: "telegraf/+/test/hello",
|
Topic: "telegraf/+/test/hello",
|
||||||
Measurement: "_/_/measurement/_",
|
Measurement: "_/_/measurement/_",
|
||||||
|
|
@ -366,7 +366,7 @@ func TestTopicTag(t *testing.T) {
|
||||||
tag := ""
|
tag := ""
|
||||||
return &tag
|
return &tag
|
||||||
},
|
},
|
||||||
topicParsing: []TopicParsingConfig{
|
topicParsing: []topicParsingConfig{
|
||||||
{
|
{
|
||||||
Topic: "telegraf/+/test/hello",
|
Topic: "telegraf/+/test/hello",
|
||||||
Measurement: "_/_/measurement/_",
|
Measurement: "_/_/measurement/_",
|
||||||
|
|
@ -396,7 +396,7 @@ func TestTopicTag(t *testing.T) {
|
||||||
tag := ""
|
tag := ""
|
||||||
return &tag
|
return &tag
|
||||||
},
|
},
|
||||||
topicParsing: []TopicParsingConfig{
|
topicParsing: []topicParsingConfig{
|
||||||
{
|
{
|
||||||
Topic: "telegraf/+/test/hello",
|
Topic: "telegraf/+/test/hello",
|
||||||
Tags: "testTag/_/_/_",
|
Tags: "testTag/_/_/_",
|
||||||
|
|
@ -428,7 +428,7 @@ func TestTopicTag(t *testing.T) {
|
||||||
tag := ""
|
tag := ""
|
||||||
return &tag
|
return &tag
|
||||||
},
|
},
|
||||||
topicParsing: []TopicParsingConfig{
|
topicParsing: []topicParsingConfig{
|
||||||
{
|
{
|
||||||
Topic: "/telegraf/+/test/hello",
|
Topic: "/telegraf/+/test/hello",
|
||||||
Measurement: "/_/_/measurement/_",
|
Measurement: "/_/_/measurement/_",
|
||||||
|
|
@ -461,7 +461,7 @@ func TestTopicTag(t *testing.T) {
|
||||||
tag := ""
|
tag := ""
|
||||||
return &tag
|
return &tag
|
||||||
},
|
},
|
||||||
topicParsing: []TopicParsingConfig{
|
topicParsing: []topicParsingConfig{
|
||||||
{
|
{
|
||||||
Topic: "/telegraf/#/test/hello",
|
Topic: "/telegraf/#/test/hello",
|
||||||
Measurement: "/#/measurement/_",
|
Measurement: "/#/measurement/_",
|
||||||
|
|
@ -495,7 +495,7 @@ func TestTopicTag(t *testing.T) {
|
||||||
tag := ""
|
tag := ""
|
||||||
return &tag
|
return &tag
|
||||||
},
|
},
|
||||||
topicParsing: []TopicParsingConfig{
|
topicParsing: []topicParsingConfig{
|
||||||
{
|
{
|
||||||
Topic: "/telegraf/#",
|
Topic: "/telegraf/#",
|
||||||
Measurement: "/#/measurement/_",
|
Measurement: "/#/measurement/_",
|
||||||
|
|
@ -521,22 +521,22 @@ func TestTopicTag(t *testing.T) {
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
var handler mqtt.MessageHandler
|
var handler mqtt.MessageHandler
|
||||||
client := &FakeClient{
|
fClient := &fakeClient{
|
||||||
ConnectF: func() mqtt.Token {
|
connectF: func() mqtt.Token {
|
||||||
return &FakeToken{}
|
return &fakeToken{}
|
||||||
},
|
},
|
||||||
AddRouteF: func(callback mqtt.MessageHandler) {
|
addRouteF: func(callback mqtt.MessageHandler) {
|
||||||
handler = callback
|
handler = callback
|
||||||
},
|
},
|
||||||
SubscribeMultipleF: func() mqtt.Token {
|
subscribeMultipleF: func() mqtt.Token {
|
||||||
return &FakeToken{}
|
return &fakeToken{}
|
||||||
},
|
},
|
||||||
DisconnectF: func() {
|
disconnectF: func() {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin := New(func(*mqtt.ClientOptions) Client {
|
plugin := newMQTTConsumer(func(*mqtt.ClientOptions) client {
|
||||||
return client
|
return fClient
|
||||||
})
|
})
|
||||||
plugin.Log = testutil.Logger{}
|
plugin.Log = testutil.Logger{}
|
||||||
plugin.Topics = []string{tt.topic}
|
plugin.Topics = []string{tt.topic}
|
||||||
|
|
@ -557,7 +557,7 @@ func TestTopicTag(t *testing.T) {
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
require.NoError(t, plugin.Start(&acc))
|
require.NoError(t, plugin.Start(&acc))
|
||||||
|
|
||||||
var m Message
|
var m message
|
||||||
m.topic = tt.topic
|
m.topic = tt.topic
|
||||||
|
|
||||||
handler(nil, &m)
|
handler(nil, &m)
|
||||||
|
|
@ -570,20 +570,20 @@ func TestTopicTag(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddRouteCalledForEachTopic(t *testing.T) {
|
func TestAddRouteCalledForEachTopic(t *testing.T) {
|
||||||
client := &FakeClient{
|
fClient := &fakeClient{
|
||||||
ConnectF: func() mqtt.Token {
|
connectF: func() mqtt.Token {
|
||||||
return &FakeToken{}
|
return &fakeToken{}
|
||||||
},
|
},
|
||||||
AddRouteF: func(mqtt.MessageHandler) {
|
addRouteF: func(mqtt.MessageHandler) {
|
||||||
},
|
},
|
||||||
SubscribeMultipleF: func() mqtt.Token {
|
subscribeMultipleF: func() mqtt.Token {
|
||||||
return &FakeToken{}
|
return &fakeToken{}
|
||||||
},
|
},
|
||||||
DisconnectF: func() {
|
disconnectF: func() {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
plugin := New(func(*mqtt.ClientOptions) Client {
|
plugin := newMQTTConsumer(func(*mqtt.ClientOptions) client {
|
||||||
return client
|
return fClient
|
||||||
})
|
})
|
||||||
plugin.Log = testutil.Logger{}
|
plugin.Log = testutil.Logger{}
|
||||||
plugin.Topics = []string{"a", "b"}
|
plugin.Topics = []string{"a", "b"}
|
||||||
|
|
@ -595,24 +595,24 @@ func TestAddRouteCalledForEachTopic(t *testing.T) {
|
||||||
|
|
||||||
plugin.Stop()
|
plugin.Stop()
|
||||||
|
|
||||||
require.Equal(t, 2, client.addRouteCallCount)
|
require.Equal(t, 2, fClient.addRouteCallCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSubscribeCalledIfNoSession(t *testing.T) {
|
func TestSubscribeCalledIfNoSession(t *testing.T) {
|
||||||
client := &FakeClient{
|
fClient := &fakeClient{
|
||||||
ConnectF: func() mqtt.Token {
|
connectF: func() mqtt.Token {
|
||||||
return &FakeToken{}
|
return &fakeToken{}
|
||||||
},
|
},
|
||||||
AddRouteF: func(mqtt.MessageHandler) {
|
addRouteF: func(mqtt.MessageHandler) {
|
||||||
},
|
},
|
||||||
SubscribeMultipleF: func() mqtt.Token {
|
subscribeMultipleF: func() mqtt.Token {
|
||||||
return &FakeToken{}
|
return &fakeToken{}
|
||||||
},
|
},
|
||||||
DisconnectF: func() {
|
disconnectF: func() {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
plugin := New(func(*mqtt.ClientOptions) Client {
|
plugin := newMQTTConsumer(func(*mqtt.ClientOptions) client {
|
||||||
return client
|
return fClient
|
||||||
})
|
})
|
||||||
plugin.Log = testutil.Logger{}
|
plugin.Log = testutil.Logger{}
|
||||||
plugin.Topics = []string{"b"}
|
plugin.Topics = []string{"b"}
|
||||||
|
|
@ -624,24 +624,24 @@ func TestSubscribeCalledIfNoSession(t *testing.T) {
|
||||||
|
|
||||||
plugin.Stop()
|
plugin.Stop()
|
||||||
|
|
||||||
require.Equal(t, 1, client.subscribeCallCount)
|
require.Equal(t, 1, fClient.subscribeCallCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSubscribeNotCalledIfSession(t *testing.T) {
|
func TestSubscribeNotCalledIfSession(t *testing.T) {
|
||||||
client := &FakeClient{
|
fClient := &fakeClient{
|
||||||
ConnectF: func() mqtt.Token {
|
connectF: func() mqtt.Token {
|
||||||
return &FakeToken{sessionPresent: true}
|
return &fakeToken{sessionPresent: true}
|
||||||
},
|
},
|
||||||
AddRouteF: func(mqtt.MessageHandler) {
|
addRouteF: func(mqtt.MessageHandler) {
|
||||||
},
|
},
|
||||||
SubscribeMultipleF: func() mqtt.Token {
|
subscribeMultipleF: func() mqtt.Token {
|
||||||
return &FakeToken{}
|
return &fakeToken{}
|
||||||
},
|
},
|
||||||
DisconnectF: func() {
|
disconnectF: func() {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
plugin := New(func(*mqtt.ClientOptions) Client {
|
plugin := newMQTTConsumer(func(*mqtt.ClientOptions) client {
|
||||||
return client
|
return fClient
|
||||||
})
|
})
|
||||||
plugin.Log = testutil.Logger{}
|
plugin.Log = testutil.Logger{}
|
||||||
plugin.Topics = []string{"b"}
|
plugin.Topics = []string{"b"}
|
||||||
|
|
@ -652,7 +652,7 @@ func TestSubscribeNotCalledIfSession(t *testing.T) {
|
||||||
require.NoError(t, plugin.Start(&acc))
|
require.NoError(t, plugin.Start(&acc))
|
||||||
plugin.Stop()
|
plugin.Stop()
|
||||||
|
|
||||||
require.Equal(t, 0, client.subscribeCallCount)
|
require.Equal(t, 0, fClient.subscribeCallCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIntegration(t *testing.T) {
|
func TestIntegration(t *testing.T) {
|
||||||
|
|
@ -679,7 +679,7 @@ func TestIntegration(t *testing.T) {
|
||||||
// Setup the plugin and connect to the broker
|
// Setup the plugin and connect to the broker
|
||||||
url := fmt.Sprintf("tcp://%s:%s", container.Address, container.Ports[servicePort])
|
url := fmt.Sprintf("tcp://%s:%s", container.Address, container.Ports[servicePort])
|
||||||
topic := "/telegraf/test"
|
topic := "/telegraf/test"
|
||||||
factory := func(o *mqtt.ClientOptions) Client { return mqtt.NewClient(o) }
|
factory := func(o *mqtt.ClientOptions) client { return mqtt.NewClient(o) }
|
||||||
plugin := &MQTTConsumer{
|
plugin := &MQTTConsumer{
|
||||||
Servers: []string{url},
|
Servers: []string{url},
|
||||||
Topics: []string{topic},
|
Topics: []string{topic},
|
||||||
|
|
@ -768,7 +768,7 @@ func TestStartupErrorBehaviorErrorIntegration(t *testing.T) {
|
||||||
// Setup the plugin and connect to the broker
|
// Setup the plugin and connect to the broker
|
||||||
url := fmt.Sprintf("tcp://%s:%s", container.Address, container.Ports[servicePort])
|
url := fmt.Sprintf("tcp://%s:%s", container.Address, container.Ports[servicePort])
|
||||||
topic := "/telegraf/test"
|
topic := "/telegraf/test"
|
||||||
factory := func(o *mqtt.ClientOptions) Client { return mqtt.NewClient(o) }
|
factory := func(o *mqtt.ClientOptions) client { return mqtt.NewClient(o) }
|
||||||
plugin := &MQTTConsumer{
|
plugin := &MQTTConsumer{
|
||||||
Servers: []string{url},
|
Servers: []string{url},
|
||||||
Topics: []string{topic},
|
Topics: []string{topic},
|
||||||
|
|
@ -827,7 +827,7 @@ func TestStartupErrorBehaviorIgnoreIntegration(t *testing.T) {
|
||||||
// Setup the plugin and connect to the broker
|
// Setup the plugin and connect to the broker
|
||||||
url := fmt.Sprintf("tcp://%s:%s", container.Address, container.Ports[servicePort])
|
url := fmt.Sprintf("tcp://%s:%s", container.Address, container.Ports[servicePort])
|
||||||
topic := "/telegraf/test"
|
topic := "/telegraf/test"
|
||||||
factory := func(o *mqtt.ClientOptions) Client { return mqtt.NewClient(o) }
|
factory := func(o *mqtt.ClientOptions) client { return mqtt.NewClient(o) }
|
||||||
plugin := &MQTTConsumer{
|
plugin := &MQTTConsumer{
|
||||||
Servers: []string{url},
|
Servers: []string{url},
|
||||||
Topics: []string{topic},
|
Topics: []string{topic},
|
||||||
|
|
@ -892,7 +892,7 @@ func TestStartupErrorBehaviorRetryIntegration(t *testing.T) {
|
||||||
// Setup the plugin and connect to the broker
|
// Setup the plugin and connect to the broker
|
||||||
url := fmt.Sprintf("tcp://%s:%s", container.Address, container.Ports[servicePort])
|
url := fmt.Sprintf("tcp://%s:%s", container.Address, container.Ports[servicePort])
|
||||||
topic := "/telegraf/test"
|
topic := "/telegraf/test"
|
||||||
factory := func(o *mqtt.ClientOptions) Client { return mqtt.NewClient(o) }
|
factory := func(o *mqtt.ClientOptions) client { return mqtt.NewClient(o) }
|
||||||
plugin := &MQTTConsumer{
|
plugin := &MQTTConsumer{
|
||||||
Servers: []string{url},
|
Servers: []string{url},
|
||||||
Topics: []string{topic},
|
Topics: []string{topic},
|
||||||
|
|
@ -997,7 +997,7 @@ func TestReconnectIntegration(t *testing.T) {
|
||||||
// Setup the plugin and connect to the broker
|
// Setup the plugin and connect to the broker
|
||||||
url := fmt.Sprintf("tcp://%s:%s", container.Address, container.Ports[servicePort])
|
url := fmt.Sprintf("tcp://%s:%s", container.Address, container.Ports[servicePort])
|
||||||
topic := "/telegraf/test"
|
topic := "/telegraf/test"
|
||||||
factory := func(o *mqtt.ClientOptions) Client { return mqtt.NewClient(o) }
|
factory := func(o *mqtt.ClientOptions) client { return mqtt.NewClient(o) }
|
||||||
plugin := &MQTTConsumer{
|
plugin := &MQTTConsumer{
|
||||||
Servers: []string{url},
|
Servers: []string{url},
|
||||||
Topics: []string{topic},
|
Topics: []string{topic},
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,12 @@ type mqttLogger struct {
|
||||||
telegraf.Logger
|
telegraf.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Printf implements mqtt.Logger
|
||||||
func (l mqttLogger) Printf(fmt string, args ...interface{}) {
|
func (l mqttLogger) Printf(fmt string, args ...interface{}) {
|
||||||
l.Logger.Debugf(fmt, args...)
|
l.Logger.Debugf(fmt, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Println implements mqtt.Logger
|
||||||
func (l mqttLogger) Println(args ...interface{}) {
|
func (l mqttLogger) Println(args ...interface{}) {
|
||||||
l.Logger.Debug(args...)
|
l.Logger.Debug(args...)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TopicParsingConfig struct {
|
type topicParsingConfig struct {
|
||||||
Topic string `toml:"topic"`
|
Topic string `toml:"topic"`
|
||||||
Measurement string `toml:"measurement"`
|
Measurement string `toml:"measurement"`
|
||||||
Tags string `toml:"tags"`
|
Tags string `toml:"tags"`
|
||||||
|
|
@ -17,7 +17,7 @@ type TopicParsingConfig struct {
|
||||||
FieldTypes map[string]string `toml:"types"`
|
FieldTypes map[string]string `toml:"types"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TopicParser struct {
|
type topicParser struct {
|
||||||
topicIndices map[string]int
|
topicIndices map[string]int
|
||||||
topicVarLength bool
|
topicVarLength bool
|
||||||
topicMinLength int
|
topicMinLength int
|
||||||
|
|
@ -29,8 +29,8 @@ type TopicParser struct {
|
||||||
fieldTypes map[string]string
|
fieldTypes map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *TopicParsingConfig) NewParser() (*TopicParser, error) {
|
func (cfg *topicParsingConfig) newParser() (*topicParser, error) {
|
||||||
p := &TopicParser{
|
p := &topicParser{
|
||||||
fieldTypes: cfg.FieldTypes,
|
fieldTypes: cfg.FieldTypes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,7 +150,7 @@ func (cfg *TopicParsingConfig) NewParser() (*TopicParser, error) {
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *TopicParser) Parse(metric telegraf.Metric, topic string) error {
|
func (p *topicParser) parse(metric telegraf.Metric, topic string) error {
|
||||||
// Split the actual topic into its elements and check for a match
|
// Split the actual topic into its elements and check for a match
|
||||||
topicParts := strings.Split(topic, "/")
|
topicParts := strings.Split(topic, "/")
|
||||||
if p.topicVarLength && len(topicParts) < p.topicMinLength || !p.topicVarLength && len(topicParts) != p.topicMinLength {
|
if p.topicVarLength && len(topicParts) < p.topicMinLength || !p.topicVarLength && len(topicParts) != p.topicMinLength {
|
||||||
|
|
@ -200,7 +200,7 @@ func (p *TopicParser) Parse(metric telegraf.Metric, topic string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *TopicParser) convertToFieldType(value, key string) (interface{}, error) {
|
func (p *topicParser) convertToFieldType(value, key string) (interface{}, error) {
|
||||||
// If the user configured inputs.mqtt_consumer.topic.types, check for the desired type
|
// If the user configured inputs.mqtt_consumer.topic.types, check for the desired type
|
||||||
desiredType, ok := p.fieldTypes[key]
|
desiredType, ok := p.fieldTypes[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
||||||
|
|
@ -19,15 +19,15 @@ import (
|
||||||
var sampleConfig string
|
var sampleConfig string
|
||||||
|
|
||||||
type MultiFile struct {
|
type MultiFile struct {
|
||||||
BaseDir string
|
BaseDir string `toml:"base_dir"`
|
||||||
FailEarly bool
|
FailEarly bool `toml:"fail_early"`
|
||||||
Files []File `toml:"file"`
|
Files []file `toml:"file"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type File struct {
|
type file struct {
|
||||||
Name string `toml:"file"`
|
Name string `toml:"file"`
|
||||||
Dest string
|
Dest string `toml:"dest"`
|
||||||
Conversion string
|
Conversion string `toml:"conversion"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*MultiFile) SampleConfig() string {
|
func (*MultiFile) SampleConfig() string {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ func TestFileTypes(t *testing.T) {
|
||||||
m := MultiFile{
|
m := MultiFile{
|
||||||
BaseDir: path.Join(wd, `testdata`),
|
BaseDir: path.Join(wd, `testdata`),
|
||||||
FailEarly: true,
|
FailEarly: true,
|
||||||
Files: []File{
|
Files: []file{
|
||||||
{Name: `bool.txt`, Dest: `examplebool`, Conversion: `bool`},
|
{Name: `bool.txt`, Dest: `examplebool`, Conversion: `bool`},
|
||||||
{Name: `float.txt`, Dest: `examplefloat`, Conversion: `float`},
|
{Name: `float.txt`, Dest: `examplefloat`, Conversion: `float`},
|
||||||
{Name: `int.txt`, Dest: `examplefloatX`, Conversion: `float(3)`},
|
{Name: `int.txt`, Dest: `examplefloatX`, Conversion: `float(3)`},
|
||||||
|
|
@ -43,14 +43,14 @@ func TestFileTypes(t *testing.T) {
|
||||||
}, acc.Metrics[0].Fields)
|
}, acc.Metrics[0].Fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FailEarly(failEarly bool, t *testing.T) error {
|
func failEarly(failEarly bool, t *testing.T) error {
|
||||||
wd, err := os.Getwd()
|
wd, err := os.Getwd()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
m := MultiFile{
|
m := MultiFile{
|
||||||
BaseDir: path.Join(wd, `testdata`),
|
BaseDir: path.Join(wd, `testdata`),
|
||||||
FailEarly: failEarly,
|
FailEarly: failEarly,
|
||||||
Files: []File{
|
Files: []file{
|
||||||
{Name: `int.txt`, Dest: `exampleint`, Conversion: `int`},
|
{Name: `int.txt`, Dest: `exampleint`, Conversion: `int`},
|
||||||
{Name: `int.txt`, Dest: `exampleerror`, Conversion: `bool`},
|
{Name: `int.txt`, Dest: `exampleerror`, Conversion: `bool`},
|
||||||
},
|
},
|
||||||
|
|
@ -71,8 +71,8 @@ func FailEarly(failEarly bool, t *testing.T) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFailEarly(t *testing.T) {
|
func TestFailEarly(t *testing.T) {
|
||||||
err := FailEarly(false, t)
|
err := failEarly(false, t)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
err = FailEarly(true, t)
|
err = failEarly(true, t)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,14 @@ var sampleConfig string
|
||||||
|
|
||||||
var tlsRe = regexp.MustCompile(`([\?&])(?:tls=custom)($|&)`)
|
var tlsRe = regexp.MustCompile(`([\?&])(?:tls=custom)($|&)`)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultPerfEventsStatementsDigestTextLimit = 120
|
||||||
|
defaultPerfEventsStatementsLimit = 250
|
||||||
|
defaultPerfEventsStatementsTimeLimit = 86400
|
||||||
|
defaultGatherGlobalVars = true
|
||||||
|
localhost = ""
|
||||||
|
)
|
||||||
|
|
||||||
type Mysql struct {
|
type Mysql struct {
|
||||||
Servers []*config.Secret `toml:"servers"`
|
Servers []*config.Secret `toml:"servers"`
|
||||||
PerfEventsStatementsDigestTextLimit int64 `toml:"perf_events_statements_digest_text_limit"`
|
PerfEventsStatementsDigestTextLimit int64 `toml:"perf_events_statements_digest_text_limit"`
|
||||||
|
|
@ -64,15 +72,6 @@ type Mysql struct {
|
||||||
loggedConvertFields map[string]bool
|
loggedConvertFields map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
defaultPerfEventsStatementsDigestTextLimit = 120
|
|
||||||
defaultPerfEventsStatementsLimit = 250
|
|
||||||
defaultPerfEventsStatementsTimeLimit = 86400
|
|
||||||
defaultGatherGlobalVars = true
|
|
||||||
)
|
|
||||||
|
|
||||||
const localhost = ""
|
|
||||||
|
|
||||||
func (*Mysql) SampleConfig() string {
|
func (*Mysql) SampleConfig() string {
|
||||||
return sampleConfig
|
return sampleConfig
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConversionFunc func(value sql.RawBytes) (interface{}, error)
|
type conversionFunc func(value sql.RawBytes) (interface{}, error)
|
||||||
|
|
||||||
func ParseInt(value sql.RawBytes) (interface{}, error) {
|
func ParseInt(value sql.RawBytes) (interface{}, error) {
|
||||||
v, err := strconv.ParseInt(string(value), 10, 64)
|
v, err := strconv.ParseInt(string(value), 10, 64)
|
||||||
|
|
@ -86,7 +86,7 @@ func ParseValue(value sql.RawBytes) (interface{}, error) {
|
||||||
return nil, fmt.Errorf("unconvertible value: %q", string(value))
|
return nil, fmt.Errorf("unconvertible value: %q", string(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
var GlobalStatusConversions = map[string]ConversionFunc{
|
var globalStatusConversions = map[string]conversionFunc{
|
||||||
"innodb_available_undo_logs": ParseUint,
|
"innodb_available_undo_logs": ParseUint,
|
||||||
"innodb_buffer_pool_pages_misc": ParseUint,
|
"innodb_buffer_pool_pages_misc": ParseUint,
|
||||||
"innodb_data_pending_fsyncs": ParseUint,
|
"innodb_data_pending_fsyncs": ParseUint,
|
||||||
|
|
@ -108,7 +108,7 @@ var GlobalStatusConversions = map[string]ConversionFunc{
|
||||||
"wsrep_local_send_queue_avg": ParseFloat,
|
"wsrep_local_send_queue_avg": ParseFloat,
|
||||||
}
|
}
|
||||||
|
|
||||||
var GlobalVariableConversions = map[string]ConversionFunc{
|
var globalVariableConversions = map[string]conversionFunc{
|
||||||
// see https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html
|
// see https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html
|
||||||
// see https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html
|
// see https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html
|
||||||
"delay_key_write": ParseString, // ON, OFF, ALL
|
"delay_key_write": ParseString, // ON, OFF, ALL
|
||||||
|
|
@ -140,7 +140,7 @@ func ConvertGlobalStatus(key string, value sql.RawBytes) (interface{}, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if conv, ok := GlobalStatusConversions[key]; ok {
|
if conv, ok := globalStatusConversions[key]; ok {
|
||||||
return conv(value)
|
return conv(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,7 +152,7 @@ func ConvertGlobalVariables(key string, value sql.RawBytes) (interface{}, error)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if conv, ok := GlobalVariableConversions[key]; ok {
|
if conv, ok := globalVariableConversions[key]; ok {
|
||||||
return conv(value)
|
return conv(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue