Skip to content

Commit

Permalink
[exporter/clickhouse]: Add the ability to override default table name…
Browse files Browse the repository at this point in the history
…s for all metric types (open-telemetry#34251)

**Description:** <Describe what has changed.>
- add the ability to override default table names for all metric types

**Link to tracking Issue:** open-telemetry#34225

**Testing:**
- unit tests

**Documentation:** <Describe the documentation added.>

---------

Signed-off-by: odubajDT <[email protected]>
  • Loading branch information
odubajDT authored and jriguera committed Oct 4, 2024
1 parent a2ca2a2 commit 530be87
Show file tree
Hide file tree
Showing 14 changed files with 337 additions and 44 deletions.
28 changes: 28 additions & 0 deletions .chloggen/metrics-tables-clickhouse.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: exporter/clickhouse

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add the ability to override default table names for all metric types.

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [34225]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext: |
'metrics_table_name' of the clickhouse exporter config is deprecated and newly introduced parameter 'metrics_tables' should be used instead.
# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: []
24 changes: 22 additions & 2 deletions exporter/clickhouseexporter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,17 @@ ClickHouse tables:

- `logs_table_name` (default = otel_logs): The table name for logs.
- `traces_table_name` (default = otel_traces): The table name for traces.
- `metrics_table_name` (default = otel_metrics): The table name for metrics.
- `metrics_tables`
- `gauge`
- `name` (default = "otel_metrics_gauge")
- `sum`
- `name` (default = "otel_metrics_sum")
- `summary`
- `name` (default = "otel_metrics_summary")
- `histogram`
- `name` (default = "otel_metrics_histogram")
- `exponential_histogram`
- `name` (default = "otel_metrics_exp_histogram")

Cluster definition:

Expand Down Expand Up @@ -365,8 +375,18 @@ exporters:
create_schema: true
logs_table_name: otel_logs
traces_table_name: otel_traces
metrics_table_name: otel_metrics
timeout: 5s
metrics_tables:
gauge:
name: "otel_metrics_gauge"
sum:
name: "otel_metrics_sum"
summary:
name: "otel_metrics_summary"
histogram:
name: "otel_metrics_histogram"
exponential_histogram:
name: "otel_metrics_exp_histogram"
retry_on_failure:
enabled: true
initial_interval: 5s
Expand Down
61 changes: 61 additions & 0 deletions exporter/clickhouseexporter/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"go.opentelemetry.io/collector/config/configopaque"
"go.opentelemetry.io/collector/config/configretry"
"go.opentelemetry.io/collector/exporter/exporterhelper"

"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/clickhouseexporter/internal"
)

