diff --git a/pkg/receiver/smartagentreceiver/log.go b/pkg/receiver/smartagentreceiver/log.go index 86a00aadc5f..6aaddf1e8c2 100644 --- a/pkg/receiver/smartagentreceiver/log.go +++ b/pkg/receiver/smartagentreceiver/log.go @@ -17,6 +17,7 @@ package smartagentreceiver import ( "fmt" "io" + "sort" "strings" "sync" @@ -136,6 +137,11 @@ func (l *logrusToZap) Fire(entry *logrus.Entry) error { monitorID: monitorID, }) + sort.Slice(fields, func(i, j int) bool { + fI, fJ := fields[i], fields[j] + return fI.Key < fJ.Key + }) + if ce := zapLogger.Check(logrusToZapLevel[entry.Level], entry.Message); ce != nil { ce.Time = entry.Time // clear stack so that it's not for parent Check() diff --git a/pkg/receiver/smartagentreceiver/log_test.go b/pkg/receiver/smartagentreceiver/log_test.go index deb7a2d84fe..bb92004d288 100644 --- a/pkg/receiver/smartagentreceiver/log_test.go +++ b/pkg/receiver/smartagentreceiver/log_test.go @@ -312,6 +312,11 @@ func TestRedirectMonitorLogsWithMissingMapEntryUsesDefaultLogger(t *testing.T) { assert.Equal(t, 0, zap1Logs.Len()) require.Equal(t, 1, defaultZapLogs.Len()) require.Equal(t, msg1, defaultZapLogs.All()[0].Message) + require.Equal(t, []zapcore.Field{ + {Key: "monitorID", Type: zapcore.StringType, String: "id1"}, + {Key: "monitorType", Type: zapcore.StringType, String: "monitor1"}, + }, defaultZapLogs.All()[0].Context) + }) } } @@ -348,6 +353,14 @@ func TestRedirectSameMonitorManyInstancesLogs(t *testing.T) { require.Equal(t, msg1, zap1Logs.All()[0].Message) require.Equal(t, 1, zap2Logs.Len()) require.Equal(t, msg2, zap2Logs.All()[0].Message) + require.Equal(t, []zapcore.Field{ + {Key: "monitorID", Type: zapcore.StringType, String: "id1"}, + {Key: "monitorType", Type: zapcore.StringType, String: "monitor1"}, + }, zap1Logs.All()[0].Context) + require.Equal(t, []zapcore.Field{ + {Key: "monitorID", Type: zapcore.StringType, String: "id2"}, + {Key: "monitorType", Type: zapcore.StringType, String: "monitor1"}, + }, zap2Logs.All()[0].Context) }) } } diff --git a/pkg/signalfx-agent/pkg/core/config/config.go b/pkg/signalfx-agent/pkg/core/config/config.go index fe00431ad34..024b6cf8fc6 100644 --- a/pkg/signalfx-agent/pkg/core/config/config.go +++ b/pkg/signalfx-agent/pkg/core/config/config.go @@ -11,15 +11,15 @@ import ( "runtime" "strings" - "github.com/signalfx/signalfx-agent/pkg/utils/timeutil" - "github.com/mitchellh/hashstructure" + log "github.com/sirupsen/logrus" + "github.com/signalfx/signalfx-agent/pkg/core/common/constants" "github.com/signalfx/signalfx-agent/pkg/core/config/sources" "github.com/signalfx/signalfx-agent/pkg/core/config/validation" "github.com/signalfx/signalfx-agent/pkg/utils" "github.com/signalfx/signalfx-agent/pkg/utils/hostfs" - log "github.com/sirupsen/logrus" + "github.com/signalfx/signalfx-agent/pkg/utils/timeutil" ) const ( @@ -389,7 +389,8 @@ type CollectdConfig struct { InstanceName string `yaml:"-"` // A hack to allow custom collectd to easily specify a single monitorID via // query parameter - WriteServerQuery string `yaml:"-"` + WriteServerQuery string `yaml:"-"` + Logger log.FieldLogger `yaml:"-"` } // Validate the collectd specific config diff --git a/pkg/signalfx-agent/pkg/core/config/monitor.go b/pkg/signalfx-agent/pkg/core/config/monitor.go index 9a18851f4e9..cbbabb28522 100644 --- a/pkg/signalfx-agent/pkg/core/config/monitor.go +++ b/pkg/signalfx-agent/pkg/core/config/monitor.go @@ -8,11 +8,12 @@ import ( "strings" "github.com/mitchellh/hashstructure" + log "github.com/sirupsen/logrus" + yaml "gopkg.in/yaml.v2" + "github.com/signalfx/signalfx-agent/pkg/core/common/constants" "github.com/signalfx/signalfx-agent/pkg/core/dpfilters" "github.com/signalfx/signalfx-agent/pkg/monitors/types" - log "github.com/sirupsen/logrus" - yaml "gopkg.in/yaml.v2" ) // MonitorConfig is used to configure monitor instances. One instance of @@ -132,6 +133,9 @@ type MonitorConfig struct { // emitted by default. A metric group is simply a collection of metrics, // and they are defined in each monitor's documentation. ExtraGroups []string `yaml:"extraGroups" json:"extraGroups"` + // If this is a native collectd plugin-based monitor it will + // run its own collectd subprocess. No effect otherwise. + IsolatedCollectd bool `yaml:"isolatedCollectd" json:"isolatedCollectd"` // OtherConfig is everything else that is custom to a particular monitor OtherConfig map[string]interface{} `yaml:",inline" neverLog:"omit"` Hostname string `yaml:"-" json:"-"` diff --git a/pkg/signalfx-agent/pkg/monitors/collectd/collectd.go b/pkg/signalfx-agent/pkg/monitors/collectd/collectd.go index 1d9b3f588c3..363904d746f 100644 --- a/pkg/signalfx-agent/pkg/monitors/collectd/collectd.go +++ b/pkg/signalfx-agent/pkg/monitors/collectd/collectd.go @@ -78,12 +78,18 @@ func MainInstance() *Manager { // InitCollectd makes a new instance of a manager and initializes it, but does // not start collectd func InitCollectd(conf *config.CollectdConfig) *Manager { + logger := conf.Logger + if logger == nil { + logger = log.StandardLogger() + } + logger = logger.WithField("collectdInstance", conf.InstanceName) + manager := &Manager{ conf: conf, activeMonitors: make(map[types.MonitorID]types.Output), genericJMXUsers: make(map[types.MonitorID]bool), requestRestart: make(chan struct{}), - logger: log.WithField("collectdInstance", conf.InstanceName), + logger: logger, } manager.deleteExistingConfig() @@ -305,7 +311,7 @@ func (cm *Manager) manageCollectd(initCh chan<- struct{}, terminated chan struct go func() { scanner := utils.ChunkScanner(output) for scanner.Scan() { - logLine(scanner.Text(), cm.logger) + cm.logLine(scanner.Text()) } }() diff --git a/pkg/signalfx-agent/pkg/monitors/collectd/custom/custom.go b/pkg/signalfx-agent/pkg/monitors/collectd/custom/custom.go index 446313a60e8..6ba95eb628b 100644 --- a/pkg/signalfx-agent/pkg/monitors/collectd/custom/custom.go +++ b/pkg/signalfx-agent/pkg/monitors/collectd/custom/custom.go @@ -13,7 +13,6 @@ import ( "github.com/signalfx/signalfx-agent/pkg/core/config" "github.com/signalfx/signalfx-agent/pkg/monitors" "github.com/signalfx/signalfx-agent/pkg/monitors/collectd" - "github.com/signalfx/signalfx-agent/pkg/utils" ) func init() { @@ -120,19 +119,7 @@ func (cm *Monitor) Configure(conf *Config) error { if err != nil { return err } - - collectdConf := *collectd.MainInstance().Config() - - collectdConf.WriteServerPort = 0 - collectdConf.WriteServerQuery = "?monitorID=" + string(conf.MonitorID) - collectdConf.InstanceName = "monitor-" + string(conf.MonitorID) - collectdConf.ReadThreads = utils.FirstNonZero(conf.CollectdReadThreads, utils.MinInt(len(conf.allTemplates()), 10)) - collectdConf.WriteThreads = 1 - collectdConf.WriteQueueLimitHigh = 10000 - collectdConf.WriteQueueLimitLow = 10000 - collectdConf.IntervalSeconds = conf.IntervalSeconds - - cm.MonitorCore.SetCollectdInstance(collectd.InitCollectd(&collectdConf)) - + // always run an isolated collectd instance per monitor instance + conf.IsolatedCollectd = true return cm.SetConfigurationAndRun(conf) } diff --git a/pkg/signalfx-agent/pkg/monitors/collectd/logging.go b/pkg/signalfx-agent/pkg/monitors/collectd/logging.go index dce9dd2ab77..cb56fe22521 100644 --- a/pkg/signalfx-agent/pkg/monitors/collectd/logging.go +++ b/pkg/signalfx-agent/pkg/monitors/collectd/logging.go @@ -7,8 +7,6 @@ import ( "regexp" "strings" - log "github.com/sirupsen/logrus" - "github.com/signalfx/signalfx-agent/pkg/utils" ) @@ -18,17 +16,18 @@ var logRE = regexp.MustCompile( `(?:\[(?P\w+?)\] )?` + `(?P(?:(?P[\w-]+?): )?.*)`) -func logLine(line string, logger log.FieldLogger) { +func (cm *Manager) logLine(line string) { groups := utils.RegexpGroupMap(logRE, line) + logger := cm.logger var level string var message string if groups == nil { level = "info" message = line } else { - if groups["plugin"] != "" { - logger = logger.WithField("plugin", groups["plugin"]) + if plugin := groups["plugin"]; plugin != "" { + logger = logger.WithField("plugin", plugin) } level = groups["level"] diff --git a/pkg/signalfx-agent/pkg/monitors/collectd/monitorcore.go b/pkg/signalfx-agent/pkg/monitors/collectd/monitorcore.go index 3a39fe74d9a..510494f973d 100644 --- a/pkg/signalfx-agent/pkg/monitors/collectd/monitorcore.go +++ b/pkg/signalfx-agent/pkg/monitors/collectd/monitorcore.go @@ -31,6 +31,7 @@ type MonitorCore struct { lock sync.Mutex UsesGenericJMX bool collectdInstanceOverride *Manager + logger log.FieldLogger } // NewMonitorCore creates a new initialized but unconfigured MonitorCore with @@ -38,72 +39,90 @@ type MonitorCore struct { func NewMonitorCore(template *template.Template) *MonitorCore { return &MonitorCore{ Template: template, + logger: log.StandardLogger(), } } // Init generates a unique file name for each distinct monitor instance -func (bm *MonitorCore) Init() error { - InjectTemplateFuncs(bm.Template) +func (mc *MonitorCore) Init() error { + InjectTemplateFuncs(mc.Template) return nil } // SetCollectdInstance allows you to override the instance of collectd used by // this monitor -func (bm *MonitorCore) SetCollectdInstance(instance *Manager) { - bm.collectdInstanceOverride = instance +func (mc *MonitorCore) SetCollectdInstance(instance *Manager) { + mc.collectdInstanceOverride = instance } -func (bm *MonitorCore) collectdInstance() *Manager { - if bm.collectdInstanceOverride != nil { - return bm.collectdInstanceOverride +func (mc *MonitorCore) collectdInstance() *Manager { + if mc.collectdInstanceOverride != nil { + return mc.collectdInstanceOverride } return MainInstance() } // SetConfigurationAndRun sets the configuration to be used when rendering // templates, and writes config before queueing a collectd restart. -func (bm *MonitorCore) SetConfigurationAndRun(conf config.MonitorCustomConfig) error { - bm.lock.Lock() - defer bm.lock.Unlock() - - bm.config = conf - bm.monitorID = conf.MonitorConfigCore().MonitorID +func (mc *MonitorCore) SetConfigurationAndRun(conf config.MonitorCustomConfig) error { + mc.lock.Lock() + defer mc.lock.Unlock() + + mConf := conf.MonitorConfigCore() + mc.monitorID = mConf.MonitorID + mc.logger = mc.logger.WithFields(log.Fields{"monitorType": conf.MonitorConfigCore().Type, "monitorID": string(mc.monitorID)}) + + if mConf.IsolatedCollectd { + cconf := *MainInstance().Config() + cconf.WriteServerPort = 0 + cconf.WriteServerQuery = "?monitorID=" + string(mConf.MonitorID) + cconf.InstanceName = "monitor-" + string(mConf.MonitorID) + cconf.ReadThreads = 10 + cconf.WriteThreads = 1 + cconf.WriteQueueLimitHigh = 10000 + cconf.WriteQueueLimitLow = 10000 + cconf.IntervalSeconds = mConf.IntervalSeconds + cconf.Logger = mc.logger + mc.logger.Info(fmt.Sprintf("starting isolated configd instance %q", cconf.InstanceName)) + mc.SetCollectdInstance(InitCollectd(&cconf)) + } - bm.configFilename = fmt.Sprintf("20-%s.%s.conf", bm.Template.Name(), string(bm.monitorID)) + mc.config = conf + mc.configFilename = fmt.Sprintf("20-%s.%s.conf", mc.Template.Name(), string(mc.monitorID)) - if err := bm.WriteConfigForPlugin(); err != nil { + if err := mc.WriteConfigForPlugin(); err != nil { return err } - return bm.SetConfiguration(conf) + return mc.SetConfiguration() } // SetConfiguration adds various fields from the config to the template context // but does not render the config. -func (bm *MonitorCore) SetConfiguration(conf config.MonitorCustomConfig) error { - return bm.collectdInstance().ConfigureFromMonitor(bm.monitorID, bm.Output, bm.UsesGenericJMX) +func (mc *MonitorCore) SetConfiguration() error { + return mc.collectdInstance().ConfigureFromMonitor(mc.monitorID, mc.Output, mc.UsesGenericJMX) } // WriteConfigForPlugin will render the config template to the filesystem and // queue a collectd restart -func (bm *MonitorCore) WriteConfigForPlugin() error { +func (mc *MonitorCore) WriteConfigForPlugin() error { pluginConfigText := bytes.Buffer{} - err := bm.Template.Execute(&pluginConfigText, bm.config) + err := mc.Template.Execute(&pluginConfigText, mc.config) if err != nil { return fmt.Errorf("Could not render collectd config file for %s. Context was %#v %w", - bm.Template.Name(), bm.config, err) + mc.Template.Name(), mc.config, err) } - log.WithFields(log.Fields{ - "renderPath": bm.renderPath(), - "context": bm.config, + mc.logger.WithFields(log.Fields{ + "renderPath": mc.renderPath(), + "context": mc.config, }).Debug("Writing collectd plugin config file") - if err := WriteConfFile(pluginConfigText.String(), bm.renderPath()); err != nil { - log.WithFields(log.Fields{ + if err := WriteConfFile(pluginConfigText.String(), mc.renderPath()); err != nil { + mc.logger.WithFields(log.Fields{ "error": err, - "path": bm.renderPath(), + "path": mc.renderPath(), }).Error("Could not render collectd plugin config") return err } @@ -111,21 +130,21 @@ func (bm *MonitorCore) WriteConfigForPlugin() error { return nil } -func (bm *MonitorCore) renderPath() string { - return filepath.Join(bm.collectdInstance().ManagedConfigDir(), bm.configFilename) +func (mc *MonitorCore) renderPath() string { + return filepath.Join(mc.collectdInstance().ManagedConfigDir(), mc.configFilename) } // RemoveConfFile deletes the collectd config file for this monitor -func (bm *MonitorCore) RemoveConfFile() { - os.Remove(bm.renderPath()) +func (mc *MonitorCore) RemoveConfFile() { + os.Remove(mc.renderPath()) } // Shutdown removes the config file and restarts collectd -func (bm *MonitorCore) Shutdown() { - log.WithFields(log.Fields{ - "path": bm.renderPath(), +func (mc *MonitorCore) Shutdown() { + mc.logger.WithFields(log.Fields{ + "path": mc.renderPath(), }).Debug("Removing collectd plugin config") - bm.RemoveConfFile() - bm.collectdInstance().MonitorDidShutdown(bm.monitorID) + mc.RemoveConfFile() + mc.collectdInstance().MonitorDidShutdown(mc.monitorID) } diff --git a/tests/go.mod b/tests/go.mod index 07579995b38..bc1e83ba8cf 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -6,6 +6,7 @@ require ( github.com/Masterminds/sprig/v3 v3.2.3 github.com/docker/docker v23.0.3+incompatible github.com/docker/go-connections v0.4.0 + github.com/go-sql-driver/mysql v1.4.0 github.com/google/uuid v1.3.0 github.com/shirou/gopsutil/v3 v3.23.3 github.com/signalfx/com_signalfx_metrics_protobuf v0.0.3 diff --git a/tests/go.sum b/tests/go.sum index a93b05266e4..f08fe458d00 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -443,6 +443,7 @@ github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7 github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= github.com/go-openapi/validate v0.22.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= +github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= diff --git a/tests/receivers/smartagent/collectd-custom/custom_upstat_test.go b/tests/receivers/smartagent/collectd-custom/custom_upstat_test.go index 89fcf02e9ff..b58cbd43b41 100644 --- a/tests/receivers/smartagent/collectd-custom/custom_upstat_test.go +++ b/tests/receivers/smartagent/collectd-custom/custom_upstat_test.go @@ -20,22 +20,45 @@ package tests import ( "path" "path/filepath" + "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zaptest/observer" "github.com/signalfx/splunk-otel-collector/tests/testutils" ) func TestCustomUpstatIntegration(t *testing.T) { + core, observed := observer.New(zap.DebugLevel) path, err := filepath.Abs(path.Join(".", "testdata", "upstat")) require.NoError(t, err) testutils.AssertAllMetricsReceived(t, "all.yaml", "custom_upstat.yaml", nil, []testutils.CollectorBuilder{func(collector testutils.Collector) testutils.Collector { + collector = collector.WithLogger(zap.New(core)) if cc, ok := collector.(*testutils.CollectorContainer); ok { collector = cc.WithMount(path, "/var/collectd-python/upstat") return collector.WithEnv(map[string]string{"PLUGIN_FOLDER": "/var/collectd-python/upstat"}) } return collector.WithEnv(map[string]string{"PLUGIN_FOLDER": path}) }}) + + expectedContent := map[string]bool{ + `starting isolated configd instance "monitor-smartagentcollectdcustom"`: false, + `"name": "smartagent/collectd/custom"`: false, + `"monitorType": "collectd/custom"`: false, + `"monitorID": "smartagentcollectdcustom"`: false, + } + for _, l := range observed.All() { + for expected := range expectedContent { + if strings.Contains(l.Message, expected) { + expectedContent[expected] = true + } + } + } + for expected, found := range expectedContent { + assert.True(t, found, expected) + } } diff --git a/tests/receivers/smartagent/collectd-mysql/collectd_mysql_test.go b/tests/receivers/smartagent/collectd-mysql/collectd_mysql_test.go new file mode 100644 index 00000000000..cfd9c633937 --- /dev/null +++ b/tests/receivers/smartagent/collectd-mysql/collectd_mysql_test.go @@ -0,0 +1,193 @@ +// Copyright Splunk, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build integration + +package tests + +import ( + "context" + "database/sql" + "fmt" + "io" + "strings" + "testing" + "time" + + "github.com/go-sql-driver/mysql" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zaptest/observer" + + "github.com/signalfx/splunk-otel-collector/tests/testutils" +) + +const ( + user = "testuser" + password = "testpass" + dbName = "testdb" +) + +var mysqlServer = testutils.NewContainer().WithImage("mysql:latest").WithEnv( + map[string]string{ + "MYSQL_DATABASE": dbName, + "MYSQL_USER": user, + "MYSQL_PASSWORD": password, + "MYSQL_ROOT_PASSWORD": password, + }).WithExposedPorts("3306:3306").WithName("mysql-server").WithNetworks( + "mysql", +).WillWaitForPorts("3306").WillWaitForLogs( + "MySQL init process done. Ready for start up.", + "ready for connections. Bind-address:", +) + +func TestCollectdMySQLProvidesAllMetrics(t *testing.T) { + tc := testutils.NewTestcase(t) + defer tc.PrintLogsOnFailure() + defer tc.ShutdownOTLPReceiverSink() + + cntrs, shutdown := tc.Containers(mysqlServer) + defer shutdown() + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + rc, r, err := cntrs[0].Exec(ctx, []string{"mysql", "-uroot", "-ptestpass", "-e", "grant PROCESS on *.* TO 'testuser'@'%'; flush privileges;"}) + if r != nil { + defer func() { + if t.Failed() { + out, readErr := io.ReadAll(r) + require.NoError(t, readErr) + fmt.Printf("mysql:\n%s\n", string(out)) + } + }() + } + require.NoError(t, err) + require.Zero(t, rc) + + cfg := mysql.Config{ + User: user, + Passwd: password, + Net: "tcp", + Addr: "127.0.0.1:3306", + DBName: dbName, + } + + db, err := sql.Open("mysql", cfg.FormatDSN()) + require.NoError(t, err) + require.NoError(t, db.Ping()) + + // exercise the target server for use-based metric generation + _, err = db.Exec("CREATE TABLE a_table (name VARCHAR(255), preference VARCHAR(255))") + require.NoError(t, err) + _, err = db.Exec("ALTER TABLE a_table ADD COLUMN id INT AUTO_INCREMENT PRIMARY KEY") + require.NoError(t, err) + insert := "INSERT INTO a_table (name, preference) VALUES (?, ?)" + _, err = db.Exec(insert, "some.name", "some preference") + require.NoError(t, err) + _, err = db.Exec(insert, "another.name", "another preference") + require.NoError(t, err) + _, err = db.Exec("UPDATE a_table SET preference = 'the real preference' WHERE name = 'some.name'") + require.NoError(t, err) + rows, err := db.Query("SELECT * FROM a_table") + defer rows.Close() + require.NoError(t, err) + _, err = db.Exec("DELETE FROM a_table WHERE name = 'another.name'") + require.NoError(t, err) + + testutils.AssertAllMetricsReceived(t, "all.yaml", "isolated_config.yaml", nil, nil) +} + +func TestCollectdIsolatedLogger(t *testing.T) { + tc := testutils.NewTestcase(t) + defer tc.ShutdownOTLPReceiverSink() + + _, shutdown := tc.Containers(mysqlServer) + defer shutdown() + + for _, test := range []struct { + config string + expectedLogContent map[string]bool + unexpectedLogContent map[string]bool + }{ + { + config: "isolated_config.yaml", + expectedLogContent: map[string]bool{ + `"collectdInstance": "monitor-smartagentcollectdmysql", "monitorID": "smartagentcollectdmysql"`: false, + `"monitorType": "collectd/mysql"`: false, + `"name": "smartagent/collectd/mysql"`: false, + `mysql plugin: Failed to store query result: Access denied; you need (at least one of) the PROCESS privilege(s) for this operation {"kind": "receiver", "name": "smartagent/collectd/mysql", "data_type": "metrics", "collectdInstance": "monitor-smartagentcollectdmysql", "monitorID": "smartagentcollectdmysql", "monitorType": "collectd/mysql"`: false, + `starting isolated configd instance "monitor-smartagentcollectdmysql"`: false, + }, + unexpectedLogContent: map[string]bool{ + `"collectdInstance": "global", "monitorID": "smartagentcollectdmysql"`: false, + }, + }, + { + config: "not_isolated_config.yaml", + expectedLogContent: map[string]bool{ + `mysql plugin: Failed to store query result: Access denied; you need (at least one of) the PROCESS privilege(s) for this operation {"kind": "receiver", "name": "smartagent/collectd/mysql", "data_type": "metrics", "name": "default", "collectdInstance": "global"}`: false, + }, + unexpectedLogContent: map[string]bool{ + `starting isolated configd instance`: false, + `"collectdInstance": "monitor-smartagentcollectdmysql"`: false, + }, + }, + } { + t.Run(test.config, func(t *testing.T) { + expectedContent := test.expectedLogContent + unexpectedContent := test.unexpectedLogContent + core, observed := observer.New(zap.DebugLevel) + t.Cleanup(func() { + if t.Failed() { + fmt.Printf("Logs: \n") + for _, statement := range observed.All() { + fmt.Printf("%v\n", statement) + } + } + }) + + _, shutdownCollector := tc.SplunkOtelCollector( + test.config, func(collector testutils.Collector) testutils.Collector { + return collector.WithLogger(zap.New(core)) + }) + defer shutdownCollector() + + require.Eventually(t, func() bool { + for _, l := range observed.All() { + for expected := range expectedContent { + if strings.Contains(l.Message, expected) { + expectedContent[expected] = true + } + } + for unexpected := range unexpectedContent { + if strings.Contains(l.Message, unexpected) { + unexpectedContent[unexpected] = true + } + } + } + for _, found := range expectedContent { + if !found { + return false + } + } + for _, found := range unexpectedContent { + if found { + return false + } + } + return true + }, time.Minute, time.Second, "expected: %v, unexpected: %v", expectedContent, unexpectedContent) + }) + } +} diff --git a/tests/receivers/smartagent/collectd-mysql/testdata/isolated_config.yaml b/tests/receivers/smartagent/collectd-mysql/testdata/isolated_config.yaml new file mode 100644 index 00000000000..e33ad16b121 --- /dev/null +++ b/tests/receivers/smartagent/collectd-mysql/testdata/isolated_config.yaml @@ -0,0 +1,25 @@ +receivers: + smartagent/collectd/mysql: + type: collectd/mysql + host: 127.0.0.1 + port: 3306 + username: testuser + password: testpass + databases: + - name: testdb + innodbStats: true + isolatedCollectd: true + extraMetrics: ["*"] + intervalSeconds: 1 + +exporters: + otlp: + endpoint: "${OTLP_ENDPOINT}" + tls: + insecure: true + +service: + pipelines: + metrics: + receivers: [smartagent/collectd/mysql] + exporters: [otlp] diff --git a/tests/receivers/smartagent/collectd-mysql/testdata/not_isolated_config.yaml b/tests/receivers/smartagent/collectd-mysql/testdata/not_isolated_config.yaml new file mode 100644 index 00000000000..5994b366e1f --- /dev/null +++ b/tests/receivers/smartagent/collectd-mysql/testdata/not_isolated_config.yaml @@ -0,0 +1,24 @@ +receivers: + smartagent/collectd/mysql: + type: collectd/mysql + host: 127.0.0.1 + port: 3306 + username: testuser + password: testpass + databases: + - name: testdb + innodbStats: true + extraMetrics: ["*"] + intervalSeconds: 1 + +exporters: + otlp: + endpoint: "${OTLP_ENDPOINT}" + tls: + insecure: true + +service: + pipelines: + metrics: + receivers: [smartagent/collectd/mysql] + exporters: [otlp] diff --git a/tests/receivers/smartagent/collectd-mysql/testdata/resource_metrics/all.yaml b/tests/receivers/smartagent/collectd-mysql/testdata/resource_metrics/all.yaml new file mode 100644 index 00000000000..cdf792aa84e --- /dev/null +++ b/tests/receivers/smartagent/collectd-mysql/testdata/resource_metrics/all.yaml @@ -0,0 +1,637 @@ +resource_metrics: + - scope_metrics: + - metrics: +# Most of the metrics aren't evaluated because they are only generated w/ applicable interaction. +# TODO: revisit this w/ updated receiver +# - name: cache_result.cache_size +# - name: cache_result.qcache-hits +# - name: cache_result.qcache-inserts +# - name: cache_result.qcache-not_cached +# - name: cache_result.qcache-prunes +# - name: cache_size.qcache + - name: mysql_commands.admin_commands + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql +# - name: mysql_commands.alter_db +# - name: mysql_commands.alter_db_upgrade +# - name: mysql_commands.alter_event +# - name: mysql_commands.alter_function +# - name: mysql_commands.alter_procedure +# - name: mysql_commands.alter_server + - name: mysql_commands.alter_table + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql +# - name: mysql_commands.alter_tablespace +# - name: mysql_commands.alter_user +# - name: mysql_commands.analyze +# - name: mysql_commands.assign_to_keycache +# - name: mysql_commands.begin +# - name: mysql_commands.binlog +# - name: mysql_commands.call_procedure + - name: mysql_commands.change_db + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql +# - name: mysql_commands.change_master +# - name: mysql_commands.check +# - name: mysql_commands.checksum +# - name: mysql_commands.commit + - name: mysql_commands.create_db + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql +# - name: mysql_commands.create_event +# - name: mysql_commands.create_function +# - name: mysql_commands.create_index +# - name: mysql_commands.create_procedure +# - name: mysql_commands.create_server + - name: mysql_commands.create_table + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql +# - name: mysql_commands.create_trigger +# - name: mysql_commands.create_udf +# - name: mysql_commands.create_user +# - name: mysql_commands.create_view +# - name: mysql_commands.dealloc_sql + - name: mysql_commands.delete + type: IntMonotonicCumulativeSum +# - name: mysql_commands.delete_multi +# - name: mysql_commands.do +# - name: mysql_commands.drop_db +# - name: mysql_commands.drop_event +# - name: mysql_commands.drop_function +# - name: mysql_commands.drop_index +# - name: mysql_commands.drop_procedure +# - name: mysql_commands.drop_server +# - name: mysql_commands.drop_table +# - name: mysql_commands.drop_trigger +# - name: mysql_commands.drop_user +# - name: mysql_commands.drop_view +# - name: mysql_commands.empty_query +# - name: mysql_commands.execute_sql + - name: mysql_commands.flush + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql +# - name: mysql_commands.get_diagnostics + - name: mysql_commands.grant + type: IntMonotonicCumulativeSum +# - name: mysql_commands.ha_close +# - name: mysql_commands.ha_open +# - name: mysql_commands.ha_read +# - name: mysql_commands.help + - name: mysql_commands.insert + type: IntMonotonicCumulativeSum +# - name: mysql_commands.insert_select +# - name: mysql_commands.install_plugin +# - name: mysql_commands.kill +# - name: mysql_commands.load +# - name: mysql_commands.lock_tables +# - name: mysql_commands.optimize +# - name: mysql_commands.preload_keys +# - name: mysql_commands.prepare_sql +# - name: mysql_commands.purge +# - name: mysql_commands.purge_before_date +# - name: mysql_commands.release_savepoint +# - name: mysql_commands.rename_table +# - name: mysql_commands.rename_user +# - name: mysql_commands.repair +# - name: mysql_commands.replace +# - name: mysql_commands.replace_select +# - name: mysql_commands.reset +# - name: mysql_commands.resignal +# - name: mysql_commands.revoke +# - name: mysql_commands.revoke_all +# - name: mysql_commands.rollback +# - name: mysql_commands.rollback_to_savepoint +# - name: mysql_commands.savepoint + - name: mysql_commands.select + type: IntMonotonicCumulativeSum + - name: mysql_commands.set_option + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql +# - name: mysql_commands.show_binlog_events +# - name: mysql_commands.show_binlogs +# - name: mysql_commands.show_charsets +# - name: mysql_commands.show_collations +# - name: mysql_commands.show_create_db +# - name: mysql_commands.show_create_event +# - name: mysql_commands.show_create_func +# - name: mysql_commands.show_create_proc +# - name: mysql_commands.show_create_table +# - name: mysql_commands.show_create_trigger +# - name: mysql_commands.show_databases +# - name: mysql_commands.show_engine_logs +# - name: mysql_commands.show_engine_mutex +# - name: mysql_commands.show_engine_status +# - name: mysql_commands.show_errors +# - name: mysql_commands.show_events +# - name: mysql_commands.show_fields +# - name: mysql_commands.show_function_code +# - name: mysql_commands.show_function_status +# - name: mysql_commands.show_grants +# - name: mysql_commands.show_keys +# - name: mysql_commands.show_master_status +# - name: mysql_commands.show_open_tables +# - name: mysql_commands.show_plugins +# - name: mysql_commands.show_privileges +# - name: mysql_commands.show_procedure_code +# - name: mysql_commands.show_procedure_status +# - name: mysql_commands.show_processlist +# - name: mysql_commands.show_profile +# - name: mysql_commands.show_profiles +# - name: mysql_commands.show_relaylog_events +# - name: mysql_commands.show_slave_hosts +# - name: mysql_commands.show_slave_status + - name: mysql_commands.show_status + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql +# - name: mysql_commands.show_storage_engines +# - name: mysql_commands.show_table_status +# - name: mysql_commands.show_tables +# - name: mysql_commands.show_triggers +# - name: mysql_commands.show_variables +# - name: mysql_commands.show_warnings +# - name: mysql_commands.signal +# - name: mysql_commands.slave_start +# - name: mysql_commands.slave_stop +# - name: mysql_commands.truncate +# - name: mysql_commands.uninstall_plugin +# - name: mysql_commands.unlock_tables + - name: mysql_commands.update + type: IntMonotonicCumulativeSum +# - name: mysql_commands.update_multi +# - name: mysql_commands.xa_commit +# - name: mysql_commands.xa_end +# - name: mysql_commands.xa_prepare +# - name: mysql_commands.xa_recover +# - name: mysql_commands.xa_rollback +# - name: mysql_commands.xa_start + - name: mysql_handler.commit + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_handler.delete + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_handler.prepare + type: IntMonotonicCumulativeSum + - name: mysql_handler.read_first + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_handler.read_key + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_handler.read_next + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql +# - name: mysql_handler.read_prev +# - name: mysql_handler.read_rnd + - name: mysql_handler.read_rnd_next + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql +# - name: mysql_handler.rollback +# - name: mysql_handler.savepoint +# - name: mysql_handler.savepoint_rollback + - name: mysql_handler.update + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_handler.write + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_handler.external_lock + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_locks.immediate + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_locks.waited + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_octets.rx + type: IntMonotonicCumulativeSum + attributes: + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_octets.tx + type: IntMonotonicCumulativeSum + attributes: + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_select.full_join + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_select.full_range_join + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_select.range + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_select.range_check + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_select.scan + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_sort_merge_passes + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_sort.range + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_sort_rows + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_sort.scan + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_slow_queries + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: threads.cached + type: IntGauge + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: threads.connected + type: IntGauge + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: threads.running + type: IntGauge + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: total_threads.created + type: IntMonotonicCumulativeSum + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_bpool_pages.data + type: IntGauge + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_bpool_pages.dirty + type: IntGauge + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_bpool_pages.free + type: IntGauge + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_bpool_pages.misc + type: IntGauge + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_bpool_pages.total + type: IntGauge + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_bpool_counters.pages_flushed + type: IntMonotonicCumulativeSum + - name: mysql_bpool_counters.read_ahead_rnd + type: IntMonotonicCumulativeSum + - name: mysql_bpool_counters.read_ahead + type: IntMonotonicCumulativeSum + - name: mysql_bpool_counters.read_ahead_evicted + type: IntMonotonicCumulativeSum + - name: mysql_bpool_counters.read_requests + type: IntMonotonicCumulativeSum + - name: mysql_bpool_counters.reads + type: IntMonotonicCumulativeSum + - name: mysql_bpool_counters.wait_free + type: IntMonotonicCumulativeSum + - name: mysql_bpool_counters.write_requests + type: IntMonotonicCumulativeSum + - name: mysql_bpool_bytes.data + type: IntGauge + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_bpool_bytes.dirty + type: IntGauge + attributes: + dsname: value + host: + monitorID: smartagentcollectdmysql + plugin: mysql + plugin_instance: _testdb + system.type: mysql + - name: mysql_innodb_data.fsyncs + type: IntMonotonicCumulativeSum + - name: mysql_innodb_data.read + type: IntMonotonicCumulativeSum + - name: mysql_innodb_data.reads + type: IntMonotonicCumulativeSum + - name: mysql_innodb_data.writes + type: IntMonotonicCumulativeSum + - name: mysql_innodb_data.written + type: IntMonotonicCumulativeSum + - name: mysql_innodb_dblwr.writes + type: IntMonotonicCumulativeSum + - name: mysql_innodb_dblwr.written + type: IntMonotonicCumulativeSum + - name: mysql_innodb_log.waits + type: IntMonotonicCumulativeSum + - name: mysql_innodb_log.write_requests + type: IntMonotonicCumulativeSum + - name: mysql_innodb_log.writes + type: IntMonotonicCumulativeSum + - name: mysql_innodb_log.fsyncs + type: IntMonotonicCumulativeSum + - name: mysql_innodb_log.written + type: IntMonotonicCumulativeSum + - name: mysql_innodb_pages.created + type: IntMonotonicCumulativeSum + - name: mysql_innodb_pages.read + type: IntMonotonicCumulativeSum + - name: mysql_innodb_pages.written + type: IntMonotonicCumulativeSum + - name: mysql_innodb_row_lock.time + type: IntMonotonicCumulativeSum + - name: mysql_innodb_row_lock.waits + type: IntMonotonicCumulativeSum + - name: mysql_innodb_rows.deleted + type: IntMonotonicCumulativeSum + - name: mysql_innodb_rows.inserted + type: IntMonotonicCumulativeSum + - name: mysql_innodb_rows.read + type: IntMonotonicCumulativeSum + - name: mysql_innodb_rows.updated + type: IntMonotonicCumulativeSum + - name: bytes.buffer_pool_size + type: IntGauge + - name: bytes.ibuf_size + type: IntGauge + - name: gauge.file_num_open_files + type: IntGauge + - name: gauge.innodb_activity_count + type: IntGauge + - name: gauge.trx_rseg_history_len + type: IntGauge + - name: mysql_locks.lock_deadlocks + type: IntMonotonicCumulativeSum + - name: mysql_locks.lock_row_lock_current_waits + type: IntMonotonicCumulativeSum + - name: mysql_locks.lock_timeouts + type: IntMonotonicCumulativeSum + - name: operations.adaptive_hash_searches + type: IntMonotonicCumulativeSum + - name: operations.dml_deletes + type: IntMonotonicCumulativeSum + - name: operations.dml_inserts + type: IntMonotonicCumulativeSum + - name: operations.dml_updates + type: IntMonotonicCumulativeSum + - name: operations.ibuf_merges_delete + type: IntMonotonicCumulativeSum + - name: operations.ibuf_merges_delete_mark + type: IntMonotonicCumulativeSum + - name: operations.ibuf_merges_discard_delete + type: IntMonotonicCumulativeSum + - name: operations.ibuf_merges_discard_delete_mark + type: IntMonotonicCumulativeSum + - name: operations.ibuf_merges_discard_insert + type: IntMonotonicCumulativeSum + - name: operations.ibuf_merges_insert + type: IntMonotonicCumulativeSum + - name: operations.innodb_rwlock_s_os_waits + type: IntMonotonicCumulativeSum + - name: operations.innodb_rwlock_s_spin_rounds + type: IntMonotonicCumulativeSum + - name: operations.innodb_rwlock_s_spin_waits + type: IntMonotonicCumulativeSum + - name: operations.innodb_rwlock_x_os_waits + type: IntMonotonicCumulativeSum + - name: operations.innodb_rwlock_x_spin_rounds + type: IntMonotonicCumulativeSum + - name: operations.innodb_rwlock_x_spin_waits + type: IntMonotonicCumulativeSum + - name: operations.os_log_bytes_written + type: IntMonotonicCumulativeSum + - name: operations.os_log_pending_fsyncs + type: IntMonotonicCumulativeSum + - name: operations.os_log_pending_writes + type: IntMonotonicCumulativeSum