Skip to content

Commit ae28536

Browse files
authored
Merge pull request #1 from sv-splunk/oracledbreceiver_events
Oracledbreceiver events
2 parents a6b0f06 + 5c41bae commit ae28536

35 files changed

+2059
-38
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: oracledbreceiver
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Support query-level log collection, fetching top N query metrics and details of currently running queries (samples).
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [37478]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext: |
19+
With the introduction of query level log collection, the oracledbreceiver can now send metrics for Top N queries,
20+
filtered based on the highest CPU time consumed. The number of queries can be configured. This helps the user to have
21+
a better understanding of the database operations. In addition to this, query samples can be collected for the currently
22+
executing queries. This enhancement enables users to proactively monitor and manage the database performance.
23+
24+
# If your change doesn't affect end users or the exported elements of any package,
25+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
26+
# Optional: The change log or logs in which this entry should be included.
27+
# e.g. '[user]' or '[user, api]'
28+
# Include 'user' if the change is relevant to end users.
29+
# Include 'api' if there is a change to a library API.
30+
# Default: '[user]'
31+
change_logs: [user]

receiver/oracledbreceiver/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
<!-- status autogenerated section -->
44
| Status | |
55
| ------------- |-----------|
6-
| Stability | [alpha]: metrics |
6+
| Stability | [development]: logs |
7+
| | [alpha]: metrics |
78
| Distributions | [contrib] |
89
| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Areceiver%2Foracledb%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Areceiver%2Foracledb) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Areceiver%2Foracledb%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Areceiver%2Foracledb) |
910
| Code coverage | [![codecov](https://codecov.io/github/open-telemetry/opentelemetry-collector-contrib/graph/main/badge.svg?component=receiver_oracledb)](https://app.codecov.io/gh/open-telemetry/opentelemetry-collector-contrib/tree/main/?components%5B0%5D=receiver_oracledb&displayType=list) |
1011
| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@dmitryax](https://www.github.com/dmitryax), [@crobert-1](https://www.github.com/crobert-1), [@atoulme](https://www.github.com/atoulme) |
1112

13+
[development]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#development
1214
[alpha]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#alpha
1315
[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib
1416
<!-- end autogenerated section -->

receiver/oracledbreceiver/config.go

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,31 @@ import (
1717
)
1818

1919
var (
20-
errBadDataSource = errors.New("datasource is invalid")
21-
errBadEndpoint = errors.New("endpoint must be specified as host:port")
22-
errBadPort = errors.New("invalid port in endpoint")
23-
errEmptyEndpoint = errors.New("endpoint must be specified")
24-
errEmptyPassword = errors.New("password must be set")
25-
errEmptyService = errors.New("service must be specified")
26-
errEmptyUsername = errors.New("username must be set")
20+
errBadDataSource = errors.New("datasource is invalid")
21+
errBadEndpoint = errors.New("endpoint must be specified as host:port")
22+
errBadPort = errors.New("invalid port in endpoint")
23+
errEmptyEndpoint = errors.New("endpoint must be specified")
24+
errEmptyPassword = errors.New("password must be set")
25+
errEmptyService = errors.New("service must be specified")
26+
errEmptyUsername = errors.New("username must be set")
27+
errMaxQuerySampleCount = errors.New("`max_query_sample_count` must be between 1 and 10000")
28+
errTopQueryCount = errors.New("`top_query_count` must be between 1 and 200 and less than or equal to `max_query_sample_count`")
29+
errQueryCacheSize = errors.New("`query_cache_size` must be strictly positive")
2730
)
2831

32+
type TopQueryCollection struct {
33+
MaxQuerySampleCount uint `mapstructure:"max_query_sample_count"`
34+
TopQueryCount uint `mapstructure:"top_query_count"`
35+
QueryCacheSize int `mapstructure:"query_cache_size"`
36+
}
37+
38+
type QuerySample struct {
39+
MaxRowsPerQuery uint64 `mapstructure:"max_rows_per_query"`
40+
41+
// prevent unkeyed literal initialization
42+
_ struct{}
43+
}
44+
2945
type Config struct {
3046
DataSource string `mapstructure:"datasource"`
3147
Endpoint string `mapstructure:"endpoint"`
@@ -34,6 +50,10 @@ type Config struct {
3450
Username string `mapstructure:"username"`
3551
scraperhelper.ControllerConfig `mapstructure:",squash"`
3652
metadata.MetricsBuilderConfig `mapstructure:",squash"`
53+
metadata.LogsBuilderConfig `mapstructure:",squash"`
54+
55+
TopQueryCollection `mapstructure:"top_query_collection"`
56+
QuerySample `mapstructure:"query_sample_collection"`
3757
}
3858

3959
func (c Config) Validate() error {
@@ -79,5 +99,15 @@ func (c Config) Validate() error {
7999
allErrs = multierr.Append(allErrs, fmt.Errorf("%w: %s", errBadDataSource, err.Error()))
80100
}
81101
}
102+
103+
if c.MaxQuerySampleCount < 1 || c.MaxQuerySampleCount > 10000 {
104+
allErrs = multierr.Append(allErrs, errMaxQuerySampleCount)
105+
}
106+
if c.TopQueryCount < 1 || c.TopQueryCount > 200 || c.TopQueryCount > c.MaxQuerySampleCount {
107+
allErrs = multierr.Append(allErrs, errTopQueryCount)
108+
}
109+
if c.QueryCacheSize <= 0 {
110+
allErrs = multierr.Append(allErrs, errQueryCacheSize)
111+
}
82112
return allErrs
83113
}

receiver/oracledbreceiver/db_client.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
)
1717

1818
type dbClient interface {
19-
metricRows(ctx context.Context) ([]metricRow, error)
19+
metricRows(ctx context.Context, args ...any) ([]metricRow, error)
2020
}
2121

2222
type metricRow map[string]string
@@ -35,8 +35,8 @@ func newDbClient(db *sql.DB, sql string, logger *zap.Logger) dbClient {
3535
}
3636
}
3737

38-
func (cl dbSQLClient) metricRows(ctx context.Context) ([]metricRow, error) {
39-
sqlRows, err := cl.db.QueryContext(ctx, cl.sql)
38+
func (cl dbSQLClient) metricRows(ctx context.Context, args ...any) ([]metricRow, error) {
39+
sqlRows, err := cl.db.QueryContext(ctx, cl.sql, args...)
4040
if err != nil {
4141
return nil, err
4242
}

receiver/oracledbreceiver/documentation.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,4 +373,5 @@ Number of SELECT statements executed in parallel
373373
374374
| Name | Description | Values | Enabled |
375375
| ---- | ----------- | ------ | ------- |
376+
| host.name | The host name of Oracle Server | Any Str | true |
376377
| oracledb.instance.name | The name of the instance that data is coming from. | Any Str | true |

receiver/oracledbreceiver/factory.go

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ import (
1111
"strconv"
1212
"time"
1313

14+
lru "github.com/hashicorp/golang-lru/v2"
1415
go_ora "github.com/sijms/go-ora/v2"
1516
"go.opentelemetry.io/collector/component"
1617
"go.opentelemetry.io/collector/consumer"
1718
"go.opentelemetry.io/collector/receiver"
19+
"go.opentelemetry.io/collector/scraper"
1820
"go.opentelemetry.io/collector/scraper/scraperhelper"
21+
"go.uber.org/zap"
1922

2023
"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/oracledbreceiver/internal/metadata"
2124
)
@@ -27,7 +30,10 @@ func NewFactory() receiver.Factory {
2730
createDefaultConfig,
2831
receiver.WithMetrics(createReceiverFunc(func(dataSourceName string) (*sql.DB, error) {
2932
return sql.Open("oracle", dataSourceName)
30-
}, newDbClient), metadata.MetricsStability))
33+
}, newDbClient), metadata.MetricsStability),
34+
receiver.WithLogs(createLogsReceiverFunc(func(dataSourceName string) (*sql.DB, error) {
35+
return sql.Open("oracle", dataSourceName)
36+
}, newDbClient), metadata.LogsStability))
3137
}
3238

