Skip to content

Commit 68e1000

Browse files
authored
Add profiles support to OTLP exporter (#11435)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description This uses the profiles internal packages that were implemented in previous PRs to provide profiles support to the OTLP exporter. This PR requires #11226✅ to be merged first.
1 parent f330cfb commit 68e1000

File tree

10 files changed

+479
-19
lines changed

10 files changed

+479
-19
lines changed

.chloggen/otlp-exporter-profiles.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
7+
component: otlpexporter
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add profiles support to OTLP exporter
11+
12+
# One or more tracking issues or pull requests related to the change
13+
issues: [11435]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext:
19+
20+
# Optional: The change log or logs in which this entry should be included.
21+
# e.g. '[user]' or '[user, api]'
22+
# Include 'user' if the change is relevant to end users.
23+
# Include 'api' if there is a change to a library API.
24+
# Default: '[user]'
25+
change_logs: [api]

exporter/otlpexporter/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
<!-- status autogenerated section -->
44
| Status | |
55
| ------------- |-----------|
6-
| Stability | [beta]: logs |
6+
| Stability | [development]: profiles |
7+
| | [beta]: logs |
78
| | [stable]: traces, metrics |
89
| Distributions | [core], [contrib], [k8s] |
910
| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aopen%20label%3Aexporter%2Fotlp%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aopen+is%3Aissue+label%3Aexporter%2Fotlp) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aclosed%20label%3Aexporter%2Fotlp%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aclosed+is%3Aissue+label%3Aexporter%2Fotlp) |
1011

12+
[development]: https://github.com/open-telemetry/opentelemetry-collector#development
1113
[beta]: https://github.com/open-telemetry/opentelemetry-collector#beta
1214
[stable]: https://github.com/open-telemetry/opentelemetry-collector#stable
1315
[core]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol

exporter/otlpexporter/cfg-schema.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ fields:
6868
- name: endpoint
6969
kind: string
7070
doc: |
71-
The target to which the exporter is going to send traces or metrics,
72-
using the gRPC protocol. The valid syntax is described at
71+
The target to which the exporter is going to send traces, metrics, logs or
72+
profiles using the gRPC protocol. The valid syntax is described at
7373
https://github.com/grpc/grpc/blob/master/doc/naming.md.
7474
- name: compression
7575
kind: string

exporter/otlpexporter/factory.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,20 @@ import (
1515
"go.opentelemetry.io/collector/exporter"
1616
"go.opentelemetry.io/collector/exporter/exporterbatcher"
1717
"go.opentelemetry.io/collector/exporter/exporterhelper"
18+
"go.opentelemetry.io/collector/exporter/exporterhelper/exporterhelperprofiles"
19+
"go.opentelemetry.io/collector/exporter/exporterprofiles"
1820
"go.opentelemetry.io/collector/exporter/otlpexporter/internal/metadata"
1921
)
2022

2123
// NewFactory creates a factory for OTLP exporter.
2224
func NewFactory() exporter.Factory {
23-
return exporter.NewFactory(
25+
return exporterprofiles.NewFactory(
2426
metadata.Type,
2527
createDefaultConfig,
26-
exporter.WithTraces(createTraces, metadata.TracesStability),
27-
exporter.WithMetrics(createMetrics, metadata.MetricsStability),
28-
exporter.WithLogs(createLogs, metadata.LogsStability),
28+
exporterprofiles.WithTraces(createTraces, metadata.TracesStability),
29+
exporterprofiles.WithMetrics(createMetrics, metadata.MetricsStability),
30+
exporterprofiles.WithLogs(createLogs, metadata.LogsStability),
31+
exporterprofiles.WithProfiles(createProfilesExporter, metadata.ProfilesStability),
2932
)
3033
}
3134

@@ -104,3 +107,22 @@ func createLogs(
104107
exporterhelper.WithShutdown(oce.shutdown),
105108
)
106109
}
110+
111+
func createProfilesExporter(
112+
ctx context.Context,
113+
set exporter.Settings,
114+
cfg component.Config,
115+
) (exporterprofiles.Profiles, error) {
116+
oce := newExporter(cfg, set)
117+
oCfg := cfg.(*Config)
118+
return exporterhelperprofiles.NewProfilesExporter(ctx, set, cfg,
119+
oce.pushProfiles,
120+
exporterhelper.WithCapabilities(consumer.Capabilities{MutatesData: false}),
121+
exporterhelper.WithTimeout(oCfg.TimeoutConfig),
122+
exporterhelper.WithRetry(oCfg.RetryConfig),
123+
exporterhelper.WithQueue(oCfg.QueueConfig),
124+
exporterhelper.WithBatcher(oCfg.BatcherConfig),
125+
exporterhelper.WithStart(oce.start),
126+
exporterhelper.WithShutdown(oce.shutdown),
127+
)
128+
}

