diff --git a/.chloggen/drosiek-tracing.yaml b/.chloggen/drosiek-tracing.yaml new file mode 100644 index 0000000000000..079d77931630f --- /dev/null +++ b/.chloggen/drosiek-tracing.yaml @@ -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: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: sumologicexporter + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: add support for tracing + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [32315] + +# (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] diff --git a/exporter/sumologicexporter/README.md b/exporter/sumologicexporter/README.md index 4f81f94eee952..477069565565e 100644 --- a/exporter/sumologicexporter/README.md +++ b/exporter/sumologicexporter/README.md @@ -3,7 +3,7 @@ | Status | | | ------------- |-----------| -| Stability | [beta]: metrics, logs | +| Stability | [beta]: metrics, logs, traces | | Distributions | [contrib] | | Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Aexporter%2Fsumologic%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Aexporter%2Fsumologic) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Aexporter%2Fsumologic%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Aexporter%2Fsumologic) | | [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@aboguszewski-sumo](https://www.github.com/aboguszewski-sumo), [@kkujawa-sumo](https://www.github.com/kkujawa-sumo), [@mat-rumian](https://www.github.com/mat-rumian), [@rnishtala-sumo](https://www.github.com/rnishtala-sumo), [@sumo-drosiek](https://www.github.com/sumo-drosiek), [@swiatekm-sumo](https://www.github.com/swiatekm-sumo) | @@ -86,6 +86,8 @@ exporters: max_request_body_size: # format to use when sending logs to Sumo Logic, default = otlp, + # see Sumo Logic documentation for details regarding log formats: + # https://help.sumologic.com/docs/send-data/opentelemetry-collector/data-source-configurations/mapping-records-resources/ log_format: {otlp, json, text} # format to use when sending metrics to Sumo Logic, default = otlp, @@ -99,7 +101,7 @@ exporters: decompose_otlp_histograms: {true, false} # timeout is the timeout for every attempt to send data to the backend, - # maximum connection timeout is 55s, default = 5s + # maximum connection timeout is 55s, default = 30s timeout: # defines if sticky session support is enable. diff --git a/exporter/sumologicexporter/config.go b/exporter/sumologicexporter/config.go index ba1e9e254474d..0623f4eca3383 100644 --- a/exporter/sumologicexporter/config.go +++ b/exporter/sumologicexporter/config.go @@ -37,6 +37,11 @@ type Config struct { // Format to post logs into Sumo. (default json) // * text - Logs will appear in Sumo Logic in text format. // * json - Logs will appear in Sumo Logic in json format. + // * otlp - Logs will be send in otlp format and will appear in Sumo Logic: + // * in json format if record level attributes exists + // * in text format in case of no level attributes + // See Sumo Logic documentation for more details: + // https://help.sumologic.com/docs/send-data/opentelemetry-collector/data-source-configurations/mapping-records-resources/ LogFormat LogFormatType `mapstructure:"log_format"` // Metrics related configuration @@ -46,6 +51,7 @@ type Config struct { // Decompose OTLP Histograms into individual metrics, similar to how they're represented in Prometheus format DecomposeOtlpHistograms bool `mapstructure:"decompose_otlp_histograms"` + // Sumo specific options // Name of the client Client string `mapstructure:"client"` @@ -66,6 +72,30 @@ func createDefaultClientConfig() confighttp.ClientConfig { } func (cfg *Config) Validate() error { + + switch cfg.CompressEncoding { + case configcompression.TypeGzip: + case configcompression.TypeDeflate: + case NoCompression: + + default: + return fmt.Errorf("invalid compression encoding type: %v", cfg.CompressEncoding) + } + + switch cfg.ClientConfig.Compression { + case configcompression.TypeGzip: + case configcompression.TypeDeflate: + case configcompression.TypeZstd: + case NoCompression: + + default: + return fmt.Errorf("invalid compression encoding type: %v", cfg.ClientConfig.Compression) + } + + if cfg.CompressEncoding != NoCompression && cfg.ClientConfig.Compression != DefaultCompressEncoding { + return fmt.Errorf("compress_encoding is deprecated and should not be used when compression is set to a non-default value") + } + switch cfg.LogFormat { case OTLPLogFormat: case JSONFormat: @@ -75,26 +105,16 @@ func (cfg *Config) Validate() error { } switch cfg.MetricFormat { + case OTLPMetricFormat: + case PrometheusFormat: case RemovedGraphiteFormat: return fmt.Errorf("support for the graphite metric format was removed, please use prometheus or otlp instead") case RemovedCarbon2Format: return fmt.Errorf("support for the carbon2 metric format was removed, please use prometheus or otlp instead") - case PrometheusFormat: - case OTLPMetricFormat: default: return fmt.Errorf("unexpected metric format: %s", cfg.MetricFormat) } - switch cfg.ClientConfig.Compression { - case configcompression.TypeGzip: - case configcompression.TypeDeflate: - case configcompression.TypeZstd: - case NoCompression: - - default: - return fmt.Errorf("invalid compression encoding type: %v", cfg.ClientConfig.Compression) - } - if len(cfg.ClientConfig.Endpoint) == 0 && cfg.ClientConfig.Auth == nil { return errors.New("no endpoint and no auth extension specified") } @@ -118,35 +138,39 @@ type LogFormatType string // MetricFormatType represents metric_format type MetricFormatType string +// TraceFormatType represents trace_format +type TraceFormatType string + // PipelineType represents type of the pipeline type PipelineType string -// CompressEncodingType represents type of the pipeline -type CompressEncodingType string - const ( // TextFormat represents log_format: text TextFormat LogFormatType = "text" // JSONFormat represents log_format: json JSONFormat LogFormatType = "json" - // RemovedGraphiteFormat represents the no longer supported graphite metric format + // OTLPLogFormat represents log_format: otlp OTLPLogFormat LogFormatType = "otlp" // RemovedGraphiteFormat represents the no longer supported graphite metric format RemovedGraphiteFormat MetricFormatType = "graphite" // RemovedCarbon2Format represents the no longer supported carbon2 metric format RemovedCarbon2Format MetricFormatType = "carbon2" - // GraphiteFormat represents metric_format: text + // PrometheusFormat represents metric_format: prometheus PrometheusFormat MetricFormatType = "prometheus" // OTLPMetricFormat represents metric_format: otlp OTLPMetricFormat MetricFormatType = "otlp" + // OTLPTraceFormat represents trace_format: otlp + OTLPTraceFormat TraceFormatType = "otlp" // NoCompression represents disabled compression NoCompression configcompression.Type = "" // MetricsPipeline represents metrics pipeline MetricsPipeline PipelineType = "metrics" // LogsPipeline represents metrics pipeline LogsPipeline PipelineType = "logs" + // TracesPipeline represents traces pipeline + TracesPipeline PipelineType = "traces" // defaultTimeout - defaultTimeout time.Duration = 5 * time.Second + defaultTimeout time.Duration = 30 * time.Second // DefaultCompress defines default Compress DefaultCompress bool = true // DefaultCompressEncoding defines default CompressEncoding @@ -157,12 +181,6 @@ const ( DefaultLogFormat LogFormatType = OTLPLogFormat // DefaultMetricFormat defines default MetricFormat DefaultMetricFormat MetricFormatType = OTLPMetricFormat - // DefaultSourceCategory defines default SourceCategory - DefaultSourceCategory string = "" - // DefaultSourceName defines default SourceName - DefaultSourceName string = "" - // DefaultSourceHost defines default SourceHost - DefaultSourceHost string = "" // DefaultClient defines default Client DefaultClient string = "otelcol" // DefaultLogKey defines default LogKey value diff --git a/exporter/sumologicexporter/config_test.go b/exporter/sumologicexporter/config_test.go index 3f335ec7bb428..c182c0b47c191 100644 --- a/exporter/sumologicexporter/config_test.go +++ b/exporter/sumologicexporter/config_test.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package sumologicexporter +package sumologicexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/sumologicexporter" import ( "errors" @@ -69,18 +69,6 @@ func TestInitExporterInvalidConfiguration(t *testing.T) { }, }, }, - { - name: "no endpoint and no auth extension specified", - expectedError: errors.New("no endpoint and no auth extension specified"), - cfg: &Config{ - LogFormat: "json", - MetricFormat: "otlp", - ClientConfig: confighttp.ClientConfig{ - Timeout: defaultTimeout, - Compression: "gzip", - }, - }, - }, } for _, tc := range testcases { diff --git a/exporter/sumologicexporter/exporter.go b/exporter/sumologicexporter/exporter.go index b12a8176d5fab..29df62456ba8b 100644 --- a/exporter/sumologicexporter/exporter.go +++ b/exporter/sumologicexporter/exporter.go @@ -20,6 +20,7 @@ import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/plog" "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" "go.uber.org/zap" "github.com/open-telemetry/opentelemetry-collector-contrib/extension/sumologicextension" @@ -120,6 +121,28 @@ func newMetricsExporter( ) } +func newTracesExporter( + ctx context.Context, + params exporter.CreateSettings, + cfg *Config, +) (exporter.Traces, error) { + se := initExporter(cfg, params) + + return exporterhelper.NewTracesExporter( + ctx, + params, + cfg, + se.pushTracesData, + // Disable exporterhelper Timeout, since we are using a custom mechanism + // within exporter itself + exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}), + exporterhelper.WithRetry(cfg.BackOffConfig), + exporterhelper.WithQueue(cfg.QueueSettings), + exporterhelper.WithStart(se.start), + exporterhelper.WithShutdown(se.shutdown), + ) +} + // start starts the exporter func (se *sumologicexporter) start(ctx context.Context, host component.Host) (err error) { se.host = host @@ -374,6 +397,26 @@ func (se *sumologicexporter) handleUnauthorizedErrors(ctx context.Context, errs } } +func (se *sumologicexporter) pushTracesData(ctx context.Context, td ptrace.Traces) error { + logsURL, metricsURL, tracesURL := se.getDataURLs() + sdr := newSender( + se.logger, + se.config, + se.getHTTPClient(), + se.prometheusFormatter, + metricsURL, + logsURL, + tracesURL, + se.StickySessionCookie, + se.SetStickySessionCookie, + se.id, + ) + + err := sdr.sendTraces(ctx, td) + se.handleUnauthorizedErrors(ctx, err) + return err +} + func (se *sumologicexporter) StickySessionCookie() string { if se.foundSumologicExtension { return se.sumologicExtension.StickySessionCookie() diff --git a/exporter/sumologicexporter/exporter_test.go b/exporter/sumologicexporter/exporter_test.go index 9c8dfa76ce484..596d4d85c81a1 100644 --- a/exporter/sumologicexporter/exporter_test.go +++ b/exporter/sumologicexporter/exporter_test.go @@ -8,6 +8,7 @@ import ( "errors" "net/http" "net/http/httptest" + "sync" "sync/atomic" "testing" "time" @@ -16,12 +17,14 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/config/configcompression" "go.opentelemetry.io/collector/config/confighttp" "go.opentelemetry.io/collector/consumer/consumererror" "go.opentelemetry.io/collector/exporter" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/plog" "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" "go.uber.org/zap" ) @@ -241,8 +244,6 @@ func TestPartiallyFailed(t *testing.T) { func TestInvalidHTTPCLient(t *testing.T) { exp := initExporter(&Config{ - LogFormat: "json", - MetricFormat: "otlp", ClientConfig: confighttp.ClientConfig{ Endpoint: "test_endpoint", CustomRoundTripper: func(_ http.RoundTripper) (http.RoundTripper, error) { @@ -495,6 +496,84 @@ func TestMetricsPrometheusFormatMetadataFilter(t *testing.T) { assert.NoError(t, err) } +func Benchmark_ExporterPushLogs(b *testing.B) { + createConfig := func() *Config { + config := createDefaultConfig().(*Config) + config.MetricFormat = PrometheusFormat + config.LogFormat = TextFormat + config.ClientConfig.Auth = nil + config.ClientConfig.Compression = configcompression.TypeGzip + return config + } + + testServer := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) { + })) + b.Cleanup(func() { testServer.Close() }) + + cfg := createConfig() + cfg.ClientConfig.Endpoint = testServer.URL + + exp := initExporter(cfg, createExporterCreateSettings()) + require.NoError(b, exp.start(context.Background(), componenttest.NewNopHost())) + defer func() { + require.NoError(b, exp.shutdown(context.Background())) + }() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + wg := sync.WaitGroup{} + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + logs := logRecordsToLogs(exampleNLogs(128)) + logs.MarkReadOnly() + err := exp.pushLogsData(context.Background(), logs) + if err != nil { + b.Logf("Failed pushing logs: %v", err) + } + wg.Done() + }() + } + + wg.Wait() + } +} + +func TestSendEmptyLogsOTLP(t *testing.T) { + test := prepareExporterTest(t, createTestConfig(), []func(w http.ResponseWriter, req *http.Request){ + // No request is sent + }) + + logs := plog.NewLogs() + logs.MarkReadOnly() + + err := test.exp.pushLogsData(context.Background(), logs) + assert.NoError(t, err) +} + +func TestSendEmptyMetricsOTLP(t *testing.T) { + test := prepareExporterTest(t, createTestConfig(), []func(w http.ResponseWriter, req *http.Request){ + // No request is sent + }) + test.exp.config.MetricFormat = OTLPMetricFormat + + metrics := metricPairToMetrics() + + err := test.exp.pushMetricsData(context.Background(), metrics) + assert.NoError(t, err) +} + +func TestSendEmptyTraces(t *testing.T) { + test := prepareExporterTest(t, createTestConfig(), []func(w http.ResponseWriter, req *http.Request){ + // No request is sent + }) + + traces := ptrace.NewTraces() + + err := test.exp.pushTracesData(context.Background(), traces) + assert.NoError(t, err) +} + func TestGetSignalURL(t *testing.T) { testCases := []struct { description string @@ -524,6 +603,20 @@ func TestGetSignalURL(t *testing.T) { endpointURL: "http://localhost", expected: "http://localhost/v1/traces", }, + { + description: "always add suffix for logs if not present", + signalType: component.DataTypeLogs, + cfg: Config{LogFormat: OTLPLogFormat}, + endpointURL: "http://localhost", + expected: "http://localhost/v1/logs", + }, + { + description: "always add suffix for metrics if not present", + signalType: component.DataTypeMetrics, + cfg: Config{MetricFormat: OTLPMetricFormat}, + endpointURL: "http://localhost", + expected: "http://localhost/v1/metrics", + }, { description: "no change if suffix already present", signalType: component.DataTypeTraces, diff --git a/exporter/sumologicexporter/factory.go b/exporter/sumologicexporter/factory.go index 2918daed344f6..daab2a3c6800f 100644 --- a/exporter/sumologicexporter/factory.go +++ b/exporter/sumologicexporter/factory.go @@ -24,6 +24,7 @@ func NewFactory() exporter.Factory { createDefaultConfig, exporter.WithLogs(createLogsExporter, metadata.LogsStability), exporter.WithMetrics(createMetricsExporter, metadata.MetricsStability), + exporter.WithTraces(createTracesExporter, metadata.TracesStability), ) } @@ -69,3 +70,16 @@ func createMetricsExporter( return exp, nil } + +func createTracesExporter( + ctx context.Context, + params exporter.CreateSettings, + cfg component.Config, +) (exporter.Traces, error) { + exp, err := newTracesExporter(ctx, params, cfg.(*Config)) + if err != nil { + return nil, fmt.Errorf("failed to create the traces exporter: %w", err) + } + + return exp, nil +} diff --git a/exporter/sumologicexporter/factory_test.go b/exporter/sumologicexporter/factory_test.go index 8902cca7de734..53bfac1b42413 100644 --- a/exporter/sumologicexporter/factory_test.go +++ b/exporter/sumologicexporter/factory_test.go @@ -36,7 +36,7 @@ func TestCreateDefaultConfig(t *testing.T) { Client: "otelcol", ClientConfig: confighttp.ClientConfig{ - Timeout: 5 * time.Second, + Timeout: 30 * time.Second, Compression: "gzip", Auth: &configauth.Authentication{ AuthenticatorID: component.NewID(metadata.Type), diff --git a/exporter/sumologicexporter/generated_component_test.go b/exporter/sumologicexporter/generated_component_test.go index b25d93c71310a..09f4afc5e9087 100644 --- a/exporter/sumologicexporter/generated_component_test.go +++ b/exporter/sumologicexporter/generated_component_test.go @@ -48,6 +48,13 @@ func TestComponentLifecycle(t *testing.T) { return factory.CreateMetricsExporter(ctx, set, cfg) }, }, + + { + name: "traces", + createFn: func(ctx context.Context, set exporter.CreateSettings, cfg component.Config) (component.Component, error) { + return factory.CreateTracesExporter(ctx, set, cfg) + }, + }, } cm, err := confmaptest.LoadConf("metadata.yaml") diff --git a/exporter/sumologicexporter/internal/metadata/generated_status.go b/exporter/sumologicexporter/internal/metadata/generated_status.go index 4ab516dd7be2f..c80070ecf8700 100644 --- a/exporter/sumologicexporter/internal/metadata/generated_status.go +++ b/exporter/sumologicexporter/internal/metadata/generated_status.go @@ -13,4 +13,5 @@ var ( const ( MetricsStability = component.StabilityLevelBeta LogsStability = component.StabilityLevelBeta + TracesStability = component.StabilityLevelBeta ) diff --git a/exporter/sumologicexporter/metadata.yaml b/exporter/sumologicexporter/metadata.yaml index 69bfad133f02f..b9347ba9334ec 100644 --- a/exporter/sumologicexporter/metadata.yaml +++ b/exporter/sumologicexporter/metadata.yaml @@ -4,7 +4,7 @@ scope_name: otelcol/sumologic status: class: exporter stability: - beta: [metrics, logs] + beta: [metrics, logs, traces] distributions: [contrib] codeowners: active: [aboguszewski-sumo, kkujawa-sumo, mat-rumian, rnishtala-sumo, sumo-drosiek, swiatekm-sumo] diff --git a/exporter/sumologicexporter/sender.go b/exporter/sumologicexporter/sender.go index 5ed1d491f2ebd..65dc4b2f91a85 100644 --- a/exporter/sumologicexporter/sender.go +++ b/exporter/sumologicexporter/sender.go @@ -20,6 +20,7 @@ import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/plog" "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" "go.uber.org/zap" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/sumologicexporter/internal/observability" @@ -28,6 +29,7 @@ import ( var ( metricsMarshaler = pmetric.ProtoMarshaler{} logsMarshaler = plog.ProtoMarshaler{} + tracesMarshaler = ptrace.ProtoMarshaler{} ) // metricPair represents information required to send one metric to the Sumo Logic @@ -314,6 +316,8 @@ func (s *sender) createRequest(ctx context.Context, pipeline PipelineType, data url = s.dataURLMetrics case LogsPipeline: url = s.dataURLLogs + case TracesPipeline: + url = s.dataURLTraces default: return nil, fmt.Errorf("unknown pipeline type: %s", pipeline) } @@ -593,6 +597,30 @@ func (s *sender) appendAndMaybeSend( return sent, err } +// sendTraces sends traces in right format basing on the s.config.TraceFormat +func (s *sender) sendTraces(ctx context.Context, td ptrace.Traces) error { + return s.sendOTLPTraces(ctx, td) +} + +// sendOTLPTraces sends trace records in OTLP format +func (s *sender) sendOTLPTraces(ctx context.Context, td ptrace.Traces) error { + if td.ResourceSpans().Len() == 0 { + s.logger.Debug("there are no traces to send, moving on") + return nil + } + + capacity := td.SpanCount() + + body, err := tracesMarshaler.MarshalTraces(td) + if err != nil { + return err + } + if err := s.send(ctx, TracesPipeline, newCountingReader(capacity).withBytes(body), fields{}); err != nil { + return err + } + return nil +} + func addSourcesHeaders(req *http.Request, flds fields) { sourceHeaderValues := getSourcesHeaders(flds) @@ -648,6 +676,10 @@ func addMetricsHeaders(req *http.Request, mf MetricFormatType) error { return nil } +func addTracesHeaders(req *http.Request) { + req.Header.Add(headerContentType, contentTypeOTLP) +} + func (s *sender) addRequestHeaders(req *http.Request, pipeline PipelineType, flds fields) error { req.Header.Add(headerClient, s.config.Client) addSourcesHeaders(req, flds) @@ -659,6 +691,8 @@ func (s *sender) addRequestHeaders(req *http.Request, pipeline PipelineType, fld if err := addMetricsHeaders(req, s.config.MetricFormat); err != nil { return err } + case TracesPipeline: + addTracesHeaders(req) default: return fmt.Errorf("unexpected pipeline: %v", pipeline) } diff --git a/exporter/sumologicexporter/sender_test.go b/exporter/sumologicexporter/sender_test.go index 00e2d2b9200a1..d451e78d056e8 100644 --- a/exporter/sumologicexporter/sender_test.go +++ b/exporter/sumologicexporter/sender_test.go @@ -27,6 +27,7 @@ import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/plog" "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) @@ -137,6 +138,16 @@ func exampleTwoLogs() []plog.LogRecord { return buffer } +func exampleNLogs(n int) []plog.LogRecord { + buffer := make([]plog.LogRecord, n) + for i := 0; i < n; i++ { + buffer[i] = plog.NewLogRecord() + buffer[i].Body().SetStr("Example log") + } + + return buffer +} + func decodeGzip(t *testing.T, data io.Reader) string { r, err := gzip.NewReader(data) require.NoError(t, err) @@ -170,6 +181,24 @@ func decodeZlib(t *testing.T, data io.Reader) string { return string(buf) } +func TestSendTrace(t *testing.T) { + tracesMarshaler = ptrace.ProtoMarshaler{} + td := exampleTrace() + traceBody, err := tracesMarshaler.MarshalTraces(td) + assert.NoError(t, err) + test := prepareSenderTest(t, NoCompression, []func(w http.ResponseWriter, req *http.Request){ + func(_ http.ResponseWriter, req *http.Request) { + body := extractBody(t, req) + assert.Equal(t, string(traceBody), body) + assert.Equal(t, "otelcol", req.Header.Get("X-Sumo-Client")) + assert.Equal(t, "application/x-protobuf", req.Header.Get("Content-Type")) + }, + }) + + err = test.s.sendTraces(context.Background(), td) + assert.NoError(t, err) +} + func TestSendLogs(t *testing.T) { test := prepareSenderTest(t, NoCompression, []func(w http.ResponseWriter, req *http.Request){ func(_ http.ResponseWriter, req *http.Request) { @@ -1102,7 +1131,6 @@ func TestSendOTLPHistogram(t *testing.T) { defer func() { test.srv.Close() }() test.s.config.DecomposeOtlpHistograms = true - test.s.config.MetricFormat = OTLPMetricFormat metricHistogram, attrs := exampleHistogramMetric() diff --git a/exporter/sumologicexporter/test_data_test.go b/exporter/sumologicexporter/test_data_test.go index 1747512881483..cfb5b97f58ba8 100644 --- a/exporter/sumologicexporter/test_data_test.go +++ b/exporter/sumologicexporter/test_data_test.go @@ -6,6 +6,7 @@ package sumologicexporter // import "github.com/open-telemetry/opentelemetry-col import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" ) func exampleIntMetric() (pmetric.Metric, pcommon.Map) { @@ -290,3 +291,21 @@ func fieldsFromMap(s map[string]string) fields { } return newFields(attrMap) } + +func exampleTrace() ptrace.Traces { + td := ptrace.NewTraces() + rs := td.ResourceSpans().AppendEmpty() + rs.Resource().Attributes().PutStr("hostname", "testHost") + rs.Resource().Attributes().PutStr("_sourceHost", "source_host") + rs.Resource().Attributes().PutStr("_sourceName", "source_name") + rs.Resource().Attributes().PutStr("_sourceCategory", "source_category") + span := rs.ScopeSpans().AppendEmpty().Spans().AppendEmpty() + span.SetTraceID(pcommon.TraceID([16]byte{0x5B, 0x8E, 0xFF, 0xF7, 0x98, 0x3, 0x81, 0x3, 0xD2, 0x69, 0xB6, 0x33, 0x81, 0x3F, 0xC6, 0xC})) + span.SetSpanID(pcommon.SpanID([8]byte{0xEE, 0xE1, 0x9B, 0x7E, 0xC3, 0xC1, 0xB1, 0x73})) + span.SetName("testSpan") + span.SetStartTimestamp(1544712660000000000) + span.SetEndTimestamp(1544712661000000000) + span.Attributes().PutInt("attr1", 55) + td.MarkReadOnly() + return td +}