Skip to content

Commit 5adedc5

Browse files
codebotendmathieu
andauthored
add grpc support for log export (#6340)
This adds support for grpc export for the logging signal in the config package. ~Leaving this in draft until #6338 is merged and i'll rebase.~ --------- Signed-off-by: Alex Boten <[email protected]> Co-authored-by: Damien Mathieu <[email protected]>
1 parent 71759c6 commit 5adedc5

File tree

5 files changed

+167
-0
lines changed

5 files changed

+167
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
1111
### Added
1212

1313
- Added support for providing `endpoint`, `pollingIntervalMs` and `initialSamplingRate` using environment variable `OTEL_TRACES_SAMPLER_ARG` in `go.opentelemetry.io/contrib/samples/jaegerremote`. (#6310)
14+
- Added support exporting logs via OTLP over gRPC in `go.opentelemetry.io/contrib/config`. (#6340)
1415

1516
### Fixed
1617

config/go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/prometheus/client_golang v1.20.5
77
github.com/stretchr/testify v1.9.0
88
go.opentelemetry.io/otel v1.32.0
9+
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0
910
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0
1011
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0
1112
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0

config/go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
4545
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
4646
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
4747
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
48+
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls=
49+
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0/go.mod h1:hKvJwTzJdp90Vh7p6q/9PAOd55dI6WA6sWj62a/JvSs=
4850
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 h1:S+LdBGiQXtJdowoJoQPEtI52syEP/JYBUpjO49EQhV8=
4951
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0/go.mod h1:5KXybFvPGds3QinJWQT7pmXf+TN5YIa7CNYObWRkj50=
5052
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 h1:j7ZSD+5yn+lo3sGV69nW04rRR0jhYnBwjuX3r0HvnK0=

config/log.go

+45
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"net/url"
1111
"time"
1212

13+
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
1314
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
1415
"go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
1516
"go.opentelemetry.io/otel/log"
@@ -79,6 +80,8 @@ func logExporter(ctx context.Context, exporter LogRecordExporter) (sdklog.Export
7980
switch *exporter.OTLP.Protocol {
8081
case protocolProtobufHTTP:
8182
return otlpHTTPLogExporter(ctx, exporter.OTLP)
83+
case protocolProtobufGRPC:
84+
return otlpGRPCLogExporter(ctx, exporter.OTLP)
8285
default:
8386
return nil, fmt.Errorf("unsupported protocol %q", *exporter.OTLP.Protocol)
8487
}
@@ -153,3 +156,45 @@ func otlpHTTPLogExporter(ctx context.Context, otlpConfig *OTLP) (sdklog.Exporter
153156

154157
return otlploghttp.New(ctx, opts...)
155158
}
159+
160+
func otlpGRPCLogExporter(ctx context.Context, otlpConfig *OTLP) (sdklog.Exporter, error) {
161+
var opts []otlploggrpc.Option
162+
163+
if otlpConfig.Endpoint != nil {
164+
u, err := url.ParseRequestURI(*otlpConfig.Endpoint)
165+
if err != nil {
166+
return nil, err
167+
}
168+
// ParseRequestURI leaves the Host field empty when no
169+
// scheme is specified (i.e. localhost:4317). This check is
170+
// here to support the case where a user may not specify a
171+
// scheme. The code does its best effort here by using
172+
// otlpConfig.Endpoint as-is in that case
173+
if u.Host != "" {
174+
opts = append(opts, otlploggrpc.WithEndpoint(u.Host))
175+
} else {
176+
opts = append(opts, otlploggrpc.WithEndpoint(*otlpConfig.Endpoint))
177+
}
178+
if u.Scheme == "http" {
179+
opts = append(opts, otlploggrpc.WithInsecure())
180+
}
181+
}
182+
if otlpConfig.Compression != nil {
183+
switch *otlpConfig.Compression {
184+
case compressionGzip:
185+
opts = append(opts, otlploggrpc.WithCompressor(*otlpConfig.Compression))
186+
case compressionNone:
187+
// none requires no options
188+
default:
189+
return nil, fmt.Errorf("unsupported compression %q", *otlpConfig.Compression)
190+
}
191+
}
192+
if otlpConfig.Timeout != nil && *otlpConfig.Timeout > 0 {
193+
opts = append(opts, otlploggrpc.WithTimeout(time.Millisecond*time.Duration(*otlpConfig.Timeout)))
194+
}
195+
if len(otlpConfig.Headers) > 0 {
196+
opts = append(opts, otlploggrpc.WithHeaders(toStringMap(otlpConfig.Headers)))
197+
}
198+
199+
return otlploggrpc.New(ctx, opts...)
200+
}

config/log_test.go

+118
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/stretchr/testify/assert"
1414
"github.com/stretchr/testify/require"
1515

16+
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
1617
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
1718
"go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
1819
"go.opentelemetry.io/otel/log"
@@ -64,6 +65,9 @@ func TestLogProcessor(t *testing.T) {
6465
otlpHTTPExporter, err := otlploghttp.New(ctx)
6566
require.NoError(t, err)
6667

68+
otlpGRPCExporter, err := otlploggrpc.New(ctx)
69+
require.NoError(t, err)
70+
6771
consoleExporter, err := stdoutlog.New(
6872
stdoutlog.WithPrettyPrint(),
6973
)
@@ -172,6 +176,120 @@ func TestLogProcessor(t *testing.T) {
172176
},
173177
wantProcessor: sdklog.NewBatchProcessor(consoleExporter),
174178
},
179+
{
180+
name: "batch/otlp-grpc-exporter-no-endpoint",
181+
processor: LogRecordProcessor{
182+
Batch: &BatchLogRecordProcessor{
183+
MaxExportBatchSize: ptr(0),
184+
ExportTimeout: ptr(0),
185+
MaxQueueSize: ptr(0),
186+
ScheduleDelay: ptr(0),
187+
Exporter: LogRecordExporter{
188+
OTLP: &OTLP{
189+
Protocol: ptr("grpc"),
190+
Compression: ptr("gzip"),
191+
Timeout: ptr(1000),
192+
Headers: []NameStringValuePair{
193+
{Name: "test", Value: ptr("test1")},
194+
},
195+
},
196+
},
197+
},
198+
},
199+
wantProcessor: sdklog.NewBatchProcessor(otlpGRPCExporter),
200+
},
201+
{
202+
name: "batch/otlp-grpc-exporter",
203+
processor: LogRecordProcessor{
204+
Batch: &BatchLogRecordProcessor{
205+
MaxExportBatchSize: ptr(0),
206+
ExportTimeout: ptr(0),
207+
MaxQueueSize: ptr(0),
208+
ScheduleDelay: ptr(0),
209+
Exporter: LogRecordExporter{
210+
OTLP: &OTLP{
211+
Protocol: ptr("grpc"),
212+
Endpoint: ptr("http://localhost:4317"),
213+
Compression: ptr("gzip"),
214+
Timeout: ptr(1000),
215+
Headers: []NameStringValuePair{
216+
{Name: "test", Value: ptr("test1")},
217+
},
218+
},
219+
},
220+
},
221+
},
222+
wantProcessor: sdklog.NewBatchProcessor(otlpGRPCExporter),
223+
},
224+
{
225+
name: "batch/otlp-grpc-exporter-no-scheme",
226+
processor: LogRecordProcessor{
227+
Batch: &BatchLogRecordProcessor{
228+
MaxExportBatchSize: ptr(0),
229+
ExportTimeout: ptr(0),
230+
MaxQueueSize: ptr(0),
231+
ScheduleDelay: ptr(0),
232+
Exporter: LogRecordExporter{
233+
OTLP: &OTLP{
234+
Protocol: ptr("grpc"),
235+
Endpoint: ptr("localhost:4317"),
236+
Compression: ptr("gzip"),
237+
Timeout: ptr(1000),
238+
Headers: []NameStringValuePair{
239+
{Name: "test", Value: ptr("test1")},
240+
},
241+
},
242+
},
243+
},
244+
},
245+
wantProcessor: sdklog.NewBatchProcessor(otlpGRPCExporter),
246+
},
247+
{
248+
name: "batch/otlp-grpc-invalid-endpoint",
249+
processor: LogRecordProcessor{
250+
Batch: &BatchLogRecordProcessor{
251+
MaxExportBatchSize: ptr(0),
252+
ExportTimeout: ptr(0),
253+
MaxQueueSize: ptr(0),
254+
ScheduleDelay: ptr(0),
255+
Exporter: LogRecordExporter{
256+
OTLP: &OTLP{
257+
Protocol: ptr("grpc"),
258+
Endpoint: ptr(" "),
259+
Compression: ptr("gzip"),
260+
Timeout: ptr(1000),
261+
Headers: []NameStringValuePair{
262+
{Name: "test", Value: ptr("test1")},
263+
},
264+
},
265+
},
266+
},
267+
},
268+
wantErr: &url.Error{Op: "parse", URL: " ", Err: errors.New("invalid URI for request")},
269+
},
270+
{
271+
name: "batch/otlp-grpc-invalid-compression",
272+
processor: LogRecordProcessor{
273+
Batch: &BatchLogRecordProcessor{
274+
MaxExportBatchSize: ptr(0),
275+
ExportTimeout: ptr(0),
276+
MaxQueueSize: ptr(0),
277+
ScheduleDelay: ptr(0),
278+
Exporter: LogRecordExporter{
279+
OTLP: &OTLP{
280+
Protocol: ptr("grpc"),
281+
Endpoint: ptr("localhost:4317"),
282+
Compression: ptr("invalid"),
283+
Timeout: ptr(1000),
284+
Headers: []NameStringValuePair{
285+
{Name: "test", Value: ptr("test1")},
286+
},
287+
},
288+
},
289+
},
290+
},
291+
wantErr: errors.New("unsupported compression \"invalid\""),
292+
},
175293
{
176294
name: "batch/otlp-http-exporter",
177295
processor: LogRecordProcessor{

0 commit comments

Comments
 (0)