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 14 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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ Main (unreleased)

- Bump snmp_exporter and embedded modules to 0.27.0. Add support for multi-module handling by comma separation and expose argument to increase SNMP polling concurrency for `prometheus.exporter.snmp`. (@v-zhuravlev)

- (_Experimental_) Add enable/disable collector configurability to `database_observability.mysql`. This removes the `query_samples_enabled` argument, now configurable via enable/disable collector. (@fridgepoet)

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,30 @@ 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. | `"10s"` | no
`query_samples_enabled` | `bool` | Whether to enable collection of query samples. | `true` | no
Name | Type | Description | Default | Required
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

----------------------|----------------------|---------------------------------------------------------------|---------|----------
`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. | `"10s"` | no
`enable_collectors` | `list(string)` | A list of [collectors][] to enable on top of the default set. | | no
`disable_collectors` | `list(string)` | A list of [collectors][] to disable from the default set. | | no
fridgepoet marked this conversation as resolved.
Show resolved Hide resolved

[collectors]: #supported-collectors

## Blocks

The `database_observability.mysql` component doesn't support any blocks. You can configure this component with arguments.

### Supported Collectors

The full list of supported collectors is:

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


fridgepoet marked this conversation as resolved.
Show resolved Hide resolved
## Example

```alloy
Expand Down
84 changes: 59 additions & 25 deletions internal/component/database_observability/mysql/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ import (
"github.com/grafana/alloy/syntax/alloytypes"
)

const name = "database_observability.mysql"
const (
name = "database_observability.mysql"
querySample = "query_sample"
schemaTable = "schema_table"
fridgepoet marked this conversation as resolved.
Show resolved Hide resolved
)

func init() {
component.Register(component.Registration{
Expand All @@ -49,18 +53,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: 10 * time.Second,
QuerySamplesEnabled: true,
CollectInterval: 10 * time.Second,
}

func (a *Arguments) SetToDefault() {
Expand Down Expand Up @@ -206,6 +208,33 @@ 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{
querySample: true,
schemaTable: true,
}

for _, disabled := range a.DisableCollectors {
for c := range collectors {
if c == disabled {
collectors[c] = false
break
}
}
fridgepoet marked this conversation as resolved.
Show resolved Hide resolved
}
for _, enabled := range a.EnableCollectors {
for c := range collectors {
if c == enabled {
collectors[c] = true
break
}
}
}

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 +251,9 @@ func (c *Component) startCollectors() error {

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

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

if collectors[querySample] {
qsCollector, err := collector.NewQuerySample(collector.QuerySampleArguments{
DB: dbConnection,
InstanceKey: c.instanceKey,
Expand All @@ -241,23 +272,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[schemaTable] {
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
108 changes: 108 additions & 0 deletions internal/component/database_observability/mysql/component_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package mysql

import (
"testing"

"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{
querySample: true,
schemaTable: 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{
querySample: true,
schemaTable: 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{
querySample: false,
schemaTable: 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{
querySample: true,
schemaTable: 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{
querySample: true,
schemaTable: false,
}, actualCollectors)
})
}