exporter/otlpexporter/factory_test.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"go.opentelemetry.io/collector/config/configretry"
2020
"go.opentelemetry.io/collector/config/configtls"
2121
"go.opentelemetry.io/collector/exporter/exporterhelper"
22+
"go.opentelemetry.io/collector/exporter/exporterprofiles"
2223
"go.opentelemetry.io/collector/exporter/exportertest"
2324
"go.opentelemetry.io/collector/internal/testutil"
2425
)
@@ -197,3 +198,143 @@ func TestCreateLogs(t *testing.T) {
197198
require.NoError(t, err)
198199
require.NotNil(t, oexp)
199200
}
201+
202+
func TestCreateProfiles(t *testing.T) {
203+
endpoint := testutil.GetAvailableLocalAddress(t)
204+
tests := []struct {
205+
name string
206+
config *Config
207+
mustFailOnStart bool
208+
}{
209+
{
210+
name: "UseSecure",
211+
config: &Config{
212+
ClientConfig: configgrpc.ClientConfig{
213+
Endpoint: endpoint,
214+
TLSSetting: configtls.ClientConfig{
215+
Insecure: false,
216+
},
217+
},
218+
},
219+
},
220+
{
221+
name: "Keepalive",
222+
config: &Config{
223+
ClientConfig: configgrpc.ClientConfig{
224+
Endpoint: endpoint,
225+
Keepalive: &configgrpc.KeepaliveClientConfig{
226+
Time: 30 * time.Second,
227+
Timeout: 25 * time.Second,
228+
PermitWithoutStream: true,
229+
},
230+
},
231+
},
232+
},
233+
{
234+
name: "NoneCompression",
235+
config: &Config{
236+
ClientConfig: configgrpc.ClientConfig{
237+
Endpoint: endpoint,
238+
Compression: "none",
239+
},
240+
},
241+
},
242+
{
243+
name: "GzipCompression",
244+
config: &Config{
245+
ClientConfig: configgrpc.ClientConfig{
246+
Endpoint: endpoint,
247+
Compression: configcompression.TypeGzip,
248+
},
249+
},
250+
},
251+
{
252+
name: "SnappyCompression",
253+
config: &Config{
254+
ClientConfig: configgrpc.ClientConfig{
255+
Endpoint: endpoint,
256+
Compression: configcompression.TypeSnappy,
257+
},
258+
},
259+
},
260+
{
261+
name: "ZstdCompression",
262+
config: &Config{
263+
ClientConfig: configgrpc.ClientConfig{
264+
Endpoint: endpoint,
265+
Compression: configcompression.TypeZstd,
266+
},
267+
},
268+
},
269+
{
270+
name: "Headers",
271+
config: &Config{
272+
ClientConfig: configgrpc.ClientConfig{
273+
Endpoint: endpoint,
274+
Headers: map[string]configopaque.String{
275+
"hdr1": "val1",
276+
"hdr2": "val2",
277+
},
278+
},
279+
},
280+
},
281+
{
282+
name: "NumConsumers",
283+
config: &Config{
284+
ClientConfig: configgrpc.ClientConfig{
285+
Endpoint: endpoint,
286+
},
287+
},
288+
},
289+
{
290+
name: "CaCert",
291+
config: &Config{
292+
ClientConfig: configgrpc.ClientConfig{
293+
Endpoint: endpoint,
294+
TLSSetting: configtls.ClientConfig{
295+
Config: configtls.Config{
296+
CAFile: filepath.Join("testdata", "test_cert.pem"),
297+
},
298+
},
299+
},
300+
},
301+
},
302+
{
303+
name: "CertPemFileError",
304+
config: &Config{
305+
ClientConfig: configgrpc.ClientConfig{
306+
Endpoint: endpoint,
307+
TLSSetting: configtls.ClientConfig{
308+
Config: configtls.Config{
309+
CAFile: "nosuchfile",
310+
},
311+
},
312+
},
313+
},
314+
mustFailOnStart: true,
315+
},
316+
}
317+
318+
for _, tt := range tests {
319+
t.Run(tt.name, func(t *testing.T) {
320+
factory := NewFactory()
321+
set := exportertest.NewNopSettings()
322+
consumer, err := factory.(exporterprofiles.Factory).CreateProfiles(context.Background(), set, tt.config)
323+
require.NoError(t, err)
324+
assert.NotNil(t, consumer)
325+
err = consumer.Start(context.Background(), componenttest.NewNopHost())
326+
if tt.mustFailOnStart {
327+
require.Error(t, err)
328+
} else {
329+
require.NoError(t, err)
330+
}
331+
// Shutdown is called even when Start fails
332+
err = consumer.Shutdown(context.Background())
333+
if err != nil {
334+
// Since the endpoint of OTLP exporter doesn't actually exist,
335+
// exporter may already stop because it cannot connect.
336+
assert.Equal(t, "rpc error: code = Canceled desc = grpc: the client connection is closing", err.Error())
337+
}
338+
})
339+
}
340+
}

