Repository: open-telemetry/opentelemetry-collector (Go)

Issue:
Components that do not use exporterhelper.WithRetry do not get any logging of export errors.
**Describe the bug**

After https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/25900, all error logging for the google cloud exporters was removed.

**Steps to reproduce**
See reproduction steps in https://github.com/GoogleCloudPlatform/opentelemetry-operations-go/issues/760, for an example.

**What did you expect to see?**
I expected to see errors printed, similar to how they are printed when retry settings.Enabled = false:

https://github.com/open-telemetry/opentelemetry-collector/blob/66166c4cc371fbe945d7ff19232decd3ee65f76a/exporter/exporterhelper/retry_sender.go#L108-L115

**What did you see instead?**

Failures without any error messages.

**What version did you use?**
Version: v0.88.0 (contrib)



Discussion:

Patch:
diff --git a/.chloggen/fix_logging_without_retry.yaml b/.chloggen/fix_logging_without_retry.yaml
new file mode 100644
index 00000000000..c654a917981
--- /dev/null
+++ b/.chloggen/fix_logging_without_retry.yaml
@@ -0,0 +1,25 @@
+# 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. otlpreceiver)
+component: exporterhelper
+
+# A brief description of the change.  Surround your text with quotes ("") if it needs to start with a backtick (`).
+note: Log export errors when retry is not used by the component.
+
+# One or more tracking issues or pull requests related to the change
+issues: [8791]
+
+# (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:
+
+# 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]
\ No newline at end of file
diff --git a/exporter/exporterhelper/common.go b/exporter/exporterhelper/common.go
index cc97cbc3db1..57c89fe0554 100644
--- a/exporter/exporterhelper/common.go
+++ b/exporter/exporterhelper/common.go
@@ -6,6 +6,8 @@ package exporterhelper // import "go.opentelemetry.io/collector/exporter/exporte
 import (
 	"context"
 
+	"go.uber.org/zap"
+
 	"go.opentelemetry.io/collector/component"
 	"go.opentelemetry.io/collector/consumer"
 	"go.opentelemetry.io/collector/exporter"
@@ -40,6 +42,22 @@ func (b *baseRequestSender) setNextSender(nextSender requestSender) {
 	b.nextSender = nextSender
 }
 
+type errorLoggingRequestSender struct {
+	baseRequestSender
+	logger *zap.Logger
+}
+
+func (l *errorLoggingRequestSender) send(req internal.Request) error {
+	err := l.baseRequestSender.send(req)
+	if err != nil {
+		l.logger.Error(
+			"Exporting failed",
+			zap.Error(err),
+		)
+	}
+	return err
+}
+
 type obsrepSenderFactory func(obsrep *ObsReport) requestSender
 
 // baseRequest is a base implementation for the internal.Request.
@@ -176,7 +194,7 @@ func newBaseExporter(set exporter.CreateSettings, signal component.DataType, req
 
 		queueSender:   &baseRequestSender{},
 		obsrepSender:  osf(obsReport),
-		retrySender:   &baseRequestSender{},
+		retrySender:   &errorLoggingRequestSender{logger: set.Logger},
 		timeoutSender: &timeoutSender{cfg: NewDefaultTimeoutSettings()},
 
 		set:    set,


Test patch:
diff --git a/exporter/exporterhelper/common_test.go b/exporter/exporterhelper/common_test.go
index 75c915fc43a..10166c32974 100644
--- a/exporter/exporterhelper/common_test.go
+++ b/exporter/exporterhelper/common_test.go
@@ -12,6 +12,8 @@ import (
 	"go.opencensus.io/tag"
 	"go.opentelemetry.io/otel/codes"
 	sdktrace "go.opentelemetry.io/otel/sdk/trace"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zaptest/observer"
 
 	"go.opentelemetry.io/collector/component"
 	"go.opentelemetry.io/collector/component/componenttest"
@@ -79,3 +81,16 @@ func TestQueueRetryOptionsWithRequestExporter(t *testing.T) {
 			WithRetry(NewDefaultRetrySettings()), WithQueue(NewDefaultQueueSettings()))
 	})
 }
+
+func TestBaseExporterLogging(t *testing.T) {
+	set := exportertest.NewNopCreateSettings()
+	logger, observed := observer.New(zap.DebugLevel)
+	set.Logger = zap.New(logger)
+	bs, err := newBaseExporter(set, "", true, nil, nil, newNoopObsrepSender)
+	require.Nil(t, err)
+	require.True(t, bs.requestExporter)
+	sendErr := bs.send(newErrorRequest(context.Background()))
+	require.Error(t, sendErr)
+
+	require.Len(t, observed.FilterLevelExact(zap.ErrorLevel).All(), 1)
+}