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

Issue:
[bridges/otelzap] Option to set instrumentation scope attributes
### Problem Statement

The `otelzap.NewCore` function creates a `log.Logger` from the provided `log.LoggerProvider`. Options can be passed to set the instrumentation scope name, version, and schema URL. It would be good to support [instrumentation scope attributes](https://opentelemetry.io/docs/specs/otel/logs/api/#get-a-logger) as well.

### Proposed Solution

The simplest solution would be to add a `WithInstrumentationAttributes` function, which creates an `otelzap.Option` to be passed to `otelzap.NewCore`, which will then add a `log.WithInstrumentationAttributes` option to those passed to `LoggerProvider.Logger`. This is analogous to what is done for the scope version and schema URL.

#### Alternatives

An alternative would be an option to pass in a pre-created `log.Logger` directly instead.

The alternative I used in [this PR](https://github.com/open-telemetry/opentelemetry-collector/pull/12617) is to wrap the `LoggerProvider` interface temporarily to inject the extra option in the call to `LoggerProvider.Logger`.

### Additional Context

This would be used as part of [core#12217](https://github.com/open-telemetry/opentelemetry-collector/issues/12217).


Pull request:
otelzap: Add WithAttributes option
Resolves #6954, see issue for context and solution design.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 61b1667fc..3e41a9c8a 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
 
 - `http.route` attribute to otelhttp server request spans, when `net/http.Request.Pattern` is set in `go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful`, `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin`, `go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux`, `go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho` and `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp`. (#6905)
+- Add `WithAttributes` option to set instrumentation scope attributes on the created `log.Logger` in `go.opentelemetry.io/contrib/bridges/otelzap`. (#6962)
 
 ### Changed
 
diff --git a/bridges/otelzap/core.go b/bridges/otelzap/core.go
index e3564247e..f36240427 100644
--- a/bridges/otelzap/core.go
+++ b/bridges/otelzap/core.go
@@ -39,15 +39,17 @@ import (
 
 	"go.uber.org/zap/zapcore"
 
+	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/log"
 	"go.opentelemetry.io/otel/log/global"
 	semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
 )
 
 type config struct {
-	provider  log.LoggerProvider
-	version   string
-	schemaURL string
+	provider   log.LoggerProvider
+	version    string
+	schemaURL  string
+	attributes []attribute.KeyValue
 }
 
 func newConfig(options []Option) config {
@@ -92,6 +94,15 @@ func WithSchemaURL(schemaURL string) Option {
 	})
 }
 
+// WithAttributes returns an [Option] that configures the instrumentation scope
+// attributes of the [log.Logger] used by a [Core].
+func WithAttributes(attributes ...attribute.KeyValue) Option {
+	return optFunc(func(c config) config {
+		c.attributes = attributes
+		return c
+	})
+}
+
 // WithLoggerProvider returns an [Option] that configures [log.LoggerProvider]
 // used by a [Core] to create its [log.Logger].
 //
@@ -129,6 +140,9 @@ func NewCore(name string, opts ...Option) *Core {
 	if cfg.schemaURL != "" {
 		loggerOpts = append(loggerOpts, log.WithSchemaURL(cfg.schemaURL))
 	}
+	if cfg.attributes != nil {
+		loggerOpts = append(loggerOpts, log.WithInstrumentationAttributes(cfg.attributes...))
+	}
 
 	logger := cfg.provider.Logger(name, loggerOpts...)
 


Test patch:
diff --git a/bridges/otelzap/core_test.go b/bridges/otelzap/core_test.go
index 766fe3c17..825addf2c 100644
--- a/bridges/otelzap/core_test.go
+++ b/bridges/otelzap/core_test.go
@@ -14,6 +14,7 @@ import (
 	"go.uber.org/zap"
 	"go.uber.org/zap/zapcore"
 
+	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/log"
 	"go.opentelemetry.io/otel/log/global"
 	"go.opentelemetry.io/otel/log/logtest"
@@ -231,12 +232,18 @@ func TestNewCoreConfiguration(t *testing.T) {
 				WithLoggerProvider(r),
 				WithVersion("1.0.0"),
 				WithSchemaURL("url"),
+				WithAttributes(attribute.String("testattr", "testval")),
 			)
 		})
 		require.NotNil(t, h.logger)
 		require.Len(t, r.Result(), 1)
 
-		want := &logtest.ScopeRecords{Name: loggerName, Version: "1.0.0", SchemaURL: "url"}
+		want := &logtest.ScopeRecords{
+			Name:       loggerName,
+			Version:    "1.0.0",
+			SchemaURL:  "url",
+			Attributes: attribute.NewSet(attribute.String("testattr", "testval")),
+		}
 		got := r.Result()[0]
 		assert.Equal(t, want, got)
 	})