3339
func createDefaultConfig() component.Config {
@@ -37,6 +43,15 @@ func createDefaultConfig() component.Config {
3743
return &Config{
3844
ControllerConfig: cfg,
3945
MetricsBuilderConfig: metadata.DefaultMetricsBuilderConfig(),
46+
LogsBuilderConfig: metadata.DefaultLogsBuilderConfig(),
47+
QuerySample: QuerySample{
48+
MaxRowsPerQuery: 100,
49+
},
50+
TopQueryCollection: TopQueryCollection{
51+
MaxQuerySampleCount: 1000,
52+
TopQueryCount: 200,
53+
QueryCacheSize: 5000,
54+
},
4055
}
4156
}
4257

@@ -56,10 +71,14 @@ func createReceiverFunc(sqlOpenerFunc sqlOpenerFunc, clientProviderFunc clientPr
5671
if err != nil {
5772
return nil, err
5873
}
74+
hostName, hostNameErr := getHostName(getDataSource(*sqlCfg))
75+
if hostNameErr != nil {
76+
return nil, hostNameErr
77+
}
5978

6079
mp, err := newScraper(metricsBuilder, sqlCfg.MetricsBuilderConfig, sqlCfg.ControllerConfig, settings.Logger, func() (*sql.DB, error) {
6180
return sqlOpenerFunc(getDataSource(*sqlCfg))
62-
}, clientProviderFunc, instanceName)
81+
}, clientProviderFunc, instanceName, hostName)
6382
if err != nil {
6483
return nil, err
6584
}
@@ -74,6 +93,56 @@ func createReceiverFunc(sqlOpenerFunc sqlOpenerFunc, clientProviderFunc clientPr
7493
}
7594
}
7695

