Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

database_observability: make collectors configurable #2530

Merged
merged 24 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3adef90
Add logger to ConnectionInfo
fridgepoet Jan 24, 2025
4f8fb73
Add enable collectors option
fridgepoet Jan 24, 2025
6a20b35
Add logger to test
fridgepoet Jan 24, 2025
86fa050
Merge remote-tracking branch 'origin/main' into db-o11y-add-enabled-c…
fridgepoet Jan 28, 2025
2e6e6fc
polish up PR and add disable collectors
fridgepoet Jan 28, 2025
2146423
add collector
fridgepoet Jan 28, 2025
3e3943a
Remove a comment
fridgepoet Jan 28, 2025
07d577c
Revert "Add logger to ConnectionInfo"
fridgepoet Jan 28, 2025
26aec8d
Revert "Add logger to test"
fridgepoet Jan 28, 2025
65ea414
update changelog
fridgepoet Jan 28, 2025
ddf1607
fix condition
fridgepoet Jan 29, 2025
5ded38f
Update docs, change collector strings
fridgepoet Jan 29, 2025
6d5818a
Add to changelog
fridgepoet Jan 29, 2025
b0cea25
Fix format in changelog
fridgepoet Jan 29, 2025
1f179e6
Use collector constants, remove for loop
fridgepoet Jan 29, 2025
076cf25
Merge remote-tracking branch 'origin/main' into db-o11y-add-enabled-c…
fridgepoet Jan 29, 2025
cb61613
Remove formatting of const in component
fridgepoet Jan 29, 2025
a63e3e7
Merge branch 'main' into db-o11y-add-enabled-collectors
fridgepoet Jan 30, 2025
3c4f0da
Update docs/sources/reference/components/database_observability/datab…
fridgepoet Feb 3, 2025
070a3c5
Update docs/sources/reference/components/database_observability/datab…
fridgepoet Feb 4, 2025
a41ab55
Merge remote-tracking branch 'origin/main' into db-o11y-add-enabled-c…
fridgepoet Feb 4, 2025
2123d31
Remove link about collectors which has been moved below the table alr…
fridgepoet Feb 4, 2025
08b305a
Remove redundant enabled by default column
fridgepoet Feb 4, 2025
9169397
Merge branch 'main' into db-o11y-add-enabled-collectors
fridgepoet Feb 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Main (unreleased)
- Better error handling for components (@cristiangreco)
- Add namespace to `connection_info` metric (@cristiangreco)
- Added table columns parsing (@cristiagreco)
- Add enable/disable collector configurability to `database_observability.mysql`. This removes the `query_samples_enabled` argument, now configurable via enable/disable collector. (@fridgepoet)

### Bugfixes

Expand All @@ -49,13 +50,6 @@ Main (unreleased)

- Add support for pushv1.PusherService Connect API in `pyroscope.receive_http`. (@simonswine)

- Add support for path prefixes in `pyroscope.scrape` to allow scraping targets behind a proxy or with custom URL paths. (@korniltsev)

- Fixes godeltaprof hiding (renaming `godeltaprof_*` profile names to regular ones). (@korniltsev)

- Change profile handling in `pyroscope.receive_http` and `pyroscope.write` components to use in-memory processing instead of pipes. (@marcsanmi)


v1.6.1
-----------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,20 @@ database_observability.mysql "<LABEL>" {

You can use the following arguments with `database_observability.mysql`:

Name | Type | Description | Default | Required
------------------------|----------------------|----------------------------------------------------------|---------|---------
`data_source_name` | `secret` | [Data Source Name][] for the MySQL server to connect to. | | yes
`forward_to` | `list(LogsReceiver)` | Where to forward log entries after processing. | | yes
`collect_interval` | `duration` | How frequently to collect information from database. | `"1m"` | no
`query_samples_enabled` | `bool` | Whether to enable collection of query samples. | `true` | no
Name | Type | Description | Default | Required
----------------------|----------------------|-----------------------------------------------------------|---------|----------
`data_source_name` | `secret` | [Data Source Name][] for the MySQL server to connect to. | | yes
`forward_to` | `list(LogsReceiver)` | Where to forward log entries after processing. | | yes
`collect_interval` | `duration` | How frequently to collect information from database. | `"1m"` | no
`disable_collectors` | `list(string)` | A list of collectors to disable from the default set. | | no
`enable_collectors` | `list(string)` | A list of collectors to enable on top of the default set. | | no

The following collectors are enabled by default:

Name | Description
----------------|-------------------------------------------------------
`query_sample` | Collect query samples.
`schema_table` | Collect schemas and tables from `information_schema`.

## Blocks

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"go.uber.org/atomic"
)

const ConnectionInfoName = "connection_info"

