-
Notifications
You must be signed in to change notification settings - Fork 5.8k
feat(outputs.opentelemetry): Add a config option for specifying name/field separator #19156
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
base: master
Are you sure you want to change the base?
Changes from 1 commit
d5aff96
af4cbc5
dfcab49
894adfa
9fd0978
2eb45e5
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 |
|---|---|---|
|
|
@@ -34,12 +34,13 @@ type OpenTelemetry struct { | |
| EncodingType string `toml:"encoding_type"` | ||
|
|
||
| tls.ClientConfig | ||
| Timeout config.Duration `toml:"timeout"` | ||
| Compression string `toml:"compression"` | ||
| Token config.Secret `toml:"token"` | ||
| Headers map[string]string `toml:"headers"` | ||
| Attributes map[string]string `toml:"attributes"` | ||
| Coralogix *CoralogixConfig `toml:"coralogix"` | ||
| Timeout config.Duration `toml:"timeout"` | ||
| Compression string `toml:"compression"` | ||
| OtelNameSeparator string `toml:"otel_name_separator"` | ||
|
Contributor
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.
Author
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. Renamed it here dfcab49. |
||
| Token config.Secret `toml:"token"` | ||
| Headers map[string]string `toml:"headers"` | ||
| Attributes map[string]string `toml:"attributes"` | ||
| Coralogix *CoralogixConfig `toml:"coralogix"` | ||
| proxy.HTTPProxy | ||
| proxy.TCPProxy | ||
|
|
||
|
|
@@ -105,7 +106,7 @@ func (o *OpenTelemetry) Connect() error { | |
| o.Headers["Authorization"] = "Bearer " + o.Coralogix.PrivateKey | ||
| } | ||
|
|
||
| metricsConverter, err := influx2otel.NewLineProtocolToOtelMetrics(logger) | ||
| metricsConverter, err := influx2otel.NewLineProtocolToOtelMetricsWithSeparator(logger, o.OtelNameSeparator) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
@@ -232,17 +233,19 @@ func (o *OpenTelemetry) sendBatch(metrics []telegraf.Metric) error { | |
| } | ||
|
|
||
| const ( | ||
| defaultServiceAddress = "localhost:4317" | ||
| defaultTimeout = config.Duration(5 * time.Second) | ||
| defaultCompression = "gzip" | ||
| defaultServiceAddress = "localhost:4317" | ||
| defaultTimeout = config.Duration(5 * time.Second) | ||
| defaultCompression = "gzip" | ||
| defaultOtelNameSeparator = "_" | ||
| ) | ||
|
|
||
| func init() { | ||
| outputs.Add("opentelemetry", func() telegraf.Output { | ||
| return &OpenTelemetry{ | ||
| ServiceAddress: defaultServiceAddress, | ||
| Timeout: defaultTimeout, | ||
| Compression: defaultCompression, | ||
| ServiceAddress: defaultServiceAddress, | ||
| Timeout: defaultTimeout, | ||
| Compression: defaultCompression, | ||
| OtelNameSeparator: defaultOtelNameSeparator, | ||
| } | ||
| }) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -284,6 +284,261 @@ func TestOpenTelemetryHTTPJSON(t *testing.T) { | |
| require.JSONEq(t, string(expectJSON), string(gotJSON)) | ||
| } | ||
|
|
||
| func TestOpenTelemetrySeparator(t *testing.T) { | ||
|
Contributor
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. the new tests only cover
Author
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. |
||
| expect := pmetric.NewMetrics() | ||
| { | ||
| rm := expect.ResourceMetrics().AppendEmpty() | ||
| rm.Resource().Attributes().PutStr("host.name", "potato") | ||
| rm.Resource().Attributes().PutStr("attr-key", "attr-val") | ||
| ilm := rm.ScopeMetrics().AppendEmpty() | ||
| ilm.Scope().SetName("My Library Name") | ||
| m := ilm.Metrics().AppendEmpty() | ||
| m.SetName("mem.used_percent") | ||
| m.SetEmptyGauge() | ||
| dp := m.Gauge().DataPoints().AppendEmpty() | ||
| dp.Attributes().PutStr("foo", "bar") | ||
| dp.SetTimestamp(pcommon.Timestamp(1622848686000000000)) | ||
| dp.SetDoubleValue(87.332) | ||
| } | ||
| m := newMockOtelService(t) | ||
| t.Cleanup(m.Cleanup) | ||
|
|
||
| metricsConverter, err := influx2otel.NewLineProtocolToOtelMetricsWithSeparator(common.NoopLogger{}, ".") | ||
| require.NoError(t, err) | ||
| plugin := &OpenTelemetry{ | ||
| ServiceAddress: m.Address(), | ||
| Timeout: config.Duration(time.Second), | ||
| Headers: map[string]string{"test": "header1"}, | ||
| Attributes: map[string]string{"attr-key": "attr-val"}, | ||
| metricsConverter: metricsConverter, | ||
| otlpMetricClient: &gRPCClient{ | ||
| grpcClientConn: m.GrpcClient(), | ||
| metricsServiceClient: pmetricotlp.NewGRPCClient(m.GrpcClient()), | ||
| }, | ||
| Log: testutil.Logger{}, | ||
| } | ||
|
|
||
| input := metric.New( | ||
| "mem", | ||
| map[string]string{ | ||
| "foo": "bar", | ||
| "otel.library.name": "My Library Name", | ||
| "host.name": "potato", | ||
| }, | ||
| map[string]interface{}{ | ||
| "used_percent": 87.332, | ||
| }, | ||
| time.Unix(0, 1622848686000000000), | ||
| telegraf.Gauge, | ||
| ) | ||
|
|
||
| require.NoError(t, plugin.Write([]telegraf.Metric{input})) | ||
|
|
||
| got := m.GotMetrics() | ||
|
|
||
| marshaller := pmetric.JSONMarshaler{} | ||
| expectJSON, err := marshaller.MarshalMetrics(expect) | ||
| require.NoError(t, err) | ||
|
|
||
| gotJSON, err := marshaller.MarshalMetrics(got) | ||
| require.NoError(t, err) | ||
|
|
||
| require.JSONEq(t, string(expectJSON), string(gotJSON)) | ||
| } | ||
|
|
||
| func TestOpenTelemetrySeparatorHTTPProtobuf(t *testing.T) { | ||
| expect := pmetric.NewMetrics() | ||
| { | ||
| rm := expect.ResourceMetrics().AppendEmpty() | ||
| rm.Resource().Attributes().PutStr("host.name", "potato") | ||
| rm.Resource().Attributes().PutStr("attr-key", "attr-val") | ||
| ilm := rm.ScopeMetrics().AppendEmpty() | ||
| ilm.Scope().SetName("My Library Name") | ||
| m := ilm.Metrics().AppendEmpty() | ||
| m.SetName("mem.used_percent") | ||
| m.SetEmptyGauge() | ||
| dp := m.Gauge().DataPoints().AppendEmpty() | ||
| dp.Attributes().PutStr("foo", "bar") | ||
| dp.SetTimestamp(pcommon.Timestamp(1622848686000000000)) | ||
| dp.SetDoubleValue(87.332) | ||
| } | ||
|
|
||
| var receivedMetrics pmetric.Metrics | ||
| var receivedContentType string | ||
| server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| receivedContentType = r.Header.Get("Content-Type") | ||
| body, err := io.ReadAll(r.Body) | ||
| if err != nil { | ||
| t.Error(err) | ||
| w.WriteHeader(http.StatusInternalServerError) | ||
| return | ||
| } | ||
|
|
||
| req := pmetricotlp.NewExportRequest() | ||
| err = req.UnmarshalProto(body) | ||
| if err != nil { | ||
| t.Error(err) | ||
| w.WriteHeader(http.StatusInternalServerError) | ||
| return | ||
| } | ||
|
|
||
| receivedMetrics = pmetric.NewMetrics() | ||
| req.Metrics().CopyTo(receivedMetrics) | ||
|
|
||
| resp := pmetricotlp.NewExportResponse() | ||
| respBytes, err := resp.MarshalProto() | ||
| if err != nil { | ||
| t.Error(err) | ||
| w.WriteHeader(http.StatusInternalServerError) | ||
| return | ||
| } | ||
|
|
||
| w.Header().Set("Content-Type", "application/x-protobuf") | ||
| w.WriteHeader(http.StatusOK) | ||
| _, err = w.Write(respBytes) | ||
| if err != nil { | ||
| t.Error(err) | ||
| w.WriteHeader(http.StatusInternalServerError) | ||
| return | ||
| } | ||
| })) | ||
| defer server.Close() | ||
|
|
||
| plugin := &OpenTelemetry{ | ||
| ServiceAddress: server.URL, | ||
| EncodingType: "protobuf", | ||
| Timeout: config.Duration(time.Second), | ||
| Attributes: map[string]string{"attr-key": "attr-val"}, | ||
| Compression: "none", | ||
| OtelNameSeparator: ".", | ||
| Log: testutil.Logger{}, | ||
| } | ||
| require.NoError(t, plugin.Connect()) | ||
|
|
||
| input := metric.New( | ||
| "mem", | ||
| map[string]string{ | ||
| "foo": "bar", | ||
| "otel.library.name": "My Library Name", | ||
| "host.name": "potato", | ||
| }, | ||
| map[string]interface{}{ | ||
| "used_percent": 87.332, | ||
| }, | ||
| time.Unix(0, 1622848686000000000), | ||
| telegraf.Gauge, | ||
| ) | ||
|
|
||
| require.NoError(t, plugin.Write([]telegraf.Metric{input})) | ||
|
|
||
| require.Equal(t, "application/x-protobuf", receivedContentType) | ||
|
|
||
| marshaller := pmetric.JSONMarshaler{} | ||
| expectJSON, err := marshaller.MarshalMetrics(expect) | ||
| require.NoError(t, err) | ||
|
|
||
| gotJSON, err := marshaller.MarshalMetrics(receivedMetrics) | ||
| require.NoError(t, err) | ||
|
|
||
| require.JSONEq(t, string(expectJSON), string(gotJSON)) | ||
| } | ||
|
|
||
| func TestOpenTelemetrySeparatorHTTPJSON(t *testing.T) { | ||
|
Contributor
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. the
Author
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. Added in 9fd0978 |
||
| expect := pmetric.NewMetrics() | ||
| { | ||
| rm := expect.ResourceMetrics().AppendEmpty() | ||
| rm.Resource().Attributes().PutStr("host.name", "potato") | ||
| rm.Resource().Attributes().PutStr("attr-key", "attr-val") | ||
| ilm := rm.ScopeMetrics().AppendEmpty() | ||
| ilm.Scope().SetName("My Library Name") | ||
| m := ilm.Metrics().AppendEmpty() | ||
| m.SetName("mem.used_percent") | ||
| m.SetEmptyGauge() | ||
| dp := m.Gauge().DataPoints().AppendEmpty() | ||
| dp.Attributes().PutStr("foo", "bar") | ||
| dp.SetTimestamp(pcommon.Timestamp(1622848686000000000)) | ||
| dp.SetDoubleValue(87.332) | ||
| } | ||
|
|
||
| var receivedMetrics pmetric.Metrics | ||
| var receivedContentType string | ||
| server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| receivedContentType = r.Header.Get("Content-Type") | ||
| body, err := io.ReadAll(r.Body) | ||
| if err != nil { | ||
| t.Error(err) | ||
| return | ||
| } | ||
|
|
||
| req := pmetricotlp.NewExportRequest() | ||
| err = req.UnmarshalJSON(body) | ||
| if err != nil { | ||
| t.Error(err) | ||
| w.WriteHeader(http.StatusInternalServerError) | ||
| return | ||
| } | ||
|
|
||
| receivedMetrics = pmetric.NewMetrics() | ||
| req.Metrics().CopyTo(receivedMetrics) | ||
|
|
||
| resp := pmetricotlp.NewExportResponse() | ||
| respBytes, err := resp.MarshalJSON() | ||
| if err != nil { | ||
| t.Error(err) | ||
| w.WriteHeader(http.StatusInternalServerError) | ||
| return | ||
| } | ||
|
|
||
| w.Header().Set("Content-Type", "application/json") | ||
| w.WriteHeader(http.StatusOK) | ||
| _, err = w.Write(respBytes) | ||
| if err != nil { | ||
| t.Error(err) | ||
| w.WriteHeader(http.StatusInternalServerError) | ||
| return | ||
| } | ||
| })) | ||
| defer server.Close() | ||
|
|
||
| plugin := &OpenTelemetry{ | ||
| ServiceAddress: server.URL, | ||
| EncodingType: "json", | ||
| Timeout: config.Duration(time.Second), | ||
| Attributes: map[string]string{"attr-key": "attr-val"}, | ||
| Compression: "none", | ||
| OtelNameSeparator: ".", | ||
| Log: testutil.Logger{}, | ||
| } | ||
| require.NoError(t, plugin.Connect()) | ||
|
|
||
| input := metric.New( | ||
| "mem", | ||
| map[string]string{ | ||
| "foo": "bar", | ||
| "otel.library.name": "My Library Name", | ||
| "host.name": "potato", | ||
| }, | ||
| map[string]interface{}{ | ||
| "used_percent": 87.332, | ||
| }, | ||
| time.Unix(0, 1622848686000000000), | ||
| telegraf.Gauge, | ||
| ) | ||
|
|
||
| require.NoError(t, plugin.Write([]telegraf.Metric{input})) | ||
|
|
||
| require.Equal(t, "application/json", receivedContentType) | ||
|
|
||
| marshaller := pmetric.JSONMarshaler{} | ||
| expectJSON, err := marshaller.MarshalMetrics(expect) | ||
| require.NoError(t, err) | ||
|
|
||
| gotJSON, err := marshaller.MarshalMetrics(receivedMetrics) | ||
| require.NoError(t, err) | ||
|
|
||
| require.JSONEq(t, string(expectJSON), string(gotJSON)) | ||
| } | ||
|
|
||
| var _ pmetricotlp.GRPCServer = (*mockOtelService)(nil) | ||
|
|
||
| type mockOtelService struct { | ||
|
|
||
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.
This can't land on master. The real behavior lives in influxdb-observability#341, so that one needs to be reviewed, merged, and released first; then bump
influx2otelhere and drop thereplace. Noteinflux2otel,common, andotel2influxare versioned together (currently v0.5.12), so the bump will likely move all three.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.
Got you. Is there anything i can do to get that PR reviewed and merged?