96+
func createLogsReceiverFunc(sqlOpenerFunc sqlOpenerFunc, clientProviderFunc clientProviderFunc) receiver.CreateLogsFunc {
97+
return func(
98+
_ context.Context,
99+
settings receiver.Settings,
100+
cfg component.Config,
101+
logsConsumer consumer.Logs,
102+
) (receiver.Logs, error) {
103+
sqlCfg := cfg.(*Config)
104+
105+
logsBuilder := metadata.NewLogsBuilder(sqlCfg.LogsBuilderConfig, settings)
106+
107+
instanceName, err := getInstanceName(getDataSource(*sqlCfg))
108+
if err != nil {
109+
return nil, err
110+
}
111+
112+
hostName, hostNameErr := getHostName(getDataSource(*sqlCfg))
113+
if hostNameErr != nil {
114+
return nil, hostNameErr
115+
}
116+
117+
cacheSize := sqlCfg.QueryCacheSize
118+
metricCache, err := lru.New[string, map[string]int64](cacheSize)
119+
if err != nil {
120+
settings.Logger.Error("Failed to create LRU cache, skipping the current scraper", zap.Error(err))
121+
return nil, err
122+
}
123+
124+
mp, err := newLogsScraper(logsBuilder, sqlCfg.LogsBuilderConfig, sqlCfg.ControllerConfig, settings.Logger, func() (*sql.DB, error) {
125+
return sqlOpenerFunc(getDataSource(*sqlCfg))
126+
}, clientProviderFunc, instanceName, metricCache, sqlCfg.TopQueryCollection, sqlCfg.QuerySample, hostName)
127+
if err != nil {
128+
return nil, err
129+
}
130+
131+
f := scraper.NewFactory(metadata.Type, nil,
132+
scraper.WithLogs(func(context.Context, scraper.Settings, component.Config) (scraper.Logs, error) {
133+
return mp, nil
134+
}, component.StabilityLevelAlpha))
135+
opt := scraperhelper.AddFactoryWithConfig(f, nil)
136+
137+
return scraperhelper.NewLogsController(
138+
&sqlCfg.ControllerConfig,
139+
settings,
140+
logsConsumer,
141+
opt,
142+
)
143+
}
144+
}
145+
77146
func getDataSource(cfg Config) string {
78147
if cfg.DataSource != "" {
79148
return cfg.DataSource
@@ -95,3 +164,11 @@ func getInstanceName(datasource string) (string, error) {
95164
instanceName := datasourceURL.Host + datasourceURL.Path
96165
return instanceName, nil
97166
}
167+
168+
func getHostName(datasource string) (string, error) {
169+
datasourceURL, err := url.Parse(datasource)
170+
if err != nil {
171+
return "", err
172+
}
173+
return datasourceURL.Host, nil
174+
}

receiver/oracledbreceiver/factory_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@ func TestNewFactory(t *testing.T) {
3030
consumertest.NewNop(),
3131
)
3232
require.NoError(t, err)
33+
34+
config := factory.CreateDefaultConfig().(*Config)
35+
_, logsErr := factory.CreateLogs(
36+
context.Background(),
37+
receiver.Settings{
38+
ID: component.NewID(metadata.Type),
39+
TelemetrySettings: componenttest.NewNopTelemetrySettings(),
40+
},
41+
config,
42+
43+
consumertest.NewNop(),
44+
)
45+
require.NoError(t, logsErr)
3346
}
3447

3548
func TestGetInstanceName(t *testing.T) {

receiver/oracledbreceiver/fake_db_client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type fakeDbClient struct {
1313
RequestCounter int
1414
}
1515

16-
func (c *fakeDbClient) metricRows(context.Context) ([]metricRow, error) {
16+
func (c *fakeDbClient) metricRows(context.Context, ...any) ([]metricRow, error) {
1717
if c.Err != nil {
1818
return nil, c.Err
1919
}

receiver/oracledbreceiver/generated_component_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

receiver/oracledbreceiver/generated_package_test.go

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)