From cac9534f2bfd8da9640db64ea887fd5170bcf250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Thu, 13 Mar 2025 19:26:54 +0100 Subject: [PATCH 1/2] chore: set gorm user-agent header Set the user-agent header to go-gorm-spanner if it is Spanner gorm that is creating the connection. --- driver.go | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/driver.go b/driver.go index 3f8fba51..be5a8f52 100644 --- a/driver.go +++ b/driver.go @@ -23,6 +23,8 @@ import ( "log/slog" "math/big" "regexp" + "runtime" + "runtime/debug" "strconv" "strings" "sync" @@ -43,6 +45,9 @@ import ( const userAgent = "go-sql-spanner/1.11.2" // x-release-please-version +const gormModule = "github.com/googleapis/go-gorm-spanner" +const gormUserAgent = "go-gorm-spanner" + // LevelNotice is the default logging level that the Spanner database/sql driver // uses for informational logs. This level is deliberately chosen to be one level // lower than the default log level, which is slog.LevelInfo. This prevents the @@ -448,7 +453,26 @@ func createConnector(d *Driver, connectorConfig ConnectorConfig) (*connector, er connectorConfig.DecodeToNativeArrays = val } } - config.UserAgent = userAgent + + // Check if it is Spanner gorm that is creating the connection. + // If so, we should set a different user-agent header than the + // default go-sql-spanner header. + callers := make([]uintptr, 20) + length := runtime.Callers(0, callers) + frames := runtime.CallersFrames(callers[0:length]) + gorm := false + for frame, more := frames.Next(); more; { + if strings.HasPrefix(frame.Function, gormModule) { + gorm = true + break + } + frame, more = frames.Next() + } + if gorm { + config.UserAgent = spannerGormHeader() + } else { + config.UserAgent = userAgent + } var logger *slog.Logger if connectorConfig.logger == nil { d := slog.Default() @@ -476,6 +500,19 @@ func createConnector(d *Driver, connectorConfig ConnectorConfig) (*connector, er return c, nil } +func spannerGormHeader() string { + info, ok := debug.ReadBuildInfo() + if !ok { + return gormUserAgent + } + for _, module := range info.Deps { + if module.Path == gormModule { + return fmt.Sprintf("%s/%s", gormUserAgent, module.Version) + } + } + return gormUserAgent +} + func (c *connector) Connect(ctx context.Context) (driver.Conn, error) { c.closerMu.RLock() defer c.closerMu.RUnlock() From 45329abc4dad80bcb779042382707db5105955ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Tue, 18 Mar 2025 16:18:13 +0100 Subject: [PATCH 2/2] chore: move check for connection from gorm to separate func --- driver.go | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/driver.go b/driver.go index c34ab9fd..5e275542 100644 --- a/driver.go +++ b/driver.go @@ -476,18 +476,7 @@ func createConnector(d *Driver, connectorConfig ConnectorConfig) (*connector, er // Check if it is Spanner gorm that is creating the connection. // If so, we should set a different user-agent header than the // default go-sql-spanner header. - callers := make([]uintptr, 20) - length := runtime.Callers(0, callers) - frames := runtime.CallersFrames(callers[0:length]) - gorm := false - for frame, more := frames.Next(); more; { - if strings.HasPrefix(frame.Function, gormModule) { - gorm = true - break - } - frame, more = frames.Next() - } - if gorm { + if isConnectionFromGorm() { config.UserAgent = spannerGormHeader() } else { config.UserAgent = userAgent @@ -524,6 +513,21 @@ func createConnector(d *Driver, connectorConfig ConnectorConfig) (*connector, er return c, nil } +func isConnectionFromGorm() bool { + callers := make([]uintptr, 20) + length := runtime.Callers(0, callers) + frames := runtime.CallersFrames(callers[0:length]) + gorm := false + for frame, more := frames.Next(); more; { + if strings.HasPrefix(frame.Function, gormModule) { + gorm = true + break + } + frame, more = frames.Next() + } + return gorm +} + func spannerGormHeader() string { info, ok := debug.ReadBuildInfo() if !ok {