diff --git a/exporters/otlp/otlplog/otlploghttp/exporter.go b/exporters/otlp/otlplog/otlploghttp/exporter.go index 2338361e4c7..ac4c403a970 100644 --- a/exporters/otlp/otlplog/otlploghttp/exporter.go +++ b/exporters/otlp/otlplog/otlploghttp/exporter.go @@ -5,8 +5,10 @@ package otlploghttp // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/o import ( "context" + "errors" "sync/atomic" + "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/transform" "go.opentelemetry.io/otel/sdk/log" ) @@ -37,13 +39,20 @@ func newExporter(c *client, _ config) (*Exporter, error) { return e, nil } +// Used for testing. +var transformResourceLogs = transform.ResourceLogs + // Export transforms and transmits log records to an OTLP receiver. func (e *Exporter) Export(ctx context.Context, records []log.Record) error { if e.stopped.Load() { return nil } - // TODO: implement. - return nil + otlp, err := transformResourceLogs(records) + if otlp != nil { + // Best effort upload of transformable logs. + err = errors.Join(err, e.client.Load().UploadLogs(ctx, otlp)) + } + return err } // Shutdown shuts down the Exporter. Calls to Export or ForceFlush will perform diff --git a/exporters/otlp/otlplog/otlploghttp/exporter_test.go b/exporters/otlp/otlplog/otlploghttp/exporter_test.go index d818739a070..8779a1b2b9f 100644 --- a/exporters/otlp/otlplog/otlploghttp/exporter_test.go +++ b/exporters/otlp/otlplog/otlploghttp/exporter_test.go @@ -5,6 +5,7 @@ package otlploghttp import ( "context" + "errors" "runtime" "sync" "sync/atomic" @@ -14,8 +15,63 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/sdk/log" + logpb "go.opentelemetry.io/proto/otlp/logs/v1" ) +func TestExporterExportErrors(t *testing.T) { + var ( + errUpload = errors.New("upload") + errTForm = errors.New("transform") + ) + + c := &client{ + uploadLogs: func(context.Context, *logpb.ResourceLogs) error { + return errUpload + }, + } + + orig := transformResourceLogs + transformResourceLogs = func(r []log.Record) (*logpb.ResourceLogs, error) { + return new(logpb.ResourceLogs), errTForm + } + t.Cleanup(func() { transformResourceLogs = orig }) + + e, err := newExporter(c, config{}) + require.NoError(t, err, "New") + + err = e.Export(context.Background(), make([]log.Record, 1)) + assert.ErrorIs(t, err, errUpload) + assert.ErrorIs(t, err, errTForm) +} + +func TestExporterExport(t *testing.T) { + var uploads int + c := &client{ + uploadLogs: func(context.Context, *logpb.ResourceLogs) error { + uploads++ + return nil + }, + } + + orig := transformResourceLogs + var got []log.Record + transformResourceLogs = func(r []log.Record) (*logpb.ResourceLogs, error) { + got = r + return new(logpb.ResourceLogs), nil + } + t.Cleanup(func() { transformResourceLogs = orig }) + + e, err := newExporter(c, config{}) + require.NoError(t, err, "New") + + ctx := context.Background() + want := make([]log.Record, 1) + assert.NoError(t, e.Export(ctx, want)) + + assert.Equal(t, 1, uploads, "client UploadLogs calls") + assert.Equal(t, want, got, "transformed log records") +} + func TestExporterShutdown(t *testing.T) { ctx := context.Background() e, err := New(ctx) diff --git a/exporters/otlp/otlplog/otlploghttp/internal/transform/log.go b/exporters/otlp/otlplog/otlploghttp/internal/transform/log.go new file mode 100644 index 00000000000..bea72f2991a --- /dev/null +++ b/exporters/otlp/otlplog/otlploghttp/internal/transform/log.go @@ -0,0 +1,17 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package transform provides transformation functionality from the +// sdk/log data-types into OTLP data-types. +package transform // import "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp/internal/transform" + +import ( + lpb "go.opentelemetry.io/proto/otlp/logs/v1" + + "go.opentelemetry.io/otel/sdk/log" +) + +func ResourceLogs([]log.Record) (*lpb.ResourceLogs, error) { + // TODO: implement + return nil, nil +}