-
Notifications
You must be signed in to change notification settings - Fork 0
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
SC-20508 Add sematext exporter #2
base: main
Are you sure you want to change the base?
Changes from 30 commits
7be09e3
56075f9
6cad40b
8ed1a5a
af5ba97
6d2eff4
929070f
f958e8f
abf2930
bf6e81b
9d02ad7
2146176
6af2ce6
602ecbf
0290804
b4788ae
649215f
2e17dd5
e358f65
0cac0f9
89713a0
be7f986
d8e5f14
c7fb729
daecc41
683b038
a8346ed
02fec37
a85429a
9317219
1f32a87
8d81a7d
245725c
eeb42af
5d17fd7
b4505b0
98d51f4
1c87f19
fcd0f90
40581e7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
include ../../Makefile.Common |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# Sematext Exporter | ||
<!-- status autogenerated section --> | ||
<!-- end autogenerated section --> | ||
|
||
This exporter supports sending metrics to [Sematext Cloud](https://sematext.com/) in Influx line protocol format | ||
|
||
## Configuration | ||
|
||
The following configuration options are supported: | ||
|
||
* `endpoint` (required) HTTP/S destination for metric receivers. It is dependent on the region: | ||
- US: `spm-receiver.sematext.com` | ||
- EU: `spm-receiver.eu.sematext.com` | ||
* `timeout` (default = 5s) Timeout for requests | ||
* `AppToken` App token is the token of Sematext Monitoring App to which you want to send the metrics. | ||
* `Region` Region specifies the Sematext region the user is operating in; must be one of: | ||
Eromosele-SM marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* `US` | ||
* `EU` | ||
* `payload_max_lines` (default = 10_000) Maximum number of lines allowed per HTTP POST request | ||
* `payload_max_bytes` (default = 10_000_000) Maximum number of bytes allowed per HTTP POST request | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Eromosele-SM I'd check these defaults internally with Sematext first. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's the suggest for custom metrics. I'd ask STA devs what STA uses. |
||
* `metrics_schema` (default = telegraf-prometheus-v2) The chosen metrics schema to write; must be one of: | ||
* `otel-v1` | ||
* `telegraf-prometheus-v2` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Eromosele-SM probably only one schema in the end. Also, this Exporter will end up shipping telemetry other than metrics. So maybe you want to structure the README in preparation for that. For example, this particular schema will be only for metrics. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can always edit the readme when we have logs support ready? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ofc. I hope the YAML is also structured in anticipation of Exporter sending other telemetry later (so other App tokens, other endpoints, although the region could probably remain general). |
||
* `sending_queue` [details here](https://github.com/open-telemetry/opentelemetry-collector/blob/v0.25.0/exporter/exporterhelper/README.md#configuration) | ||
* `enabled` (default = true) | ||
* `num_consumers` (default = 10) The number of consumers from the queue | ||
* `queue_size` (default = 1000) Maximum number of batches allowed in queue at a given time | ||
* `retry_on_failure` [details here](https://github.com/open-telemetry/opentelemetry-collector/blob/v0.25.0/exporter/exporterhelper/README.md#configuration) | ||
* `enabled` (default = true) | ||
* `initial_interval` (default = 5s) Time to wait after the first failure before retrying | ||
* `max_interval` (default = 30s) Upper bound on backoff interval | ||
* `max_elapsed_time` (default = 120s) Maximum amount of time (including retries) spent trying to send a request/batch | ||
|
||
The full list of settings exposed for this exporter are documented in [config.go](config.go). | ||
|
||
Example: | ||
```yaml | ||
endpoint: spm-receiver.sematext.com | ||
timeout: 500ms | ||
sending_queue: | ||
enabled: true | ||
num_consumers: 3 | ||
queue_size: 10 | ||
retry_on_failure: | ||
enabled: true | ||
initial_interval: 1s | ||
max_interval: 3s | ||
max_elapsed_time: 10s | ||
region: US | ||
app_token: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | ||
metrics_schema: telegraf-prometheus-v2 | ||
payload_max_lines: 100 | ||
payload_max_bytes: 1000 | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package sematextexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/sematextexporter" | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"go.opentelemetry.io/collector/config/confighttp" | ||
"go.opentelemetry.io/collector/config/configretry" | ||
"go.opentelemetry.io/collector/exporter/exporterhelper" | ||
) | ||
|
||
type Config struct { | ||
confighttp.ClientConfig `mapstructure:",squash"` | ||
QueueSettings exporterhelper.QueueConfig `mapstructure:"sending_queue"` | ||
configretry.BackOffConfig `mapstructure:"retry_on_failure"` | ||
|
||
// App token is the token of Sematext Monitoring App to which you want to send the metrics. | ||
AppToken string `mapstructure:"app_token"` | ||
|
||
// Region specifies the Sematext region the user is operating in | ||
// Options: | ||
// - EU | ||
// - US | ||
Region string `mapstructure:"region"` | ||
Eromosele-SM marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// MetricsSchema indicates the metrics schema to emit to line protocol. | ||
// Options: | ||
// - telegraf-prometheus-v2 | ||
MetricsSchema string `mapstructure:"metrics_schema"` | ||
|
||
// PayloadMaxLines is the maximum number of line protocol lines to POST in a single request. | ||
PayloadMaxLines int `mapstructure:"payload_max_lines"` | ||
// PayloadMaxBytes is the maximum number of line protocol bytes to POST in a single request. | ||
PayloadMaxBytes int `mapstructure:"payload_max_bytes"` | ||
} | ||
|
||
// Validate checks for invalid or missing entries in the configuration. | ||
func (cfg *Config) Validate() error { | ||
if cfg.MetricsSchema != "telegraf-prometheus-v2" { | ||
return fmt.Errorf("invalid metrics schema: %s", cfg.MetricsSchema) | ||
} | ||
if strings.ToLower(cfg.Region) != "eu" && strings.ToLower(cfg.Region) != "us" { | ||
return fmt.Errorf("invalid region: %s. please use either 'EU' or 'US'", cfg.Region) | ||
} | ||
if len(cfg.AppToken) != 36 { | ||
return fmt.Errorf("invalid app_token: %s. app_token should be 36 characters", cfg.AppToken) | ||
} | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package sematextexporter | ||
|
||
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/confighttp" | ||
"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/sematextexporter/internal/metadata" | ||
) | ||
|
||
func TestLoadConfig(t *testing.T) { | ||
t.Parallel() | ||
|
||
cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) | ||
require.NoError(t, err) | ||
|
||
tests := []struct { | ||
id component.ID | ||
expected component.Config | ||
}{ | ||
{ | ||
id: component.NewIDWithName(metadata.Type, "default-config"), | ||
expected: createDefaultConfig(), | ||
}, | ||
{ | ||
id: component.NewIDWithName(metadata.Type, "override-config"), | ||
expected: &Config{ | ||
ClientConfig: confighttp.ClientConfig{ | ||
Endpoint: "http://localhost:8080", | ||
Timeout: 500 * time.Millisecond, | ||
Headers: map[string]configopaque.String{"User-Agent": "OpenTelemetry -> Sematext"}, | ||
}, | ||
QueueSettings: exporterhelper.QueueConfig{ | ||
Enabled: true, | ||
NumConsumers: 3, | ||
QueueSize: 10, | ||
}, | ||
BackOffConfig: configretry.BackOffConfig{ | ||
Enabled: true, | ||
InitialInterval: 1 * time.Second, | ||
MaxInterval: 3 * time.Second, | ||
MaxElapsedTime: 10 * time.Second, | ||
RandomizationFactor: backoff.DefaultRandomizationFactor, | ||
Multiplier: backoff.DefaultMultiplier, | ||
}, | ||
Region: "US", | ||
AppToken: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", | ||
MetricsSchema: "telegraf-prometheus-v2", | ||
PayloadMaxLines: 72, | ||
PayloadMaxBytes: 27, | ||
}, | ||
}, | ||
} | ||
|
||
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) | ||
}) | ||
} | ||
} |
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 sematextexporter sends metrics to sematext cloud. | ||
package sematextexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/sematextexporter" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
//go:generate mdatagen metadata.yaml | ||
|
||
package sematextexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/sematextexporter" | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"time" | ||
|
||
"github.com/influxdata/influxdb-observability/otel2influx" | ||
"go.opentelemetry.io/collector/component" | ||
"go.opentelemetry.io/collector/config/confighttp" | ||
"go.opentelemetry.io/collector/config/configopaque" | ||
"go.opentelemetry.io/collector/config/configretry" | ||
"go.opentelemetry.io/collector/exporter" | ||
"go.opentelemetry.io/collector/exporter/exporterhelper" | ||
|
||
"github.com/influxdata/influxdb-observability/common" | ||
|
||
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/sematextexporter/internal/metadata" | ||
) | ||
const appToken string = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" | ||
// NewFactory creates a factory for the Sematext metrics exporter. | ||
func NewFactory() exporter.Factory { | ||
return exporter.NewFactory( | ||
metadata.Type, | ||
createDefaultConfig, | ||
exporter.WithMetrics(createMetricsExporter, metadata.MetricsStability), | ||
) | ||
} | ||
|
||
func createDefaultConfig() component.Config { | ||
return &Config{ | ||
ClientConfig: confighttp.ClientConfig{ | ||
Timeout: 5 * time.Second, | ||
Headers: map[string]configopaque.String{ | ||
"User-Agent": "OpenTelemetry -> Sematext", | ||
}, | ||
}, | ||
QueueSettings: exporterhelper.NewDefaultQueueConfig(), | ||
BackOffConfig: configretry.NewDefaultBackOffConfig(), | ||
MetricsSchema: common.MetricsSchemaTelegrafPrometheusV2.String(), | ||
AppToken: appToken, | ||
Region: "EU", | ||
PayloadMaxLines: 10_000, | ||
Eromosele-SM marked this conversation as resolved.
Show resolved
Hide resolved
|
||
PayloadMaxBytes: 10_000_000, | ||
} | ||
} | ||
|
||
func createMetricsExporter( | ||
ctx context.Context, | ||
set exporter.Settings, | ||
config component.Config, | ||
) (exporter.Metrics, error) { | ||
cfg := config.(*Config) | ||
|
||
// Initialize the logger for Sematext | ||
logger := newZapSematextLogger(set.Logger) | ||
|
||
// Create a writer for sending metrics to Sematext | ||
writer, err := newSematextHTTPWriter(logger, cfg, set.TelemetrySettings) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to create Sematext HTTP writer: %w", err) | ||
} | ||
schema, found := common.MetricsSchemata[cfg.MetricsSchema] | ||
if !found { | ||
return nil, fmt.Errorf("schema '%s' not recognized", cfg.MetricsSchema) | ||
} | ||
|
||
expConfig := otel2influx.DefaultOtelMetricsToLineProtocolConfig() | ||
expConfig.Logger = logger | ||
expConfig.Writer = writer | ||
expConfig.Schema = schema | ||
exp, err := otel2influx.NewOtelMetricsToLineProtocol(expConfig) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return exporterhelper.NewMetricsExporter( | ||
ctx, | ||
set, | ||
cfg, | ||
exp.WriteMetrics, | ||
exporterhelper.WithQueue(cfg.QueueSettings), | ||
exporterhelper.WithRetry(cfg.BackOffConfig), | ||
exporterhelper.WithStart(writer.Start), | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Eromosele-SM make it match STA timeout?