Repository: open-telemetry/opentelemetry-go-contrib (go)

Issue:
config: add stdout exporter for log signal
### Problem Statement

As it was done for metric and tracing, the logging signal should support configuring a console exporter via config. 

### Proposed Solution

Add the following code and add unit tests as well.

```
	if exporter.Console != nil && exporter.OTLP != nil {
	 	return nil, errors.New("must not specify multiple exporters")
	 }

	 if exporter.Console != nil {
	 	return stdoutlog.New(
	 		stdoutlogWithPrettyPrint(),
	 	)
	 }
```

### Additional Context

This was not possible because of a change needed in the configuration schema (https://github.com/open-telemetry/opentelemetry-configuration/commit/834ae3c589341da26b39c8dc142e55a5b50eaed0) which has been merged, but updating the configuration package depends on https://github.com/open-telemetry/opentelemetry-go-contrib/pull/4832 to support another change in the configuration schema.


Pull request description:
config: Implement stdoutlog exporter
Depends on #5849

Closes #5432

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b5e1a4109..24f112dd9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 ### Added
 
 - Add the new `go.opentelemetry.io/contrib/instrgen` package to provide auto-generated source code instrumentation. (#3068, #3108)
+- Support for stdoutlog exporter in `go.opentelemetry.io/contrib/config`. (#5850)
 
 ## [1.28.0/0.53.0/0.22.0/0.8.0/0.3.0/0.1.0] - 2024-07-02
 
diff --git a/config/go.mod b/config/go.mod
index 72e3f4500..c4cac43d7 100644
--- a/config/go.mod
+++ b/config/go.mod
@@ -12,6 +12,7 @@ require (
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0
 	go.opentelemetry.io/otel/exporters/prometheus v0.50.0
+	go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.4.0
 	go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0
 	go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0
 	go.opentelemetry.io/otel/log v0.4.0
diff --git a/config/go.sum b/config/go.sum
index 8c44a544d..75b08e1a8 100644
--- a/config/go.sum
+++ b/config/go.sum
@@ -53,6 +53,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk=
 go.opentelemetry.io/otel/exporters/prometheus v0.50.0 h1:2Ewsda6hejmbhGFyUvWZjUThC98Cf8Zy6g0zkIimOng=
 go.opentelemetry.io/otel/exporters/prometheus v0.50.0/go.mod h1:pMm5PkUo5YwbLiuEf7t2xg4wbP0/eSJrMxIMxKosynY=
+go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.4.0 h1:0MH3f8lZrflbUWXVxyBg/zviDFdGE062uKh5+fu8Vv0=
+go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.4.0/go.mod h1:Vh68vYiHY5mPdekTr0ox0sALsqjoVy0w3Os278yX5SQ=
 go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0 h1:BJee2iLkfRfl9lc7aFmBwkWxY/RI1RDdXepSF6y8TPE=
 go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0/go.mod h1:DIzlHs3DRscCIBU3Y9YSzPfScwnYnzfnCd4g8zA7bZc=
 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 h1:EVSnY9JbEEW92bEkIYOVMw4q1WJxIAGoFTrtYOzWuRQ=
diff --git a/config/log.go b/config/log.go
index d09b59dc4..f30b37c8a 100644
--- a/config/log.go
+++ b/config/log.go
@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
+	"go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
 	"go.opentelemetry.io/otel/log"
 	"go.opentelemetry.io/otel/log/noop"
 	sdklog "go.opentelemetry.io/otel/sdk/log"
@@ -64,6 +65,16 @@ func logProcessor(ctx context.Context, processor LogRecordProcessor) (sdklog.Pro
 }
 
 func logExporter(ctx context.Context, exporter LogRecordExporter) (sdklog.Exporter, error) {
+	if exporter.Console != nil && exporter.OTLP != nil {
+		return nil, errors.New("must not specify multiple exporters")
+	}
+
+	if exporter.Console != nil {
+		return stdoutlog.New(
+			stdoutlog.WithPrettyPrint(),
+		)
+	}
+
 	if exporter.OTLP != nil {
 		switch exporter.OTLP.Protocol {
 		case protocolProtobufHTTP:


Test patch:
diff --git a/config/log_test.go b/config/log_test.go
index c670da28e..88318dc36 100644
--- a/config/log_test.go
+++ b/config/log_test.go
@@ -14,6 +14,7 @@ import (
 	"github.com/stretchr/testify/require"
 
 	"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
+	"go.opentelemetry.io/otel/exporters/stdout/stdoutlog"
 	"go.opentelemetry.io/otel/log"
 	"go.opentelemetry.io/otel/log/noop"
 	sdklog "go.opentelemetry.io/otel/sdk/log"
@@ -59,8 +60,15 @@ func TestLoggerProvider(t *testing.T) {
 
 func TestLogProcessor(t *testing.T) {
 	ctx := context.Background()
+
 	otlpHTTPExporter, err := otlploghttp.New(ctx)
 	require.NoError(t, err)
+
+	consoleExporter, err := stdoutlog.New(
+		stdoutlog.WithPrettyPrint(),
+	)
+	require.NoError(t, err)
+
 	testCases := []struct {
 		name          string
 		processor     LogRecordProcessor
@@ -149,6 +157,21 @@ func TestLogProcessor(t *testing.T) {
 			},
 			wantErr: errors.New("no valid log exporter"),
 		},
+		{
+			name: "batch/console",
+			processor: LogRecordProcessor{
+				Batch: &BatchLogRecordProcessor{
+					MaxExportBatchSize: ptr(0),
+					ExportTimeout:      ptr(0),
+					MaxQueueSize:       ptr(0),
+					ScheduleDelay:      ptr(0),
+					Exporter: LogRecordExporter{
+						Console: map[string]any{},
+					},
+				},
+			},
+			wantProcessor: sdklog.NewBatchProcessor(consoleExporter),
+		},
 		{
 			name: "batch/otlp-http-exporter",
 			processor: LogRecordProcessor{
@@ -341,6 +364,17 @@ func TestLogProcessor(t *testing.T) {
 			},
 			wantErr: errors.New("no valid log exporter"),
 		},
+		{
+			name: "simple/console",
+			processor: LogRecordProcessor{
+				Simple: &SimpleLogRecordProcessor{
+					Exporter: LogRecordExporter{
+						Console: map[string]any{},
+					},
+				},
+			},
+			wantProcessor: sdklog.NewSimpleProcessor(consoleExporter),
+		},
 		{
 			name: "simple/otlp-exporter",
 			processor: LogRecordProcessor{