// Config defines configuration for Elastic exporter.
Expand All @@ -37,6 +39,10 @@ type Config struct {
// TracesTableName is the table name for traces. default is `otel_traces`.
TracesTableName string `mapstructure:"traces_table_name"`
// MetricsTableName is the table name for metrics. default is `otel_metrics`.
//
// Deprecated: MetricsTableName exists for historical compatibility
// and should not be used. To set the metrics tables name,
// use the MetricsTables parameter instead.
MetricsTableName string `mapstructure:"metrics_table_name"`
// TTL is The data time-to-live example 30m, 48h. 0 means no ttl.
TTL time.Duration `mapstructure:"ttl"`
Expand All @@ -52,6 +58,21 @@ type Config struct {
// Ignored if async inserts are configured in the `endpoint` or `connection_params`.
// Async inserts may still be overridden server-side.
AsyncInsert bool `mapstructure:"async_insert"`
// MetricsTables defines the table names for metric types.
MetricsTables MetricTablesConfig `mapstructure:"metrics_tables"`
}

type MetricTablesConfig struct {
// Gauge is the table name for gauge metric type. default is `otel_metrics_gauge`.
Gauge internal.MetricTypeConfig `mapstructure:"gauge"`
// Sum is the table name for sum metric type. default is `otel_metrics_sum`.
Sum internal.MetricTypeConfig `mapstructure:"sum"`
// Summary is the table name for summary metric type. default is `otel_metrics_summary`.
Summary internal.MetricTypeConfig `mapstructure:"summary"`
// Histogram is the table name for histogram metric type. default is `otel_metrics_histogram`.
Histogram internal.MetricTypeConfig `mapstructure:"histogram"`
// ExponentialHistogram is the table name for exponential histogram metric type. default is `otel_metrics_exponential_histogram`.
ExponentialHistogram internal.MetricTypeConfig `mapstructure:"exponential_histogram"`
}

// TableEngine defines the ENGINE string value when creating the table.
Expand All @@ -62,6 +83,12 @@ type TableEngine struct {

const defaultDatabase = "default"
const defaultTableEngineName = "MergeTree"
const defaultMetricTableName = "otel_metrics"
const defaultGaugeSuffix = "_gauge"
const defaultSumSuffix = "_sum"
const defaultSummarySuffix = "_summary"
const defaultHistogramSuffix = "_histogram"
const defaultExpHistogramSuffix = "_exponential_histogram"

var (
errConfigNoEndpoint = errors.New("endpoint must be specified")
Expand All @@ -78,6 +105,8 @@ func (cfg *Config) Validate() (err error) {
err = errors.Join(err, e)
}

cfg.buildMetricTableNames()

// Validate DSN with clickhouse driver.
// Last chance to catch invalid config.
if _, e := clickhouse.ParseDSN(dsn); e != nil {
Expand Down Expand Up @@ -153,6 +182,38 @@ func (cfg *Config) shouldCreateSchema() bool {
return cfg.CreateSchema
}

func (cfg *Config) buildMetricTableNames() {
tableName := defaultMetricTableName

if len(cfg.MetricsTableName) != 0 && !cfg.areMetricTableNamesSet() {
tableName = cfg.MetricsTableName
}

if len(cfg.MetricsTables.Gauge.Name) == 0 {
cfg.MetricsTables.Gauge.Name = tableName + defaultGaugeSuffix
}
if len(cfg.MetricsTables.Sum.Name) == 0 {
cfg.MetricsTables.Sum.Name = tableName + defaultSumSuffix
}
if len(cfg.MetricsTables.Summary.Name) == 0 {
cfg.MetricsTables.Summary.Name = tableName + defaultSummarySuffix
}
if len(cfg.MetricsTables.Histogram.Name) == 0 {
cfg.MetricsTables.Histogram.Name = tableName + defaultHistogramSuffix
}
if len(cfg.MetricsTables.ExponentialHistogram.Name) == 0 {
cfg.MetricsTables.ExponentialHistogram.Name = tableName + defaultExpHistogramSuffix
}
}

func (cfg *Config) areMetricTableNamesSet() bool {
return len(cfg.MetricsTables.Gauge.Name) != 0 ||
len(cfg.MetricsTables.Sum.Name) != 0 ||
len(cfg.MetricsTables.Summary.Name) != 0 ||
len(cfg.MetricsTables.Histogram.Name) != 0 ||
len(cfg.MetricsTables.ExponentialHistogram.Name) != 0
}

// tableEngineString generates the ENGINE string.
func (cfg *Config) tableEngineString() string {
engine := cfg.TableEngine.Name
Expand Down
142 changes: 133 additions & 9 deletions exporter/clickhouseexporter/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"go.opentelemetry.io/collector/confmap/confmaptest"
"go.opentelemetry.io/collector/exporter/exporterhelper"

"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/clickhouseexporter/internal"
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/clickhouseexporter/internal/metadata"
)

Expand Down Expand Up @@ -47,15 +48,14 @@ func TestLoadConfig(t *testing.T) {
{
id: component.NewIDWithName(metadata.Type, "full"),
expected: &Config{
Endpoint: defaultEndpoint,
Database: "otel",
Username: "foo",
Password: "bar",
TTL: 72 * time.Hour,
LogsTableName: "otel_logs",
TracesTableName: "otel_traces",
MetricsTableName: "otel_metrics",
CreateSchema: true,
Endpoint: defaultEndpoint,
Database: "otel",
Username: "foo",
Password: "bar",
TTL: 72 * time.Hour,
LogsTableName: "otel_logs",
TracesTableName: "otel_traces",
CreateSchema: true,
TimeoutSettings: exporterhelper.TimeoutConfig{
Timeout: 5 * time.Second,
},
Expand All @@ -67,6 +67,13 @@ func TestLoadConfig(t *testing.T) {
RandomizationFactor: backoff.DefaultRandomizationFactor,
Multiplier: backoff.DefaultMultiplier,
},
MetricsTables: MetricTablesConfig{
Gauge: internal.MetricTypeConfig{Name: "otel_metrics_custom_gauge"},
Sum: internal.MetricTypeConfig{Name: "otel_metrics_custom_sum"},
Summary: internal.MetricTypeConfig{Name: "otel_metrics_custom_summary"},
Histogram: internal.MetricTypeConfig{Name: "otel_metrics_custom_histogram"},
ExponentialHistogram: internal.MetricTypeConfig{Name: "otel_metrics_custom_exp_histogram"},
},
ConnectionParams: map[string]string{},
QueueSettings: exporterhelper.QueueConfig{
Enabled: true,
Expand Down Expand Up @@ -102,6 +109,123 @@ func withDefaultConfig(fns ...func(*Config)) *Config {
return cfg
}

func TestBuildMetricMetricTableNames(t *testing.T) {
tests := []struct {
name string
cfg Config
want Config
}{
{
name: "nothing set",
cfg: Config{},
want: Config{
MetricsTables: MetricTablesConfig{
Gauge: internal.MetricTypeConfig{Name: "otel_metrics_gauge"},
Sum: internal.MetricTypeConfig{Name: "otel_metrics_sum"},
Summary: internal.MetricTypeConfig{Name: "otel_metrics_summary"},
Histogram: internal.MetricTypeConfig{Name: "otel_metrics_histogram"},
ExponentialHistogram: internal.MetricTypeConfig{Name: "otel_metrics_exponential_histogram"},
},
},
},
{
name: "only metric_table_name set",
cfg: Config{
MetricsTableName: "table_name",
},
want: Config{
MetricsTableName: "table_name",
MetricsTables: MetricTablesConfig{
Gauge: internal.MetricTypeConfig{Name: "table_name_gauge"},
Sum: internal.MetricTypeConfig{Name: "table_name_sum"},
Summary: internal.MetricTypeConfig{Name: "table_name_summary"},
Histogram: internal.MetricTypeConfig{Name: "table_name_histogram"},
ExponentialHistogram: internal.MetricTypeConfig{Name: "table_name_exponential_histogram"},
},
},
},
{
name: "only metric_tables set fully",
cfg: Config{
MetricsTables: MetricTablesConfig{
Gauge: internal.MetricTypeConfig{Name: "table_name_gauge"},
Sum: internal.MetricTypeConfig{Name: "table_name_sum"},
Summary: internal.MetricTypeConfig{Name: "table_name_summary"},
Histogram: internal.MetricTypeConfig{Name: "table_name_histogram"},
ExponentialHistogram: internal.MetricTypeConfig{Name: "table_name_exponential_histogram"},
},
},
want: Config{
MetricsTables: MetricTablesConfig{
Gauge: internal.MetricTypeConfig{Name: "table_name_gauge"},
Sum: internal.MetricTypeConfig{Name: "table_name_sum"},
Summary: internal.MetricTypeConfig{Name: "table_name_summary"},
Histogram: internal.MetricTypeConfig{Name: "table_name_histogram"},
ExponentialHistogram: internal.MetricTypeConfig{Name: "table_name_exponential_histogram"},
},
},
},
{
name: "only metric_tables set partially",
cfg: Config{
MetricsTables: MetricTablesConfig{
Summary: internal.MetricTypeConfig{Name: "table_name_summary"},
Histogram: internal.MetricTypeConfig{Name: "table_name_histogram"},
ExponentialHistogram: internal.MetricTypeConfig{Name: "table_name_exp_histogram"},
},
},
want: Config{
MetricsTables: MetricTablesConfig{
Gauge: internal.MetricTypeConfig{Name: "otel_metrics_gauge"},
Sum: internal.MetricTypeConfig{Name: "otel_metrics_sum"},
Summary: internal.MetricTypeConfig{Name: "table_name_summary"},
Histogram: internal.MetricTypeConfig{Name: "table_name_histogram"},
ExponentialHistogram: internal.MetricTypeConfig{Name: "table_name_exp_histogram"},
},
},
},
{
name: "only metric_tables set partially with metric_table_name",
cfg: Config{
MetricsTableName: "custom_name",
MetricsTables: MetricTablesConfig{
Summary: internal.MetricTypeConfig{Name: "table_name_summary"},
Histogram: internal.MetricTypeConfig{Name: "table_name_histogram"},
ExponentialHistogram: internal.MetricTypeConfig{Name: "table_name_exp_histogram"},
},
},
want: Config{
MetricsTableName: "custom_name",
MetricsTables: MetricTablesConfig{
Gauge: internal.MetricTypeConfig{Name: "otel_metrics_gauge"},
Sum: internal.MetricTypeConfig{Name: "otel_metrics_sum"},
Summary: internal.MetricTypeConfig{Name: "table_name_summary"},
Histogram: internal.MetricTypeConfig{Name: "table_name_histogram"},
ExponentialHistogram: internal.MetricTypeConfig{Name: "table_name_exp_histogram"},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.cfg.buildMetricTableNames()
require.Equal(t, tt.want, tt.cfg)
})
}
}

func TestAreMetricTableNamesSet(t *testing.T) {
cfg := Config{}
require.False(t, cfg.areMetricTableNamesSet())

cfg = Config{
MetricsTables: MetricTablesConfig{
Gauge: internal.MetricTypeConfig{Name: "gauge"},
},
}
require.True(t, cfg.areMetricTableNamesSet())
}

func TestConfig_buildDSN(t *testing.T) {
type fields struct {
Endpoint string
Expand Down
28 changes: 21 additions & 7 deletions exporter/clickhouseexporter/exporter_metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ import (
type metricsExporter struct {
client *sql.DB

logger *zap.Logger
cfg *Config
logger *zap.Logger
cfg *Config
tablesConfig internal.MetricTablesConfigMapper
}

func newMetricsExporter(logger *zap.Logger, cfg *Config) (*metricsExporter, error) {
Expand All @@ -29,10 +30,13 @@ func newMetricsExporter(logger *zap.Logger, cfg *Config) (*metricsExporter, erro
return nil, err
}

tablesConfig := generateMetricTablesConfigMapper(cfg)

return &metricsExporter{
client: client,
logger: logger,
cfg: cfg,
client: client,
logger: logger,
cfg: cfg,
tablesConfig: tablesConfig,
}, nil
}

Expand All @@ -48,7 +52,17 @@ func (e *metricsExporter) start(ctx context.Context, _ component.Host) error {
}

ttlExpr := generateTTLExpr(e.cfg.TTL, "toDateTime(TimeUnix)")
return internal.NewMetricsTable(ctx, e.cfg.MetricsTableName, e.cfg.clusterString(), e.cfg.tableEngineString(), ttlExpr, e.client)
return internal.NewMetricsTable(ctx, e.tablesConfig, e.cfg.clusterString(), e.cfg.tableEngineString(), ttlExpr, e.client)
}

func generateMetricTablesConfigMapper(cfg *Config) internal.MetricTablesConfigMapper {
return internal.MetricTablesConfigMapper{
pmetric.MetricTypeGauge: cfg.MetricsTables.Gauge,
pmetric.MetricTypeSum: cfg.MetricsTables.Sum,
pmetric.MetricTypeSummary: cfg.MetricsTables.Summary,
pmetric.MetricTypeHistogram: cfg.MetricsTables.Histogram,
pmetric.MetricTypeExponentialHistogram: cfg.MetricsTables.ExponentialHistogram,
}
}

// shutdown will shut down the exporter.
Expand All @@ -60,7 +74,7 @@ func (e *metricsExporter) shutdown(_ context.Context) error {
}

func (e *metricsExporter) pushMetricsData(ctx context.Context, md pmetric.Metrics) error {
metricsMap := internal.NewMetricsModel(e.cfg.MetricsTableName)
metricsMap := internal.NewMetricsModel(e.tablesConfig)
for i := 0; i < md.ResourceMetrics().Len(); i++ {
metrics := md.ResourceMetrics().At(i)
resAttr := attributesToMap(metrics.Resource().Attributes())
Expand Down
Loading

0 comments on commit 530be87

Please sign in to comment.