var rdsRegex = regexp.MustCompile(`(?P<identifier>[^\.]+)\.([^\.]+)\.(?P<region>[^\.]+)\.rds\.amazonaws\.com`)

type ConnectionInfoArguments struct {
Expand Down Expand Up @@ -44,7 +46,7 @@ func NewConnectionInfo(args ConnectionInfoArguments) (*ConnectionInfo, error) {
}

func (c *ConnectionInfo) Name() string {
return "ConnectionInfo"
return ConnectionInfoName
}

func (c *ConnectionInfo) Start(ctx context.Context) error {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
const (
OP_QUERY_SAMPLE = "query_sample"
OP_QUERY_PARSED_TABLE_NAME = "query_parsed_table_name"
QuerySampleName = "query_sample"
)

const selectQuerySamples = `
Expand Down Expand Up @@ -67,7 +68,7 @@ func NewQuerySample(args QuerySampleArguments) (*QuerySample, error) {
}

func (c *QuerySample) Name() string {
return "QuerySample"
return QuerySampleName
}

func (c *QuerySample) Start(ctx context.Context) error {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const (
OP_SCHEMA_DETECTION = "schema_detection"
OP_TABLE_DETECTION = "table_detection"
OP_CREATE_STATEMENT = "create_statement"
SchemaTableName = "schema_table"
)

const (
Expand Down Expand Up @@ -127,7 +128,7 @@ func NewSchemaTable(args SchemaTableArguments) (*SchemaTable, error) {
}

func (c *SchemaTable) Name() string {
return "SchemaTable"
return SchemaTableName
}

func (c *SchemaTable) Start(ctx context.Context) error {
Expand Down
72 changes: 48 additions & 24 deletions internal/component/database_observability/mysql/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,16 @@ var (
_ syntax.Validator = (*Arguments)(nil)
)

// TODO(cristian) consider using something like "enabled_collectors"
// to allow users to enable/disable collectors.
type Arguments struct {
DataSourceName alloytypes.Secret `alloy:"data_source_name,attr"`
CollectInterval time.Duration `alloy:"collect_interval,attr,optional"`
QuerySamplesEnabled bool `alloy:"query_samples_enabled,attr,optional"`
ForwardTo []loki.LogsReceiver `alloy:"forward_to,attr"`
DataSourceName alloytypes.Secret `alloy:"data_source_name,attr"`
CollectInterval time.Duration `alloy:"collect_interval,attr,optional"`
ForwardTo []loki.LogsReceiver `alloy:"forward_to,attr"`
EnableCollectors []string `alloy:"enable_collectors,attr,optional"`
DisableCollectors []string `alloy:"disable_collectors,attr,optional"`
}

var DefaultArguments = Arguments{
CollectInterval: 1 * time.Minute,
QuerySamplesEnabled: true,
CollectInterval: 1 * time.Minute,
}

func (a *Arguments) SetToDefault() {
Expand Down Expand Up @@ -206,6 +204,27 @@ func (c *Component) Update(args component.Arguments) error {
return nil
}

func enableOrDisableCollectors(a Arguments) map[string]bool {
// configurable collectors and their default enabled/disabled value
collectors := map[string]bool{
collector.QuerySampleName: true,
collector.SchemaTableName: true,
}

for _, disabled := range a.DisableCollectors {
if _, ok := collectors[disabled]; ok {
collectors[disabled] = false
}
}
for _, enabled := range a.EnableCollectors {
if _, ok := collectors[enabled]; ok {
collectors[enabled] = true
}
}

return collectors
}

func (c *Component) startCollectors() error {
dbConnection, err := sql.Open("mysql", formatDSN(string(c.args.DataSourceName), "parseTime=true"))
if err != nil {
Expand All @@ -222,7 +241,9 @@ func (c *Component) startCollectors() error {

entryHandler := loki.NewEntryHandler(c.handler.Chan(), func() {})

if c.args.QuerySamplesEnabled {
collectors := enableOrDisableCollectors(c.args)

if collectors[collector.QuerySampleName] {
qsCollector, err := collector.NewQuerySample(collector.QuerySampleArguments{
DB: dbConnection,
InstanceKey: c.instanceKey,
Expand All @@ -241,23 +262,26 @@ func (c *Component) startCollectors() error {
c.collectors = append(c.collectors, qsCollector)
}

stCollector, err := collector.NewSchemaTable(collector.SchemaTableArguments{
DB: dbConnection,
InstanceKey: c.instanceKey,
CollectInterval: c.args.CollectInterval,
EntryHandler: entryHandler,
Logger: c.opts.Logger,
})
if err != nil {
level.Error(c.opts.Logger).Log("msg", "failed to create SchemaTable collector", "err", err)
return err
}
if err := stCollector.Start(context.Background()); err != nil {
level.Error(c.opts.Logger).Log("msg", "failed to start SchemaTable collector", "err", err)
return err
if collectors[collector.QuerySampleName] {
stCollector, err := collector.NewSchemaTable(collector.SchemaTableArguments{
DB: dbConnection,
InstanceKey: c.instanceKey,
CollectInterval: c.args.CollectInterval,
EntryHandler: entryHandler,
Logger: c.opts.Logger,
})
if err != nil {
level.Error(c.opts.Logger).Log("msg", "failed to create SchemaTable collector", "err", err)
return err
}
if err := stCollector.Start(context.Background()); err != nil {
level.Error(c.opts.Logger).Log("msg", "failed to start SchemaTable collector", "err", err)
return err
}
c.collectors = append(c.collectors, stCollector)
}
c.collectors = append(c.collectors, stCollector)

// Connection Info collector is always enabled
ciCollector, err := collector.NewConnectionInfo(collector.ConnectionInfoArguments{
DSN: string(c.args.DataSourceName),
Registry: c.registry,
Expand Down
130 changes: 130 additions & 0 deletions internal/component/database_observability/mysql/component_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package mysql

import (
"testing"

"github.com/grafana/alloy/internal/component/database_observability/mysql/collector"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/grafana/alloy/syntax"
)

func Test_enableOrDisableCollectors(t *testing.T) {
t.Run("nothing specified (default behavior)", func(t *testing.T) {
var exampleDBO11yAlloyConfig = `
data_source_name = ""
forward_to = []
`

var args Arguments
err := syntax.Unmarshal([]byte(exampleDBO11yAlloyConfig), &args)
require.NoError(t, err)

actualCollectors := enableOrDisableCollectors(args)

assert.Equal(t, map[string]bool{
collector.QuerySampleName: true,
collector.SchemaTableName: true,
}, actualCollectors)
})

t.Run("enable collectors", func(t *testing.T) {
var exampleDBO11yAlloyConfig = `
data_source_name = ""
forward_to = []
enable_collectors = ["query_sample", "schema_table"]
`

var args Arguments
err := syntax.Unmarshal([]byte(exampleDBO11yAlloyConfig), &args)
require.NoError(t, err)

actualCollectors := enableOrDisableCollectors(args)

assert.Equal(t, map[string]bool{
collector.QuerySampleName: true,
collector.SchemaTableName: true,
}, actualCollectors)
})

t.Run("disable collectors", func(t *testing.T) {
var exampleDBO11yAlloyConfig = `
data_source_name = ""
forward_to = []
disable_collectors = ["query_sample", "schema_table"]
`

var args Arguments
err := syntax.Unmarshal([]byte(exampleDBO11yAlloyConfig), &args)
require.NoError(t, err)

actualCollectors := enableOrDisableCollectors(args)

assert.Equal(t, map[string]bool{
collector.QuerySampleName: false,
collector.SchemaTableName: false,
}, actualCollectors)
})

t.Run("enable collectors takes precedence over disable collectors", func(t *testing.T) {
var exampleDBO11yAlloyConfig = `
data_source_name = ""
forward_to = []
disable_collectors = ["query_sample", "schema_table"]
enable_collectors = ["query_sample", "schema_table"]
`

var args Arguments
err := syntax.Unmarshal([]byte(exampleDBO11yAlloyConfig), &args)
require.NoError(t, err)

actualCollectors := enableOrDisableCollectors(args)

assert.Equal(t, map[string]bool{
collector.QuerySampleName: true,
collector.SchemaTableName: true,
}, actualCollectors)
})

t.Run("enabling one and disabling one", func(t *testing.T) {
var exampleDBO11yAlloyConfig = `
data_source_name = ""
forward_to = []
disable_collectors = ["schema_table"]
enable_collectors = ["query_sample"]
`

var args Arguments
err := syntax.Unmarshal([]byte(exampleDBO11yAlloyConfig), &args)
require.NoError(t, err)

actualCollectors := enableOrDisableCollectors(args)

assert.Equal(t, map[string]bool{
collector.QuerySampleName: true,
collector.SchemaTableName: false,
}, actualCollectors)
})

t.Run("unknown collectors are ignored", func(t *testing.T) {
var exampleDBO11yAlloyConfig = `
data_source_name = ""
forward_to = []
enable_collectors = ["some_string"]
disable_collectors = ["another_string"]
`

var args Arguments
err := syntax.Unmarshal([]byte(exampleDBO11yAlloyConfig), &args)
require.NoError(t, err)

actualCollectors := enableOrDisableCollectors(args)

assert.Equal(t, map[string]bool{
collector.QuerySampleName: true,
collector.SchemaTableName: true,
}, actualCollectors)
})

}
Loading