exporter/otlpexporter/go.mod

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ require (
1515
go.opentelemetry.io/collector/confmap v1.17.0
1616
go.opentelemetry.io/collector/consumer v0.111.0
1717
go.opentelemetry.io/collector/exporter v0.111.0
18+
go.opentelemetry.io/collector/exporter/exporterhelper/exporterhelperprofiles v0.0.0-00010101000000-000000000000
19+
go.opentelemetry.io/collector/exporter/exporterprofiles v0.111.0
1820
go.opentelemetry.io/collector/pdata v1.17.0
21+
go.opentelemetry.io/collector/pdata/pprofile v0.111.0
1922
go.opentelemetry.io/collector/pdata/testdata v0.111.0
2023
go.uber.org/goleak v1.3.0
2124
go.uber.org/zap v1.27.0
@@ -49,14 +52,14 @@ require (
4952
go.opentelemetry.io/collector/config/confignet v1.17.0 // indirect
5053
go.opentelemetry.io/collector/config/configtelemetry v0.111.0 // indirect
5154
go.opentelemetry.io/collector/config/internal v0.111.0 // indirect
55+
go.opentelemetry.io/collector/consumer/consumererror/consumererrorprofiles v0.0.0-00010101000000-000000000000 // indirect
5256
go.opentelemetry.io/collector/consumer/consumerprofiles v0.111.0 // indirect
5357
go.opentelemetry.io/collector/consumer/consumertest v0.111.0 // indirect
54-
go.opentelemetry.io/collector/exporter/exporterprofiles v0.111.0 // indirect
5558
go.opentelemetry.io/collector/extension v0.111.0 // indirect
5659
go.opentelemetry.io/collector/extension/auth v0.111.0 // indirect
5760
go.opentelemetry.io/collector/extension/experimental/storage v0.111.0 // indirect
58-
go.opentelemetry.io/collector/pdata/pprofile v0.111.0 // indirect
5961
go.opentelemetry.io/collector/pipeline v0.111.0 // indirect
62+
go.opentelemetry.io/collector/pipeline/pipelineprofiles v0.0.0-00010101000000-000000000000 // indirect
6063
go.opentelemetry.io/collector/receiver v0.111.0 // indirect
6164
go.opentelemetry.io/collector/receiver/receiverprofiles v0.111.0 // indirect
6265
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect
@@ -128,6 +131,12 @@ replace go.opentelemetry.io/collector => ../..
128131

129132
replace go.opentelemetry.io/collector/component/componentstatus => ../../component/componentstatus
130133

134+
replace go.opentelemetry.io/collector/exporter/exporterhelper/exporterhelperprofiles => ../exporterhelper/exporterhelperprofiles
135+
136+
replace go.opentelemetry.io/collector/consumer/consumererror/consumererrorprofiles => ../../consumer/consumererror/consumererrorprofiles
137+
138+
replace go.opentelemetry.io/collector/pipeline/pipelineprofiles => ../../pipeline/pipelineprofiles
139+
131140
retract (
132141
v0.76.0 // Depends on retracted pdata v1.0.0-rc10 module, use v0.76.1
133142
v0.69.0 // Release failed, use v0.69.1

exporter/otlpexporter/internal/metadata/generated_status.go

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

exporter/otlpexporter/metadata.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ status:
66
stability:
77
stable: [traces, metrics]
88
beta: [logs]
9+
development: [profiles]
910
distributions: [core, contrib, k8s]
1011

1112
tests:
1213
config:
13-
endpoint: otelcol:4317
14+
endpoint: otelcol:4317

exporter/otlpexporter/otlp.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import (
2525
"go.opentelemetry.io/collector/pdata/plog/plogotlp"
2626
"go.opentelemetry.io/collector/pdata/pmetric"
2727
"go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp"
28+
"go.opentelemetry.io/collector/pdata/pprofile"
29+
"go.opentelemetry.io/collector/pdata/pprofile/pprofileotlp"
2830
"go.opentelemetry.io/collector/pdata/ptrace"
2931
"go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp"
3032
)
@@ -34,12 +36,13 @@ type baseExporter struct {
3436
config *Config
3537

3638
// gRPC clients and connection.
37-
traceExporter ptraceotlp.GRPCClient
38-
metricExporter pmetricotlp.GRPCClient
39-
logExporter plogotlp.GRPCClient
40-
clientConn *grpc.ClientConn
41-
metadata metadata.MD
42-
callOptions []grpc.CallOption
39+
traceExporter ptraceotlp.GRPCClient
40+
metricExporter pmetricotlp.GRPCClient
41+
logExporter plogotlp.GRPCClient
42+
profileExporter pprofileotlp.GRPCClient
43+
clientConn *grpc.ClientConn
44+
metadata metadata.MD
45+
callOptions []grpc.CallOption
4346

4447
settings component.TelemetrySettings
4548

@@ -66,6 +69,7 @@ func (e *baseExporter) start(ctx context.Context, host component.Host) (err erro
6669
e.traceExporter = ptraceotlp.NewGRPCClient(e.clientConn)
6770
e.metricExporter = pmetricotlp.NewGRPCClient(e.clientConn)
6871
e.logExporter = plogotlp.NewGRPCClient(e.clientConn)
72+
e.profileExporter = pprofileotlp.NewGRPCClient(e.clientConn)
6973
headers := map[string]string{}
7074
for k, v := range e.config.ClientConfig.Headers {
7175
headers[k] = string(v)
@@ -133,6 +137,22 @@ func (e *baseExporter) pushLogs(ctx context.Context, ld plog.Logs) error {
133137
return nil
134138
}
135139

140+
func (e *baseExporter) pushProfiles(ctx context.Context, td pprofile.Profiles) error {
141+
req := pprofileotlp.NewExportRequestFromProfiles(td)
142+
resp, respErr := e.profileExporter.Export(e.enhanceContext(ctx), req, e.callOptions...)
143+
if err := processError(respErr); err != nil {
144+
return err
145+
}
146+
partialSuccess := resp.PartialSuccess()
147+
if !(partialSuccess.ErrorMessage() == "" && partialSuccess.RejectedProfiles() == 0) {
148+
e.settings.Logger.Warn("Partial success response",
149+
zap.String("message", resp.PartialSuccess().ErrorMessage()),
150+
zap.Int64("dropped_profiles", resp.PartialSuccess().RejectedProfiles()),
151+
)
152+
}
153+
return nil
154+
}
155+
136156
func (e *baseExporter) enhanceContext(ctx context.Context) context.Context {
137157
if e.metadata.Len() > 0 {
138158
return metadata.NewOutgoingContext(ctx, e.metadata)

0 commit comments

Comments
 (0)