Skip to content

Commit

Permalink
[exporter/doris] New component: Doris Exporter (#33790)
Browse files Browse the repository at this point in the history
First PR of New component: Doris Exporter.

**Link to tracking Issue:** #33479
  • Loading branch information
joker-star-l authored Aug 27, 2024
1 parent 5612c15 commit ec83b46
Show file tree
Hide file tree
Showing 21 changed files with 797 additions and 0 deletions.
27 changes: 27 additions & 0 deletions .chloggen/dorisexporter-new-component.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use this changelog template to create an entry for release notes.

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

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

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: "Add a new component for exporting logs, traces and metrics to Doris"

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

# (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:

# 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: [user]
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ exporter/clickhouseexporter/ @open-teleme
exporter/coralogixexporter/ @open-telemetry/collector-contrib-approvers @povilasv @matej-g
exporter/datadogexporter/ @open-telemetry/collector-contrib-approvers @mx-psi @dineshg13 @liustanley @songy23 @mackjmr @ankitpatel96
exporter/datasetexporter/ @open-telemetry/collector-contrib-approvers @atoulme @martin-majlis-s1 @zdaratom-s1 @tomaz-s1
exporter/dorisexporter/ @open-telemetry/collector-contrib-approvers @atoulme @joker-star-l
exporter/elasticsearchexporter/ @open-telemetry/collector-contrib-approvers @JaredTan95 @ycombinator @carsonip
exporter/fileexporter/ @open-telemetry/collector-contrib-approvers @atingchen
exporter/googlecloudexporter/ @open-telemetry/collector-contrib-approvers @aabmass @dashpole @jsuereth @punya @damemi @psx95
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ body:
- exporter/coralogix
- exporter/datadog
- exporter/dataset
- exporter/doris
- exporter/elasticsearch
- exporter/file
- exporter/googlecloud
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ body:
- exporter/coralogix
- exporter/datadog
- exporter/dataset
- exporter/doris
- exporter/elasticsearch
- exporter/file
- exporter/googlecloud
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/other.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ body:
- exporter/coralogix
- exporter/datadog
- exporter/dataset
- exporter/doris
- exporter/elasticsearch
- exporter/file
- exporter/googlecloud
Expand Down
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/unmaintained.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ body:
- exporter/coralogix
- exporter/datadog
- exporter/dataset
- exporter/doris
- exporter/elasticsearch
- exporter/file
- exporter/googlecloud
Expand Down
1 change: 1 addition & 0 deletions cmd/githubgen/allowlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ tomasmota
m1rp
jriguera
abhishek-at-cloudwerx
joker-star-l
2 changes: 2 additions & 0 deletions exporter/dorisexporter/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include ../../Makefile.Common

72 changes: 72 additions & 0 deletions exporter/dorisexporter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Apache Doris Exporter
<!-- status autogenerated section -->
| Status | |
| ------------- |-----------|
| Stability | [development]: traces, metrics, logs |
| Distributions | [] |
| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Aexporter%2Fdoris%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Aexporter%2Fdoris) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Aexporter%2Fdoris%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Aexporter%2Fdoris) |
| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@atoulme](https://www.github.com/atoulme), [@joker-star-l](https://www.github.com/joker-star-l) |

[development]: https://github.com/open-telemetry/opentelemetry-collector#development
<!-- end autogenerated section -->

This exporter supports sending traces, metrics, and logs data to [Apache Doris](https://doris.apache.org/) (version >= 2.1).

## Configuration

The following configuration options are supported:

* `endpoint` The http stream load address.
* `database` (default = otel) The database name.
* `username` The authentication username.
* `password` The authentication password.
* `table`
* `logs` (default = otel_logs) The table name for logs.
* `traces` (default = otel_traces) The table name for traces.
* `metrics` (default = otel_metrics) The table name for metrics.
* `create_schema` (default = true) Whether databases and tables are created automatically.
* `mysql_endpoint` The mysql protocol address to create the schema; ignored if `create_schema` is false.
* `history_days` (default = 0) Data older than these days will be deleted; ignored if `create_schema` is false. If set to 0, historical data will not be deleted.
* `create_history_days` (default = 0) The number of days in the history partition that was created when the table was created; ignored if `create_schema` is false. If `history_days` is not 0, `create_history_days` needs to be less than or equal to `history_days`.
* `replication_num` (default = 1) The number of replicas of the table; ignored if `create_schema` is false.
* `timezone` (default is the time zone of the opentelemetry collector) The time zone of doris.
* `timeout` (default = 5s) Time to wait per individual attempt to send data to a backend.
* `sending_queue` [details here](https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/exporterhelper#configuration)
* `enabled` (default = true)
* `num_consumers` (default = 10) Number of consumers that dequeue batches; ignored if `enabled` is false.
* `queue_size` (default = 1000) Maximum number of batches kept in memory before dropping; ignored if `enabled` is false.
* `retry_on_failure` [details here](https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/exporterhelper#configuration)
* `enabled` (default = true)
* `initial_interval` (default = 5s) Time to wait after the first failure before retrying; ignored if `enabled` is false.
* `max_interval` (default = 30s) The upper bound on backoff; ignored if `enabled` is false.
* `max_elapsed_time` (default = 300s) The maximum amount of time spent trying to send a batch; ignored if `enabled` is false. If set to 0, the retries are never stopped.

Example:
```yaml
exporters:
doris:
endpoint: http://localhost:8030
database: otel
username: admin
password: admin
table:
logs: otel_logs
traces: otel_traces
metrics: otel_metrics
create_schema: true
mysql_endpoint: localhost:9030
history_days: 0
create_history_days: 0
replication_num: 1
timezone: Asia/Shanghai
timeout: 5s
sending_queue:
enabled: true
num_consumers: 10
queue_size: 1000
retry_on_failure:
enabled: true
initial_interval: 5s
max_interval: 30s
max_elapsed_time: 300s
```
97 changes: 97 additions & 0 deletions exporter/dorisexporter/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package dorisexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dorisexporter"

import (
"errors"
"regexp"

"go.opentelemetry.io/collector/config/configopaque"
"go.opentelemetry.io/collector/config/configretry"
"go.opentelemetry.io/collector/exporter/exporterhelper"
)

type Config struct {
exporterhelper.TimeoutSettings `mapstructure:",squash"`
configretry.BackOffConfig `mapstructure:"retry_on_failure"`
exporterhelper.QueueSettings `mapstructure:"sending_queue"`

// TableNames is the table name for logs, traces and metrics.
Table `mapstructure:"table"`

// Endpoint is the http stream load address.
Endpoint string `mapstructure:"endpoint"`
// Database is the database name.
Database string `mapstructure:"database"`
// Username is the authentication username.
Username string `mapstructure:"username"`
// Password is the authentication password.
Password configopaque.String `mapstructure:"password"`
// CreateSchema is whether databases and tables are created automatically.
CreateSchema bool `mapstructure:"create_schema"`
// MySQLEndpoint is the mysql protocol address to create the schema; ignored if create_schema is false.
MySQLEndpoint string `mapstructure:"mysql_endpoint"`
// Data older than these days will be deleted; ignored if create_schema is false. If set to 0, historical data will not be deleted.
HistoryDays int32 `mapstructure:"history_days"`
// The number of days in the history partition that was created when the table was created; ignored if create_schema is false.
// If history_days is not 0, create_history_days needs to be less than or equal to history_days.
CreateHistoryDays int32 `mapstructure:"create_history_days"`
// ReplicationNum is the number of replicas of the table; ignored if create_schema is false.
ReplicationNum int32 `mapstructure:"replication_num"`
// Timezone is the timezone of the doris.
TimeZone string `mapstructure:"timezone"`
}

type Table struct {
// Logs is the table name for logs.
Logs string `mapstructure:"logs"`
// Traces is the table name for traces.
Traces string `mapstructure:"traces"`
// Metrics is the table name for metrics.
Metrics string `mapstructure:"metrics"`
}

func (cfg *Config) Validate() (err error) {
if cfg.Endpoint == "" {
err = errors.Join(err, errors.New("endpoint must be specified"))
}
if cfg.CreateSchema {
if cfg.MySQLEndpoint == "" {
err = errors.Join(err, errors.New("mysql_endpoint must be specified"))
}

if cfg.HistoryDays < 0 {
err = errors.Join(err, errors.New("history_days must be greater than or equal to 0"))
}

if cfg.CreateHistoryDays < 0 {
err = errors.Join(err, errors.New("create_history_days must be greater than or equal to 0"))
}

if cfg.HistoryDays > 0 && cfg.CreateHistoryDays > cfg.HistoryDays {
err = errors.Join(err, errors.New("create_history_days must be less than or equal to history_days"))
}

if cfg.ReplicationNum < 1 {
err = errors.Join(err, errors.New("replication_num must be greater than or equal to 1"))
}
}

// Preventing SQL Injection Attacks
re := regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
if !re.MatchString(cfg.Database) {
err = errors.Join(err, errors.New("database name must be alphanumeric and underscore"))
}
if !re.MatchString(cfg.Table.Logs) {
err = errors.Join(err, errors.New("logs table name must be alphanumeric and underscore"))
}
if !re.MatchString(cfg.Table.Traces) {
err = errors.Join(err, errors.New("traces table name must be alphanumeric and underscore"))
}
if !re.MatchString(cfg.Table.Metrics) {
err = errors.Join(err, errors.New("metrics table name must be alphanumeric and underscore"))
}

return err
}
90 changes: 90 additions & 0 deletions exporter/dorisexporter/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package dorisexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dorisexporter"

import (
"path/filepath"
"testing"
"time"

"github.com/cenkalti/backoff/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/configopaque"
"go.opentelemetry.io/collector/config/configretry"
"go.opentelemetry.io/collector/confmap/confmaptest"
"go.opentelemetry.io/collector/exporter/exporterhelper"

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

func TestLoadConfig(t *testing.T) {
t.Parallel()

cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml"))
require.NoError(t, err)

defaultCfg := createDefaultConfig()
defaultCfg.(*Config).Endpoint = "http://localhost:8030"
defaultCfg.(*Config).MySQLEndpoint = "localhost:9030"

tests := []struct {
id component.ID
expected component.Config
}{
{
id: component.NewIDWithName(metadata.Type, ""),
expected: defaultCfg,
},
{
id: component.NewIDWithName(metadata.Type, "full"),
expected: &Config{
TimeoutSettings: exporterhelper.TimeoutSettings{Timeout: 5 * time.Second},
BackOffConfig: configretry.BackOffConfig{
Enabled: true,
InitialInterval: 5 * time.Second,
MaxInterval: 30 * time.Second,
MaxElapsedTime: 300 * time.Second,
RandomizationFactor: backoff.DefaultRandomizationFactor,
Multiplier: backoff.DefaultMultiplier,
},
QueueSettings: exporterhelper.QueueSettings{
Enabled: true,
NumConsumers: 10,
QueueSize: 1000,
},
Table: Table{
Logs: "otel_logs",
Traces: "otel_traces",
Metrics: "otel_metrics",
},
Endpoint: "http://localhost:8030",
Database: "otel",
Username: "admin",
Password: configopaque.String("admin"),
CreateSchema: true,
MySQLEndpoint: "localhost:9030",
HistoryDays: 0,
CreateHistoryDays: 0,
ReplicationNum: 2,
TimeZone: "Asia/Shanghai",
},
},
}

for _, tt := range tests {
t.Run(tt.id.String(), func(t *testing.T) {
factory := NewFactory()
cfg := factory.CreateDefaultConfig()

sub, err := cm.Sub(tt.id.String())
require.NoError(t, err)
require.NoError(t, sub.Unmarshal(cfg))

assert.NoError(t, component.ValidateConfig(cfg))
assert.Equal(t, tt.expected, cfg)
})
}
}
7 changes: 7 additions & 0 deletions exporter/dorisexporter/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

//go:generate mdatagen metadata.yaml

// Package dorisexporter exports trace, metric and log data to an Apache Doris instance.
package dorisexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/dorisexporter"
Loading

0 comments on commit ec83b46

Please sign in to comment.