diff --git a/internal/logging/logging.go b/internal/logging/logging.go index 8fd3082b5..0e95a9875 100644 --- a/internal/logging/logging.go +++ b/internal/logging/logging.go @@ -20,6 +20,7 @@ import ( "context" "log/slog" "os" + "sync/atomic" ) // DebugEnabledFunc is a function type that determines if debug logging is enabled @@ -93,3 +94,28 @@ type LoggerSetterGetter interface { // Logger returns the logger for the object Logger() *slog.Logger } + +type LogHolder struct { + // logger is an slog.Logger pointer to use the driver + logger atomic.Pointer[slog.Logger] +} + +// Logger returns the logger for the LogHolder. If nil, returns slog.Default(). +func (l *LogHolder) Logger() *slog.Logger { + if lg := l.logger.Load(); lg != nil { + return lg + } + return slog.Default() // We rarely get here, just being defensive +} + +// SetLogger sets the logger for the LogHolder. If nil, sets the default logger. +func (l *LogHolder) SetLogger(newLogger *slog.Logger) { + if newLogger == nil { + l.logger.Store(slog.New(slog.DiscardHandler)) // Assume nil as discarding logs + return + } + l.logger.Store(newLogger) +} + +// Ensure LogHolder implements LoggerSetterGetter +var _ LoggerSetterGetter = &LogHolder{} diff --git a/pkg/action/action.go b/pkg/action/action.go index 75575b3f3..446dcd79d 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -29,7 +29,6 @@ import ( "slices" "strings" "sync" - "sync/atomic" "text/template" "time" @@ -41,6 +40,7 @@ import ( "sigs.k8s.io/kustomize/kyaml/kio" kyaml "sigs.k8s.io/kustomize/kyaml/yaml" + "helm.sh/helm/v4/internal/logging" "helm.sh/helm/v4/pkg/chart/common" chart "helm.sh/helm/v4/pkg/chart/v2" chartutil "helm.sh/helm/v4/pkg/chart/v2/util" @@ -110,11 +110,11 @@ type Configuration struct { // HookOutputFunc called with container name and returns and expects writer that will receive the log output. HookOutputFunc func(namespace, pod, container string) io.Writer - // logger is an slog.Logger pointer to use with the Configuration instance - logger atomic.Pointer[slog.Logger] - // Mutex is an exclusive lock for concurrent access to the action mutex sync.Mutex + + // Embed a LogHolder to provide logger functionality + logging.LogHolder } func NewConfiguration() *Configuration { @@ -554,23 +554,6 @@ func (cfg *Configuration) SetHookOutputFunc(hookOutputFunc func(_, _, _ string) cfg.HookOutputFunc = hookOutputFunc } -// Logger returns the logger for the Configuration. If nil, returns slog.Default(). -func (cfg *Configuration) Logger() *slog.Logger { - if lg := cfg.logger.Load(); lg != nil { - return lg - } - return slog.Default() // We rarely get here, just be defensive -} - -// SetLogger sets the logger for the Configuration. If nil, sets the default logger. -func (cfg *Configuration) SetLogger(newLogger *slog.Logger) { - if newLogger == nil { - cfg.logger.Store(slog.New(slog.DiscardHandler)) // Assume nil as discarding logs - return - } - cfg.logger.Store(newLogger) -} - func determineReleaseSSApplyMethod(serverSideApply bool) release.ApplyMethod { if serverSideApply { return release.ApplyMethodServerSideApply diff --git a/pkg/kube/client.go b/pkg/kube/client.go index cdd95d66f..9fe26bcc9 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -30,7 +30,6 @@ import ( "reflect" "strings" "sync" - "sync/atomic" jsonpatch "github.com/evanphx/json-patch/v5" v1 "k8s.io/api/core/v1" @@ -40,6 +39,8 @@ import ( "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + "helm.sh/helm/v4/internal/logging" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -82,11 +83,12 @@ type Client struct { Factory Factory // Namespace allows to bypass the kubeconfig file for the choice of the namespace Namespace string - // logger is an slog.Logger pointer to use with the kube client - logger atomic.Pointer[slog.Logger] Waiter kubeClient kubernetes.Interface + + // Embed a LogHolder to provide logger functionality + logging.LogHolder } var _ Interface = (*Client)(nil) @@ -1198,19 +1200,3 @@ func (e *joinedErrors) Error() string { func (e *joinedErrors) Unwrap() []error { return e.errs } - -// logger returns the logger for the Client. If nil, returns slog.Default(). -func (c *Client) Logger() *slog.Logger { - if lg := c.logger.Load(); lg != nil { - return lg - } - return slog.Default() // We rarely get here, just being defensive -} - -func (c *Client) SetLogger(newLogger *slog.Logger) { - if newLogger == nil { - c.logger.Store(slog.New(slog.DiscardHandler)) // Assume nil as discarding logs - return - } - c.logger.Store(newLogger) -} diff --git a/pkg/storage/driver/cfgmaps.go b/pkg/storage/driver/cfgmaps.go index 4d356a2c4..81e22ef40 100644 --- a/pkg/storage/driver/cfgmaps.go +++ b/pkg/storage/driver/cfgmaps.go @@ -22,7 +22,6 @@ import ( "log/slog" "strconv" "strings" - "sync/atomic" "time" v1 "k8s.io/api/core/v1" @@ -32,6 +31,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "helm.sh/helm/v4/internal/logging" "helm.sh/helm/v4/pkg/release" rspb "helm.sh/helm/v4/pkg/release/v1" ) @@ -45,8 +45,9 @@ const ConfigMapsDriverName = "ConfigMap" // ConfigMapsInterface. type ConfigMaps struct { impl corev1.ConfigMapInterface - // logger is an slog.Logger pointer to use the driver - logger atomic.Pointer[slog.Logger] + + // Embed a LogHolder to provide logger functionality + logging.LogHolder } // NewConfigMaps initializes a new ConfigMaps wrapping an implementation of @@ -281,19 +282,3 @@ func newConfigMapsObject(key string, rls *rspb.Release, lbs labels) (*v1.ConfigM Data: map[string]string{"release": s}, }, nil } - -// logger returns the logger for the ConfigMaps driver. If nil, returns slog.Default(). -func (cfgmaps *ConfigMaps) Logger() *slog.Logger { - if lg := cfgmaps.logger.Load(); lg != nil { - return lg - } - return slog.Default() // We rarely get here, just being defensive -} - -func (cfgmaps *ConfigMaps) SetLogger(newLogger *slog.Logger) { - if newLogger == nil { - cfgmaps.logger.Store(slog.New(slog.DiscardHandler)) // Assume nil as discarding logs - return - } - cfgmaps.logger.Store(newLogger) -} diff --git a/pkg/storage/driver/memory.go b/pkg/storage/driver/memory.go index dc950df36..ec7470aac 100644 --- a/pkg/storage/driver/memory.go +++ b/pkg/storage/driver/memory.go @@ -21,8 +21,8 @@ import ( "strconv" "strings" "sync" - "sync/atomic" + "helm.sh/helm/v4/internal/logging" "helm.sh/helm/v4/pkg/release" ) @@ -44,8 +44,8 @@ type Memory struct { namespace string // A map of namespaces to releases cache map[string]memReleases - // logger is an slog.Logger pointer to use the driver - logger atomic.Pointer[slog.Logger] + // Embed a LogHolder to provide logger functionality + logging.LogHolder } // NewMemory initializes a new memory driver. @@ -253,18 +253,3 @@ func (mem *Memory) rlock() func() { // ```defer unlock(mem.rlock())```, locks mem for reading at the // call point of defer and unlocks upon exiting the block. func unlock(fn func()) { fn() } - -func (mem *Memory) Logger() *slog.Logger { - if lg := mem.logger.Load(); lg != nil { - return lg - } - return slog.Default() // We rarely get here, just being defensive -} - -func (mem *Memory) SetLogger(newLogger *slog.Logger) { - if newLogger == nil { - mem.logger.Store(slog.New(slog.DiscardHandler)) // Assume nil as discarding logs - return - } - mem.logger.Store(newLogger) -} diff --git a/pkg/storage/driver/secrets.go b/pkg/storage/driver/secrets.go index d4a18e966..2c29c436b 100644 --- a/pkg/storage/driver/secrets.go +++ b/pkg/storage/driver/secrets.go @@ -22,7 +22,6 @@ import ( "log/slog" "strconv" "strings" - "sync/atomic" "time" v1 "k8s.io/api/core/v1" @@ -32,6 +31,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "helm.sh/helm/v4/internal/logging" "helm.sh/helm/v4/pkg/release" rspb "helm.sh/helm/v4/pkg/release/v1" ) @@ -45,8 +45,8 @@ const SecretsDriverName = "Secret" // SecretsInterface. type Secrets struct { impl corev1.SecretInterface - // logger is an slog.Logger pointer to use the driver - logger atomic.Pointer[slog.Logger] + // Embed a LogHolder to provide logger functionality + logging.LogHolder } // NewSecrets initializes a new Secrets wrapping an implementation of @@ -278,19 +278,3 @@ func newSecretsObject(key string, rls *rspb.Release, lbs labels) (*v1.Secret, er Data: map[string][]byte{"release": []byte(s)}, }, nil } - -// logger returns the logger for the Secrets driver. If nil, returns slog.Default(). -func (secrets *Secrets) Logger() *slog.Logger { - if lg := secrets.logger.Load(); lg != nil { - return lg - } - return slog.Default() // We rarely get here, just being defensive -} - -func (secrets *Secrets) SetLogger(newLogger *slog.Logger) { - if newLogger == nil { - secrets.logger.Store(slog.New(slog.DiscardHandler)) // Assume nil as discarding logs - return - } - secrets.logger.Store(newLogger) -} diff --git a/pkg/storage/driver/sql.go b/pkg/storage/driver/sql.go index df7d18ae2..c1e73e2dd 100644 --- a/pkg/storage/driver/sql.go +++ b/pkg/storage/driver/sql.go @@ -22,7 +22,6 @@ import ( "maps" "sort" "strconv" - "sync/atomic" "time" "github.com/jmoiron/sqlx" @@ -33,6 +32,7 @@ import ( // Import pq for postgres dialect _ "github.com/lib/pq" + "helm.sh/helm/v4/internal/logging" "helm.sh/helm/v4/pkg/release" rspb "helm.sh/helm/v4/pkg/release/v1" ) @@ -90,8 +90,8 @@ type SQL struct { db *sqlx.DB namespace string statementBuilder sq.StatementBuilderType - // logger is an slog.Logger pointer to use the driver - logger atomic.Pointer[slog.Logger] + // Embed a LogHolder to provide logger functionality + logging.LogHolder } // Name returns the name of the driver. @@ -706,19 +706,3 @@ func getReleaseSystemLabels(rls *rspb.Release) map[string]string { "version": strconv.Itoa(rls.Version), } } - -// logger returns the logger for the SQL driver. If nil, returns slog.Default(). -func (s *SQL) Logger() *slog.Logger { - if lg := s.logger.Load(); lg != nil { - return lg - } - return slog.Default() // We rarely get here, just being defensive -} - -func (s *SQL) SetLogger(newLogger *slog.Logger) { - if newLogger == nil { - s.logger.Store(slog.New(slog.DiscardHandler)) // Assume nil as discarding logs - return - } - s.logger.Store(newLogger) -} diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index e55c628cf..d8b141dc9 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -21,7 +21,6 @@ import ( "fmt" "log/slog" "strings" - "sync/atomic" "helm.sh/helm/v4/internal/logging" "helm.sh/helm/v4/pkg/release" @@ -47,8 +46,8 @@ type Storage struct { // ignored (meaning no limits are imposed). MaxHistory int - // logger is an slog.Logger pointer to use the storage engine - logger atomic.Pointer[slog.Logger] + // Embed a LogHolder to provide logger functionality + logging.LogHolder } // Get retrieves the release from storage. An error is returned @@ -349,19 +348,3 @@ func Init(d driver.Driver) *Storage { } return s } - -// logger returns the logger for the Storage. If nil, returns slog.Default(). -func (s *Storage) Logger() *slog.Logger { - if lg := s.logger.Load(); lg != nil { - return lg - } - return slog.Default() // We rarely get here, just being defensive -} - -func (s *Storage) SetLogger(newLogger *slog.Logger) { - if newLogger == nil { - s.logger.Store(slog.New(slog.DiscardHandler)) // Assume nil as discarding logs - return - } - s.logger.Store(newLogger) -}