1// Copyright The OpenTelemetry Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package otlphttpexporter
16
17import (
18	"context"
19	"fmt"
20	"net/url"
21	"time"
22
23	"go.opentelemetry.io/collector/component"
24	"go.opentelemetry.io/collector/config"
25	"go.opentelemetry.io/collector/config/confighttp"
26	"go.opentelemetry.io/collector/consumer"
27	"go.opentelemetry.io/collector/exporter/exporterhelper"
28)
29
30const (
31	// The value of "type" key in configuration.
32	typeStr = "otlphttp"
33)
34
35// NewFactory creates a factory for OTLP exporter.
36func NewFactory() component.ExporterFactory {
37	return exporterhelper.NewFactory(
38		typeStr,
39		createDefaultConfig,
40		exporterhelper.WithTraces(createTracesExporter),
41		exporterhelper.WithMetrics(createMetricsExporter),
42		exporterhelper.WithLogs(createLogsExporter))
43}
44
45func createDefaultConfig() config.Exporter {
46	return &Config{
47		ExporterSettings: config.NewExporterSettings(config.NewID(typeStr)),
48		RetrySettings:    exporterhelper.DefaultRetrySettings(),
49		QueueSettings:    exporterhelper.DefaultQueueSettings(),
50		HTTPClientSettings: confighttp.HTTPClientSettings{
51			Endpoint: "",
52			Timeout:  30 * time.Second,
53			Headers:  map[string]string{},
54			// We almost read 0 bytes, so no need to tune ReadBufferSize.
55			WriteBufferSize: 512 * 1024,
56		},
57	}
58}
59
60func composeSignalURL(oCfg *Config, signalOverrideURL string, signalName string) (string, error) {
61	switch {
62	case signalOverrideURL != "":
63		_, err := url.Parse(signalOverrideURL)
64		if err != nil {
65			return "", fmt.Errorf("%s_endpoint must be a valid URL", signalName)
66		}
67		return signalOverrideURL, nil
68	case oCfg.Endpoint == "":
69		return "", fmt.Errorf("either endpoint or %s_endpoint must be specified", signalName)
70	default:
71		return oCfg.Endpoint + "/v1/" + signalName, nil
72	}
73}
74
75func createTracesExporter(
76	_ context.Context,
77	set component.ExporterCreateSettings,
78	cfg config.Exporter,
79) (component.TracesExporter, error) {
80	oce, err := newExporter(cfg, set.Logger)
81	if err != nil {
82		return nil, err
83	}
84	oCfg := cfg.(*Config)
85
86	oce.tracesURL, err = composeSignalURL(oCfg, oCfg.TracesEndpoint, "traces")
87	if err != nil {
88		return nil, err
89	}
90
91	return exporterhelper.NewTracesExporter(
92		cfg,
93		set,
94		oce.pushTraces,
95		exporterhelper.WithStart(oce.start),
96		exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}),
97		// explicitly disable since we rely on http.Client timeout logic.
98		exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}),
99		exporterhelper.WithRetry(oCfg.RetrySettings),
100		exporterhelper.WithQueue(oCfg.QueueSettings))
101}
102
103func createMetricsExporter(
104	_ context.Context,
105	set component.ExporterCreateSettings,
106	cfg config.Exporter,
107) (component.MetricsExporter, error) {
108	oce, err := newExporter(cfg, set.Logger)
109	if err != nil {
110		return nil, err
111	}
112	oCfg := cfg.(*Config)
113
114	oce.metricsURL, err = composeSignalURL(oCfg, oCfg.MetricsEndpoint, "metrics")
115	if err != nil {
116		return nil, err
117	}
118
119	return exporterhelper.NewMetricsExporter(
120		cfg,
121		set,
122		oce.pushMetrics,
123		exporterhelper.WithStart(oce.start),
124		exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}),
125		// explicitly disable since we rely on http.Client timeout logic.
126		exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}),
127		exporterhelper.WithRetry(oCfg.RetrySettings),
128		exporterhelper.WithQueue(oCfg.QueueSettings))
129}
130
131func createLogsExporter(
132	_ context.Context,
133	set component.ExporterCreateSettings,
134	cfg config.Exporter,
135) (component.LogsExporter, error) {
136	oce, err := newExporter(cfg, set.Logger)
137	if err != nil {
138		return nil, err
139	}
140	oCfg := cfg.(*Config)
141
142	oce.logsURL, err = composeSignalURL(oCfg, oCfg.LogsEndpoint, "logs")
143	if err != nil {
144		return nil, err
145	}
146
147	return exporterhelper.NewLogsExporter(
148		cfg,
149		set,
150		oce.pushLogs,
151		exporterhelper.WithStart(oce.start),
152		exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}),
153		// explicitly disable since we rely on http.Client timeout logic.
154		exporterhelper.WithTimeout(exporterhelper.TimeoutSettings{Timeout: 0}),
155		exporterhelper.WithRetry(oCfg.RetrySettings),
156		exporterhelper.WithQueue(oCfg.QueueSettings))
157}
158