logs: make LoggingConfiguration an unversioned API
Making the LoggingConfiguration part of the versioned component-base/config API had the theoretic advantage that components could have offered different configuration APIs with experimental features limited to alpha versions (for example, sanitization offered only in a v1alpha1.KubeletConfiguration). Some components could have decided to only use stable logging options. In practice, this wasn't done. Furthermore, we don't want different components to make different choices regarding which logging features they offer to users. It should always be the same everywhere, for the sake of consistency. This can be achieved with a saner Go API by dropping the distinction between internal and external LoggingConfiguration types. Different stability levels of indidividual fields have to be covered by documentation (done) and potentially feature gates (not currently done). Advantages: - everything related to logging is under component-base/logs; previously this was scattered across different packages and different files under "logs" (why some code was in logs/config.go vs. logs/options.go vs. logs/logs.go always confused me again and again when coming back to the code): - long-term config and command line API are clearly separated into the "api" package underneath that - logs/logs.go itself only deals with legacy global flags and logging configuration - removal of separate Go APIs like logs.BindLoggingFlags and logs.Options - LogRegistry becomes an implementation detail, with less code and less exported functionality (only registration needs to be exported, querying is internal)
This commit is contained in:
parent
5fe00c14be
commit
1aceac797d
@ -58,6 +58,7 @@ import (
|
||||
cliflag "k8s.io/component-base/cli/flag"
|
||||
"k8s.io/component-base/cli/globalflag"
|
||||
"k8s.io/component-base/logs"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
_ "k8s.io/component-base/metrics/prometheus/workqueue" // for workqueue metric registration
|
||||
"k8s.io/component-base/term"
|
||||
"k8s.io/component-base/version"
|
||||
@ -82,7 +83,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(logs.AddFeatureGates(utilfeature.DefaultMutableFeatureGate))
|
||||
utilruntime.Must(logsapi.AddFeatureGates(utilfeature.DefaultMutableFeatureGate))
|
||||
}
|
||||
|
||||
// NewAPIServerCommand creates a *cobra.Command object with default parameters
|
||||
|
@ -55,6 +55,7 @@ import (
|
||||
"k8s.io/component-base/cli/globalflag"
|
||||
"k8s.io/component-base/configz"
|
||||
"k8s.io/component-base/logs"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
"k8s.io/component-base/term"
|
||||
"k8s.io/component-base/version"
|
||||
"k8s.io/component-base/version/verflag"
|
||||
@ -75,7 +76,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(logs.AddFeatureGates(utilfeature.DefaultMutableFeatureGate))
|
||||
utilruntime.Must(logsapi.AddFeatureGates(utilfeature.DefaultMutableFeatureGate))
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -46,6 +46,7 @@ import (
|
||||
"k8s.io/component-base/cli/globalflag"
|
||||
"k8s.io/component-base/configz"
|
||||
"k8s.io/component-base/logs"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
"k8s.io/component-base/metrics/legacyregistry"
|
||||
"k8s.io/component-base/term"
|
||||
"k8s.io/component-base/version"
|
||||
@ -62,7 +63,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(logs.AddFeatureGates(utilfeature.DefaultMutableFeatureGate))
|
||||
utilruntime.Must(logsapi.AddFeatureGates(utilfeature.DefaultMutableFeatureGate))
|
||||
}
|
||||
|
||||
// Option configures a framework.Registry.
|
||||
|
@ -28,7 +28,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
cliflag "k8s.io/component-base/cli/flag"
|
||||
"k8s.io/component-base/logs"
|
||||
"k8s.io/kubelet/config/v1beta1"
|
||||
kubeletapis "k8s.io/kubelet/pkg/apis"
|
||||
"k8s.io/kubernetes/pkg/cluster/ports"
|
||||
@ -500,7 +499,7 @@ func AddKubeletConfigFlags(mainfs *pflag.FlagSet, c *kubeletconfig.KubeletConfig
|
||||
fs.StringSliceVar(&c.EnforceNodeAllocatable, "enforce-node-allocatable", c.EnforceNodeAllocatable, "A comma separated list of levels of node allocatable enforcement to be enforced by kubelet. Acceptable options are 'none', 'pods', 'system-reserved', and 'kube-reserved'. If the latter two options are specified, '--system-reserved-cgroup' and '--kube-reserved-cgroup' must also be set, respectively. If 'none' is specified, no additional options should be set. See https://kubernetes.io/docs/tasks/administer-cluster/reserve-compute-resources/ for more details.")
|
||||
fs.StringVar(&c.SystemReservedCgroup, "system-reserved-cgroup", c.SystemReservedCgroup, "Absolute name of the top level cgroup that is used to manage non-kubernetes components for which compute resources were reserved via '--system-reserved' flag. Ex. '/system-reserved'. [default='']")
|
||||
fs.StringVar(&c.KubeReservedCgroup, "kube-reserved-cgroup", c.KubeReservedCgroup, "Absolute name of the top level cgroup that is used to manage kubernetes components for which compute resources were reserved via '--kube-reserved' flag. Ex. '/kube-reserved'. [default='']")
|
||||
logs.BindLoggingFlags(&c.Logging, fs)
|
||||
c.Logging.AddFlags(fs)
|
||||
|
||||
// Memory Manager Flags
|
||||
fs.StringVar(&c.MemoryManagerPolicy, "memory-manager-policy", c.MemoryManagerPolicy, "Memory Manager policy to use. Possible values: 'None', 'Static'.")
|
||||
|
@ -47,6 +47,7 @@ import (
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/apiserver/pkg/server/healthz"
|
||||
@ -65,6 +66,7 @@ import (
|
||||
"k8s.io/component-base/configz"
|
||||
"k8s.io/component-base/featuregate"
|
||||
"k8s.io/component-base/logs"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
"k8s.io/component-base/metrics"
|
||||
"k8s.io/component-base/metrics/legacyregistry"
|
||||
"k8s.io/component-base/version"
|
||||
@ -105,7 +107,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(logs.AddFeatureGates(utilfeature.DefaultMutableFeatureGate))
|
||||
utilruntime.Must(logsapi.AddFeatureGates(utilfeature.DefaultMutableFeatureGate))
|
||||
}
|
||||
|
||||
const (
|
||||
@ -213,9 +215,8 @@ HTTP server: The kubelet can also listen for HTTP and respond to a simple API
|
||||
|
||||
// Config and flags parsed, now we can initialize logging.
|
||||
logs.InitLogs()
|
||||
logOption := &logs.Options{Config: kubeletConfig.Logging}
|
||||
if err := logOption.ValidateAndApply(utilfeature.DefaultFeatureGate); err != nil {
|
||||
return fmt.Errorf("failed to initialize logging: %v", err)
|
||||
if err := kubeletConfig.Logging.ValidateAndApplyAsField(utilfeature.DefaultFeatureGate, field.NewPath("logging")); err != nil {
|
||||
return fmt.Errorf("initialize logging: %v", err)
|
||||
}
|
||||
cliflag.PrintFlags(cleanFlagSet)
|
||||
|
||||
|
4
pkg/generated/openapi/zz_generated.openapi.go
generated
4
pkg/generated/openapi/zz_generated.openapi.go
generated
@ -53339,7 +53339,7 @@ func schema_k8sio_kubelet_config_v1beta1_KubeletConfiguration(ref common.Referen
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "logging specifies the options of logging. Refer to [Logs Options](https://github.com/kubernetes/component-base/blob/master/logs/options.go) for more information. Default:\n Format: text",
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/component-base/config/v1alpha1.LoggingConfiguration"),
|
||||
Ref: ref("k8s.io/component-base/logs/api/v1.LoggingConfiguration"),
|
||||
},
|
||||
},
|
||||
"enableSystemLogHandler": {
|
||||
@ -53442,7 +53442,7 @@ func schema_k8sio_kubelet_config_v1beta1_KubeletConfiguration(ref common.Referen
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"k8s.io/api/core/v1.Taint", "k8s.io/apimachinery/pkg/apis/meta/v1.Duration", "k8s.io/component-base/config/v1alpha1.LoggingConfiguration", "k8s.io/kubelet/config/v1beta1.KubeletAuthentication", "k8s.io/kubelet/config/v1beta1.KubeletAuthorization", "k8s.io/kubelet/config/v1beta1.MemoryReservation", "k8s.io/kubelet/config/v1beta1.MemorySwapConfiguration", "k8s.io/kubelet/config/v1beta1.ShutdownGracePeriodByPodPriority"},
|
||||
"k8s.io/api/core/v1.Taint", "k8s.io/apimachinery/pkg/apis/meta/v1.Duration", "k8s.io/component-base/logs/api/v1.LoggingConfiguration", "k8s.io/kubelet/config/v1beta1.KubeletAuthentication", "k8s.io/kubelet/config/v1beta1.KubeletAuthorization", "k8s.io/kubelet/config/v1beta1.MemoryReservation", "k8s.io/kubelet/config/v1beta1.MemorySwapConfiguration", "k8s.io/kubelet/config/v1beta1.ShutdownGracePeriodByPodPriority"},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
componentconfigtesting "k8s.io/component-base/config/testing"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
)
|
||||
|
||||
func TestComponentConfigSetup(t *testing.T) {
|
||||
@ -32,10 +33,11 @@ func TestComponentConfigSetup(t *testing.T) {
|
||||
SchemeGroupVersion: SchemeGroupVersion,
|
||||
AddToScheme: AddToScheme,
|
||||
AllowedTags: map[reflect.Type]bool{
|
||||
reflect.TypeOf(metav1.TypeMeta{}): true,
|
||||
reflect.TypeOf(metav1.Duration{}): true,
|
||||
reflect.TypeOf(v1.NodeConfigSource{}): true,
|
||||
reflect.TypeOf(v1.Taint{}): true,
|
||||
reflect.TypeOf(logsapi.LoggingConfiguration{}): true,
|
||||
reflect.TypeOf(metav1.Duration{}): true,
|
||||
reflect.TypeOf(metav1.TypeMeta{}): true,
|
||||
reflect.TypeOf(v1.NodeConfigSource{}): true,
|
||||
reflect.TypeOf(v1.Taint{}): true,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ package config
|
||||
import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
componentbaseconfig "k8s.io/component-base/config"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
)
|
||||
|
||||
// HairpinMode denotes how the kubelet should configure networking to handle
|
||||
@ -384,7 +384,7 @@ type KubeletConfiguration struct {
|
||||
ShowHiddenMetricsForVersion string
|
||||
// Logging specifies the options of logging.
|
||||
// Refer [Logs Options](https://github.com/kubernetes/component-base/blob/master/logs/options.go) for more information.
|
||||
Logging componentbaseconfig.LoggingConfiguration
|
||||
Logging logsapi.LoggingConfiguration
|
||||
// EnableSystemLogHandler enables /logs handler.
|
||||
EnableSystemLogHandler bool
|
||||
// ShutdownGracePeriod specifies the total duration that the node should delay the shutdown and total grace period for pod termination during a node shutdown.
|
||||
|
@ -21,7 +21,6 @@ import (
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
kruntime "k8s.io/apimachinery/pkg/runtime"
|
||||
componentbaseconfigv1alpha1 "k8s.io/component-base/config/v1alpha1"
|
||||
kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1"
|
||||
|
||||
// TODO: Cut references to k8s.io/kubernetes, eventually there should be none from this package
|
||||
@ -245,7 +244,7 @@ func SetDefaults_KubeletConfiguration(obj *kubeletconfigv1beta1.KubeletConfigura
|
||||
obj.VolumePluginDir = DefaultVolumePluginDir
|
||||
}
|
||||
// Use the Default LoggingConfiguration option
|
||||
componentbaseconfigv1alpha1.RecommendedLoggingConfiguration(&obj.Logging)
|
||||
obj.Logging.SetRecommendedLoggingConfiguration()
|
||||
if obj.EnableSystemLogHandler == nil {
|
||||
obj.EnableSystemLogHandler = utilpointer.BoolPtr(true)
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
componentbaseconfigv1alpha1 "k8s.io/component-base/config/v1alpha1"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
"k8s.io/kubelet/config/v1beta1"
|
||||
"k8s.io/kubernetes/pkg/cluster/ports"
|
||||
"k8s.io/kubernetes/pkg/kubelet/qos"
|
||||
@ -111,7 +111,7 @@ func TestSetDefaultsKubeletConfiguration(t *testing.T) {
|
||||
ConfigMapAndSecretChangeDetectionStrategy: v1beta1.WatchChangeDetectionStrategy,
|
||||
EnforceNodeAllocatable: DefaultNodeAllocatableEnforcement,
|
||||
VolumePluginDir: DefaultVolumePluginDir,
|
||||
Logging: componentbaseconfigv1alpha1.LoggingConfiguration{
|
||||
Logging: logsapi.LoggingConfiguration{
|
||||
Format: "text",
|
||||
FlushFrequency: 5 * time.Second,
|
||||
},
|
||||
@ -232,7 +232,7 @@ func TestSetDefaultsKubeletConfiguration(t *testing.T) {
|
||||
VolumePluginDir: "",
|
||||
ProviderID: "",
|
||||
KernelMemcgNotification: false,
|
||||
Logging: componentbaseconfigv1alpha1.LoggingConfiguration{
|
||||
Logging: logsapi.LoggingConfiguration{
|
||||
Format: "",
|
||||
FlushFrequency: 5 * time.Second,
|
||||
},
|
||||
@ -329,7 +329,7 @@ func TestSetDefaultsKubeletConfiguration(t *testing.T) {
|
||||
EnforceNodeAllocatable: []string{},
|
||||
AllowedUnsafeSysctls: []string{},
|
||||
VolumePluginDir: DefaultVolumePluginDir,
|
||||
Logging: componentbaseconfigv1alpha1.LoggingConfiguration{
|
||||
Logging: logsapi.LoggingConfiguration{
|
||||
Format: "text",
|
||||
FlushFrequency: 5 * time.Second,
|
||||
},
|
||||
@ -468,7 +468,7 @@ func TestSetDefaultsKubeletConfiguration(t *testing.T) {
|
||||
VolumePluginDir: "volume-plugin-dir",
|
||||
ProviderID: "provider-id",
|
||||
KernelMemcgNotification: true,
|
||||
Logging: componentbaseconfigv1alpha1.LoggingConfiguration{
|
||||
Logging: logsapi.LoggingConfiguration{
|
||||
Format: "json",
|
||||
FlushFrequency: 5 * time.Second,
|
||||
},
|
||||
@ -611,7 +611,7 @@ func TestSetDefaultsKubeletConfiguration(t *testing.T) {
|
||||
VolumePluginDir: "volume-plugin-dir",
|
||||
ProviderID: "provider-id",
|
||||
KernelMemcgNotification: true,
|
||||
Logging: componentbaseconfigv1alpha1.LoggingConfiguration{
|
||||
Logging: logsapi.LoggingConfiguration{
|
||||
Format: "json",
|
||||
FlushFrequency: 5 * time.Second,
|
||||
},
|
||||
@ -705,7 +705,7 @@ func TestSetDefaultsKubeletConfiguration(t *testing.T) {
|
||||
ConfigMapAndSecretChangeDetectionStrategy: v1beta1.WatchChangeDetectionStrategy,
|
||||
EnforceNodeAllocatable: DefaultNodeAllocatableEnforcement,
|
||||
VolumePluginDir: DefaultVolumePluginDir,
|
||||
Logging: componentbaseconfigv1alpha1.LoggingConfiguration{
|
||||
Logging: logsapi.LoggingConfiguration{
|
||||
Format: "text",
|
||||
FlushFrequency: 5 * time.Second,
|
||||
},
|
||||
|
@ -28,7 +28,6 @@ import (
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
v1alpha1 "k8s.io/component-base/config/v1alpha1"
|
||||
v1beta1 "k8s.io/kubelet/config/v1beta1"
|
||||
config "k8s.io/kubernetes/pkg/kubelet/apis/config"
|
||||
)
|
||||
@ -485,9 +484,7 @@ func autoConvert_v1beta1_KubeletConfiguration_To_config_KubeletConfiguration(in
|
||||
out.VolumePluginDir = in.VolumePluginDir
|
||||
out.ProviderID = in.ProviderID
|
||||
out.KernelMemcgNotification = in.KernelMemcgNotification
|
||||
if err := v1alpha1.Convert_v1alpha1_LoggingConfiguration_To_config_LoggingConfiguration(&in.Logging, &out.Logging, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Logging = in.Logging
|
||||
if err := v1.Convert_Pointer_bool_To_bool(&in.EnableSystemLogHandler, &out.EnableSystemLogHandler, s); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -661,9 +658,7 @@ func autoConvert_config_KubeletConfiguration_To_v1beta1_KubeletConfiguration(in
|
||||
out.EnforceNodeAllocatable = *(*[]string)(unsafe.Pointer(&in.EnforceNodeAllocatable))
|
||||
out.ReservedSystemCPUs = in.ReservedSystemCPUs
|
||||
out.ShowHiddenMetricsForVersion = in.ShowHiddenMetricsForVersion
|
||||
if err := v1alpha1.Convert_config_LoggingConfiguration_To_v1alpha1_LoggingConfiguration(&in.Logging, &out.Logging, s); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Logging = in.Logging
|
||||
if err := v1.Convert_bool_To_Pointer_bool(&in.EnableSystemLogHandler, &out.EnableSystemLogHandler, s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ import (
|
||||
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/component-base/logs"
|
||||
"k8s.io/component-base/metrics"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
|
||||
@ -237,7 +236,7 @@ func ValidateKubeletConfiguration(kc *kubeletconfig.KubeletConfiguration) error
|
||||
}
|
||||
allErrors = append(allErrors, metrics.ValidateShowHiddenMetricsVersion(kc.ShowHiddenMetricsForVersion)...)
|
||||
|
||||
if errs := logs.ValidateLoggingConfiguration(&kc.Logging, field.NewPath("logging")); len(errs) > 0 {
|
||||
if errs := kc.Logging.Validate(localFeatureGate, field.NewPath("logging")); len(errs) > 0 {
|
||||
allErrors = append(allErrors, errs.ToAggregate().Errors()...)
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ import (
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
componentbaseconfig "k8s.io/component-base/config"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
|
||||
"k8s.io/kubernetes/pkg/kubelet/apis/config/validation"
|
||||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
@ -68,7 +68,7 @@ var (
|
||||
"GracefulNodeShutdown": true,
|
||||
"MemoryQoS": true,
|
||||
},
|
||||
Logging: componentbaseconfig.LoggingConfiguration{
|
||||
Logging: logsapi.LoggingConfiguration{
|
||||
Format: "text",
|
||||
},
|
||||
}
|
||||
|
@ -17,14 +17,6 @@ limitations under the License.
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
@ -86,127 +78,3 @@ type DebuggingConfiguration struct {
|
||||
// enableProfiling is true.
|
||||
EnableContentionProfiling bool
|
||||
}
|
||||
|
||||
// LoggingConfiguration contains logging options
|
||||
// Refer [Logs Options](https://github.com/kubernetes/component-base/blob/master/logs/options.go) for more information.
|
||||
type LoggingConfiguration struct {
|
||||
// Format Flag specifies the structure of log messages.
|
||||
// default value of format is `text`
|
||||
Format string
|
||||
// Maximum number of nanoseconds (i.e. 1s = 1000000000) between log
|
||||
// flushes. Ignored if the selected logging backend writes log
|
||||
// messages without buffering.
|
||||
FlushFrequency time.Duration
|
||||
// Verbosity is the threshold that determines which log messages are
|
||||
// logged. Default is zero which logs only the most important
|
||||
// messages. Higher values enable additional messages. Error messages
|
||||
// are always logged.
|
||||
Verbosity VerbosityLevel
|
||||
// VModule overrides the verbosity threshold for individual files.
|
||||
// Only supported for "text" log format.
|
||||
VModule VModuleConfiguration
|
||||
// [Experimental] Options holds additional parameters that are specific
|
||||
// to the different logging formats. Only the options for the selected
|
||||
// format get used, but all of them get validated.
|
||||
Options FormatOptions
|
||||
}
|
||||
|
||||
// FormatOptions contains options for the different logging formats.
|
||||
type FormatOptions struct {
|
||||
// [Experimental] JSON contains options for logging format "json".
|
||||
JSON JSONOptions
|
||||
}
|
||||
|
||||
// JSONOptions contains options for logging format "json".
|
||||
type JSONOptions struct {
|
||||
// [Experimental] SplitStream redirects error messages to stderr while
|
||||
// info messages go to stdout, with buffering. The default is to write
|
||||
// both to stdout, without buffering.
|
||||
SplitStream bool
|
||||
// [Experimental] InfoBufferSize sets the size of the info stream when
|
||||
// using split streams. The default is zero, which disables buffering.
|
||||
InfoBufferSize resource.QuantityValue
|
||||
}
|
||||
|
||||
// VModuleConfiguration is a collection of individual file names or patterns
|
||||
// and the corresponding verbosity threshold.
|
||||
type VModuleConfiguration []VModuleItem
|
||||
|
||||
var _ pflag.Value = &VModuleConfiguration{}
|
||||
|
||||
// VModuleItem defines verbosity for one or more files which match a certain
|
||||
// glob pattern.
|
||||
type VModuleItem struct {
|
||||
// FilePattern is a base file name (i.e. minus the ".go" suffix and
|
||||
// directory) or a "glob" pattern for such a name. It must not contain
|
||||
// comma and equal signs because those are separators for the
|
||||
// corresponding klog command line argument.
|
||||
FilePattern string
|
||||
// Verbosity is the threshold for log messages emitted inside files
|
||||
// that match the pattern.
|
||||
Verbosity VerbosityLevel
|
||||
}
|
||||
|
||||
// String returns the -vmodule parameter (comma-separated list of pattern=N).
|
||||
func (vmodule *VModuleConfiguration) String() string {
|
||||
var patterns []string
|
||||
for _, item := range *vmodule {
|
||||
patterns = append(patterns, fmt.Sprintf("%s=%d", item.FilePattern, item.Verbosity))
|
||||
}
|
||||
return strings.Join(patterns, ",")
|
||||
}
|
||||
|
||||
// Set parses the -vmodule parameter (comma-separated list of pattern=N).
|
||||
func (vmodule *VModuleConfiguration) Set(value string) error {
|
||||
// This code mirrors https://github.com/kubernetes/klog/blob/9ad246211af1ed84621ee94a26fcce0038b69cd1/klog.go#L287-L313
|
||||
|
||||
for _, pat := range strings.Split(value, ",") {
|
||||
if len(pat) == 0 {
|
||||
// Empty strings such as from a trailing comma can be ignored.
|
||||
continue
|
||||
}
|
||||
patLev := strings.Split(pat, "=")
|
||||
if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 {
|
||||
return fmt.Errorf("%q does not have the pattern=N format", pat)
|
||||
}
|
||||
pattern := patLev[0]
|
||||
// 31 instead of 32 to ensure that it also fits into int32.
|
||||
v, err := strconv.ParseUint(patLev[1], 10, 31)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing verbosity in %q: %v", pat, err)
|
||||
}
|
||||
*vmodule = append(*vmodule, VModuleItem{FilePattern: pattern, Verbosity: VerbosityLevel(v)})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vmodule *VModuleConfiguration) Type() string {
|
||||
return "pattern=N,..."
|
||||
}
|
||||
|
||||
// VerbosityLevel represents a klog or logr verbosity threshold.
|
||||
type VerbosityLevel uint32
|
||||
|
||||
var _ pflag.Value = new(VerbosityLevel)
|
||||
|
||||
func (l *VerbosityLevel) String() string {
|
||||
return strconv.FormatInt(int64(*l), 10)
|
||||
}
|
||||
|
||||
func (l *VerbosityLevel) Get() interface{} {
|
||||
return *l
|
||||
}
|
||||
|
||||
func (l *VerbosityLevel) Set(value string) error {
|
||||
// Limited to int32 for compatibility with klog.
|
||||
v, err := strconv.ParseUint(value, 10, 31)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*l = VerbosityLevel(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *VerbosityLevel) Type() string {
|
||||
return "Level"
|
||||
}
|
||||
|
@ -1,119 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestVModule(t *testing.T) {
|
||||
testcases := []struct {
|
||||
arg string
|
||||
expectError string
|
||||
expectValue VModuleConfiguration
|
||||
expectParam string
|
||||
}{
|
||||
{
|
||||
arg: "gopher*=1",
|
||||
expectValue: VModuleConfiguration{
|
||||
{
|
||||
FilePattern: "gopher*",
|
||||
Verbosity: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
arg: "foo=1,bar=2",
|
||||
expectValue: VModuleConfiguration{
|
||||
{
|
||||
FilePattern: "foo",
|
||||
Verbosity: 1,
|
||||
},
|
||||
{
|
||||
FilePattern: "bar",
|
||||
Verbosity: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
arg: "foo=1,bar=2,",
|
||||
expectValue: VModuleConfiguration{
|
||||
{
|
||||
FilePattern: "foo",
|
||||
Verbosity: 1,
|
||||
},
|
||||
{
|
||||
FilePattern: "bar",
|
||||
Verbosity: 2,
|
||||
},
|
||||
},
|
||||
expectParam: "foo=1,bar=2",
|
||||
},
|
||||
{
|
||||
arg: "gopher*",
|
||||
expectError: `"gopher*" does not have the pattern=N format`,
|
||||
},
|
||||
{
|
||||
arg: "=1",
|
||||
expectError: `"=1" does not have the pattern=N format`,
|
||||
},
|
||||
{
|
||||
arg: "foo=-1",
|
||||
expectError: `parsing verbosity in "foo=-1": strconv.ParseUint: parsing "-1": invalid syntax`,
|
||||
},
|
||||
{
|
||||
arg: fmt.Sprintf("validint32=%d", math.MaxInt32),
|
||||
expectValue: VModuleConfiguration{
|
||||
{
|
||||
FilePattern: "validint32",
|
||||
Verbosity: math.MaxInt32,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
arg: fmt.Sprintf("invalidint32=%d", math.MaxInt32+1),
|
||||
expectError: `parsing verbosity in "invalidint32=2147483648": strconv.ParseUint: parsing "2147483648": value out of range`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testcases {
|
||||
t.Run(test.arg, func(t *testing.T) {
|
||||
var actual VModuleConfiguration
|
||||
err := actual.Set(test.arg)
|
||||
if test.expectError != "" {
|
||||
if err == nil {
|
||||
t.Fatal("parsing should have failed")
|
||||
}
|
||||
assert.Equal(t, test.expectError, err.Error(), "parse error")
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
param := actual.String()
|
||||
expectParam := test.expectParam
|
||||
if expectParam == "" {
|
||||
expectParam = test.arg
|
||||
}
|
||||
assert.Equal(t, expectParam, param, "encoded parameter value not identical")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -51,11 +51,3 @@ func Convert_v1alpha1_LeaderElectionConfiguration_To_config_LeaderElectionConfig
|
||||
func Convert_config_LeaderElectionConfiguration_To_v1alpha1_LeaderElectionConfiguration(in *config.LeaderElectionConfiguration, out *LeaderElectionConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_config_LeaderElectionConfiguration_To_v1alpha1_LeaderElectionConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func Convert_v1alpha1_LoggingConfiguration_To_config_LoggingConfiguration(in *LoggingConfiguration, out *config.LoggingConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_LoggingConfiguration_To_config_LoggingConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func Convert_config_LoggingConfiguration_To_v1alpha1_LoggingConfiguration(in *config.LoggingConfiguration, out *LoggingConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_config_LoggingConfiguration_To_v1alpha1_LoggingConfiguration(in, out, s)
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ package v1alpha1
|
||||
import (
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
)
|
||||
@ -97,32 +96,3 @@ func NewRecommendedDebuggingConfiguration() *DebuggingConfiguration {
|
||||
RecommendedDebuggingConfiguration(ret)
|
||||
return ret
|
||||
}
|
||||
|
||||
// RecommendedLoggingConfiguration defaults logging configuration.
|
||||
// This will set the recommended default
|
||||
// values, but they may be subject to change between API versions. This function
|
||||
// is intentionally not registered in the scheme as a "normal" `SetDefaults_Foo`
|
||||
// function to allow consumers of this type to set whatever defaults for their
|
||||
// embedded configs. Forcing consumers to use these defaults would be problematic
|
||||
// as defaulting in the scheme is done as part of the conversion, and there would
|
||||
// be no easy way to opt-out. Instead, if you want to use this defaulting method
|
||||
// run it in your wrapper struct of this type in its `SetDefaults_` method.
|
||||
func RecommendedLoggingConfiguration(obj *LoggingConfiguration) {
|
||||
if obj.Format == "" {
|
||||
obj.Format = "text"
|
||||
}
|
||||
var empty resource.QuantityValue
|
||||
if obj.Options.JSON.InfoBufferSize == empty {
|
||||
obj.Options.JSON.InfoBufferSize = resource.QuantityValue{
|
||||
// This is similar, but not quite the same as a default
|
||||
// constructed instance.
|
||||
Quantity: *resource.NewQuantity(0, resource.DecimalSI),
|
||||
}
|
||||
// This sets the unexported Quantity.s which will be compared
|
||||
// by reflect.DeepEqual in some tests.
|
||||
_ = obj.Options.JSON.InfoBufferSize.String()
|
||||
}
|
||||
if obj.FlushFrequency == 0 {
|
||||
obj.FlushFrequency = 5 * time.Second
|
||||
}
|
||||
}
|
||||
|
@ -17,9 +17,6 @@ limitations under the License.
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
@ -83,61 +80,3 @@ type ClientConnectionConfiguration struct {
|
||||
// burst allows extra queries to accumulate when a client is exceeding its rate.
|
||||
Burst int32 `json:"burst"`
|
||||
}
|
||||
|
||||
// LoggingConfiguration contains logging options
|
||||
// Refer [Logs Options](https://github.com/kubernetes/component-base/blob/master/logs/options.go) for more information.
|
||||
type LoggingConfiguration struct {
|
||||
// Format Flag specifies the structure of log messages.
|
||||
// default value of format is `text`
|
||||
Format string `json:"format,omitempty"`
|
||||
// Maximum number of nanoseconds (i.e. 1s = 1000000000) between log
|
||||
// flushes. Ignored if the selected logging backend writes log
|
||||
// messages without buffering.
|
||||
FlushFrequency time.Duration `json:"flushFrequency"`
|
||||
// Verbosity is the threshold that determines which log messages are
|
||||
// logged. Default is zero which logs only the most important
|
||||
// messages. Higher values enable additional messages. Error messages
|
||||
// are always logged.
|
||||
Verbosity uint32 `json:"verbosity"`
|
||||
// VModule overrides the verbosity threshold for individual files.
|
||||
// Only supported for "text" log format.
|
||||
VModule VModuleConfiguration `json:"vmodule,omitempty"`
|
||||
// [Experimental] Options holds additional parameters that are specific
|
||||
// to the different logging formats. Only the options for the selected
|
||||
// format get used, but all of them get validated.
|
||||
Options FormatOptions `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
// FormatOptions contains options for the different logging formats.
|
||||
type FormatOptions struct {
|
||||
// [Experimental] JSON contains options for logging format "json".
|
||||
JSON JSONOptions `json:"json,omitempty"`
|
||||
}
|
||||
|
||||
// JSONOptions contains options for logging format "json".
|
||||
type JSONOptions struct {
|
||||
// [Experimental] SplitStream redirects error messages to stderr while
|
||||
// info messages go to stdout, with buffering. The default is to write
|
||||
// both to stdout, without buffering.
|
||||
SplitStream bool `json:"splitStream,omitempty"`
|
||||
// [Experimental] InfoBufferSize sets the size of the info stream when
|
||||
// using split streams. The default is zero, which disables buffering.
|
||||
InfoBufferSize resource.QuantityValue `json:"infoBufferSize,omitempty"`
|
||||
}
|
||||
|
||||
// VModuleConfiguration is a collection of individual file names or patterns
|
||||
// and the corresponding verbosity threshold.
|
||||
type VModuleConfiguration []VModuleItem
|
||||
|
||||
// VModuleItem defines verbosity for one or more files which match a certain
|
||||
// glob pattern.
|
||||
type VModuleItem struct {
|
||||
// FilePattern is a base file name (i.e. minus the ".go" suffix and
|
||||
// directory) or a "glob" pattern for such a name. It must not contain
|
||||
// comma and equal signs because those are separators for the
|
||||
// corresponding klog command line argument.
|
||||
FilePattern string `json:"filePattern"`
|
||||
// Verbosity is the threshold for log messages emitted inside files
|
||||
// that match the pattern.
|
||||
Verbosity uint32 `json:"verbosity"`
|
||||
}
|
||||
|
@ -22,9 +22,6 @@ limitations under the License.
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
time "time"
|
||||
unsafe "unsafe"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
@ -38,36 +35,6 @@ func init() {
|
||||
// RegisterConversions adds conversion functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
func RegisterConversions(s *runtime.Scheme) error {
|
||||
if err := s.AddGeneratedConversionFunc((*FormatOptions)(nil), (*config.FormatOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_FormatOptions_To_config_FormatOptions(a.(*FormatOptions), b.(*config.FormatOptions), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*config.FormatOptions)(nil), (*FormatOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_config_FormatOptions_To_v1alpha1_FormatOptions(a.(*config.FormatOptions), b.(*FormatOptions), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*JSONOptions)(nil), (*config.JSONOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_JSONOptions_To_config_JSONOptions(a.(*JSONOptions), b.(*config.JSONOptions), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*config.JSONOptions)(nil), (*JSONOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_config_JSONOptions_To_v1alpha1_JSONOptions(a.(*config.JSONOptions), b.(*JSONOptions), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*VModuleItem)(nil), (*config.VModuleItem)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_VModuleItem_To_config_VModuleItem(a.(*VModuleItem), b.(*config.VModuleItem), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*config.VModuleItem)(nil), (*VModuleItem)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_config_VModuleItem_To_v1alpha1_VModuleItem(a.(*config.VModuleItem), b.(*VModuleItem), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddConversionFunc((*config.ClientConnectionConfiguration)(nil), (*ClientConnectionConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_config_ClientConnectionConfiguration_To_v1alpha1_ClientConnectionConfiguration(a.(*config.ClientConnectionConfiguration), b.(*ClientConnectionConfiguration), scope)
|
||||
}); err != nil {
|
||||
@ -83,11 +50,6 @@ func RegisterConversions(s *runtime.Scheme) error {
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddConversionFunc((*config.LoggingConfiguration)(nil), (*LoggingConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_config_LoggingConfiguration_To_v1alpha1_LoggingConfiguration(a.(*config.LoggingConfiguration), b.(*LoggingConfiguration), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddConversionFunc((*ClientConnectionConfiguration)(nil), (*config.ClientConnectionConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_ClientConnectionConfiguration_To_config_ClientConnectionConfiguration(a.(*ClientConnectionConfiguration), b.(*config.ClientConnectionConfiguration), scope)
|
||||
}); err != nil {
|
||||
@ -103,11 +65,6 @@ func RegisterConversions(s *runtime.Scheme) error {
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddConversionFunc((*LoggingConfiguration)(nil), (*config.LoggingConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_LoggingConfiguration_To_config_LoggingConfiguration(a.(*LoggingConfiguration), b.(*config.LoggingConfiguration), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -149,52 +106,6 @@ func autoConvert_config_DebuggingConfiguration_To_v1alpha1_DebuggingConfiguratio
|
||||
return nil
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_FormatOptions_To_config_FormatOptions(in *FormatOptions, out *config.FormatOptions, s conversion.Scope) error {
|
||||
if err := Convert_v1alpha1_JSONOptions_To_config_JSONOptions(&in.JSON, &out.JSON, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_FormatOptions_To_config_FormatOptions is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_FormatOptions_To_config_FormatOptions(in *FormatOptions, out *config.FormatOptions, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_FormatOptions_To_config_FormatOptions(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_config_FormatOptions_To_v1alpha1_FormatOptions(in *config.FormatOptions, out *FormatOptions, s conversion.Scope) error {
|
||||
if err := Convert_config_JSONOptions_To_v1alpha1_JSONOptions(&in.JSON, &out.JSON, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_config_FormatOptions_To_v1alpha1_FormatOptions is an autogenerated conversion function.
|
||||
func Convert_config_FormatOptions_To_v1alpha1_FormatOptions(in *config.FormatOptions, out *FormatOptions, s conversion.Scope) error {
|
||||
return autoConvert_config_FormatOptions_To_v1alpha1_FormatOptions(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_JSONOptions_To_config_JSONOptions(in *JSONOptions, out *config.JSONOptions, s conversion.Scope) error {
|
||||
out.SplitStream = in.SplitStream
|
||||
out.InfoBufferSize = in.InfoBufferSize
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_JSONOptions_To_config_JSONOptions is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_JSONOptions_To_config_JSONOptions(in *JSONOptions, out *config.JSONOptions, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_JSONOptions_To_config_JSONOptions(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_config_JSONOptions_To_v1alpha1_JSONOptions(in *config.JSONOptions, out *JSONOptions, s conversion.Scope) error {
|
||||
out.SplitStream = in.SplitStream
|
||||
out.InfoBufferSize = in.InfoBufferSize
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_config_JSONOptions_To_v1alpha1_JSONOptions is an autogenerated conversion function.
|
||||
func Convert_config_JSONOptions_To_v1alpha1_JSONOptions(in *config.JSONOptions, out *JSONOptions, s conversion.Scope) error {
|
||||
return autoConvert_config_JSONOptions_To_v1alpha1_JSONOptions(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_LeaderElectionConfiguration_To_config_LeaderElectionConfiguration(in *LeaderElectionConfiguration, out *config.LeaderElectionConfiguration, s conversion.Scope) error {
|
||||
if err := v1.Convert_Pointer_bool_To_bool(&in.LeaderElect, &out.LeaderElect, s); err != nil {
|
||||
return err
|
||||
@ -220,47 +131,3 @@ func autoConvert_config_LeaderElectionConfiguration_To_v1alpha1_LeaderElectionCo
|
||||
out.ResourceNamespace = in.ResourceNamespace
|
||||
return nil
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_LoggingConfiguration_To_config_LoggingConfiguration(in *LoggingConfiguration, out *config.LoggingConfiguration, s conversion.Scope) error {
|
||||
out.Format = in.Format
|
||||
out.FlushFrequency = time.Duration(in.FlushFrequency)
|
||||
out.Verbosity = config.VerbosityLevel(in.Verbosity)
|
||||
out.VModule = *(*config.VModuleConfiguration)(unsafe.Pointer(&in.VModule))
|
||||
if err := Convert_v1alpha1_FormatOptions_To_config_FormatOptions(&in.Options, &out.Options, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func autoConvert_config_LoggingConfiguration_To_v1alpha1_LoggingConfiguration(in *config.LoggingConfiguration, out *LoggingConfiguration, s conversion.Scope) error {
|
||||
out.Format = in.Format
|
||||
out.FlushFrequency = time.Duration(in.FlushFrequency)
|
||||
out.Verbosity = uint32(in.Verbosity)
|
||||
out.VModule = *(*VModuleConfiguration)(unsafe.Pointer(&in.VModule))
|
||||
if err := Convert_config_FormatOptions_To_v1alpha1_FormatOptions(&in.Options, &out.Options, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_VModuleItem_To_config_VModuleItem(in *VModuleItem, out *config.VModuleItem, s conversion.Scope) error {
|
||||
out.FilePattern = in.FilePattern
|
||||
out.Verbosity = config.VerbosityLevel(in.Verbosity)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_VModuleItem_To_config_VModuleItem is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_VModuleItem_To_config_VModuleItem(in *VModuleItem, out *config.VModuleItem, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_VModuleItem_To_config_VModuleItem(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_config_VModuleItem_To_v1alpha1_VModuleItem(in *config.VModuleItem, out *VModuleItem, s conversion.Scope) error {
|
||||
out.FilePattern = in.FilePattern
|
||||
out.Verbosity = uint32(in.Verbosity)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_config_VModuleItem_To_v1alpha1_VModuleItem is an autogenerated conversion function.
|
||||
func Convert_config_VModuleItem_To_v1alpha1_VModuleItem(in *config.VModuleItem, out *VModuleItem, s conversion.Scope) error {
|
||||
return autoConvert_config_VModuleItem_To_v1alpha1_VModuleItem(in, out, s)
|
||||
}
|
||||
|
@ -63,40 +63,6 @@ func (in *DebuggingConfiguration) DeepCopy() *DebuggingConfiguration {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FormatOptions) DeepCopyInto(out *FormatOptions) {
|
||||
*out = *in
|
||||
in.JSON.DeepCopyInto(&out.JSON)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FormatOptions.
|
||||
func (in *FormatOptions) DeepCopy() *FormatOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FormatOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *JSONOptions) DeepCopyInto(out *JSONOptions) {
|
||||
*out = *in
|
||||
in.InfoBufferSize.DeepCopyInto(&out.InfoBufferSize)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONOptions.
|
||||
func (in *JSONOptions) DeepCopy() *JSONOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(JSONOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *LeaderElectionConfiguration) DeepCopyInto(out *LeaderElectionConfiguration) {
|
||||
*out = *in
|
||||
@ -120,61 +86,3 @@ func (in *LeaderElectionConfiguration) DeepCopy() *LeaderElectionConfiguration {
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *LoggingConfiguration) DeepCopyInto(out *LoggingConfiguration) {
|
||||
*out = *in
|
||||
if in.VModule != nil {
|
||||
in, out := &in.VModule, &out.VModule
|
||||
*out = make(VModuleConfiguration, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
in.Options.DeepCopyInto(&out.Options)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoggingConfiguration.
|
||||
func (in *LoggingConfiguration) DeepCopy() *LoggingConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(LoggingConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in VModuleConfiguration) DeepCopyInto(out *VModuleConfiguration) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(VModuleConfiguration, len(*in))
|
||||
copy(*out, *in)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VModuleConfiguration.
|
||||
func (in VModuleConfiguration) DeepCopy() VModuleConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(VModuleConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *VModuleItem) DeepCopyInto(out *VModuleItem) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VModuleItem.
|
||||
func (in *VModuleItem) DeepCopy() *VModuleItem {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(VModuleItem)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
@ -53,40 +53,6 @@ func (in *DebuggingConfiguration) DeepCopy() *DebuggingConfiguration {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FormatOptions) DeepCopyInto(out *FormatOptions) {
|
||||
*out = *in
|
||||
in.JSON.DeepCopyInto(&out.JSON)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FormatOptions.
|
||||
func (in *FormatOptions) DeepCopy() *FormatOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FormatOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *JSONOptions) DeepCopyInto(out *JSONOptions) {
|
||||
*out = *in
|
||||
in.InfoBufferSize.DeepCopyInto(&out.InfoBufferSize)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONOptions.
|
||||
func (in *JSONOptions) DeepCopy() *JSONOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(JSONOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *LeaderElectionConfiguration) DeepCopyInto(out *LeaderElectionConfiguration) {
|
||||
*out = *in
|
||||
@ -105,61 +71,3 @@ func (in *LeaderElectionConfiguration) DeepCopy() *LeaderElectionConfiguration {
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *LoggingConfiguration) DeepCopyInto(out *LoggingConfiguration) {
|
||||
*out = *in
|
||||
if in.VModule != nil {
|
||||
in, out := &in.VModule, &out.VModule
|
||||
*out = make(VModuleConfiguration, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
in.Options.DeepCopyInto(&out.Options)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoggingConfiguration.
|
||||
func (in *LoggingConfiguration) DeepCopy() *LoggingConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(LoggingConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in VModuleConfiguration) DeepCopyInto(out *VModuleConfiguration) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(VModuleConfiguration, len(*in))
|
||||
copy(*out, *in)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VModuleConfiguration.
|
||||
func (in VModuleConfiguration) DeepCopy() VModuleConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(VModuleConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *VModuleItem) DeepCopyInto(out *VModuleItem) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VModuleItem.
|
||||
func (in *VModuleItem) DeepCopy() *VModuleItem {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(VModuleItem)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ require (
|
||||
k8s.io/client-go v0.0.0
|
||||
k8s.io/klog/v2 v2.60.1
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
|
||||
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2
|
||||
)
|
||||
|
||||
require (
|
||||
@ -81,7 +82,6 @@ require (
|
||||
gotest.tools/v3 v3.0.3 // indirect
|
||||
k8s.io/api v0.0.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20220603121420-31174f50af60 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
|
||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||
)
|
||||
|
9
staging/src/k8s.io/component-base/logs/api/OWNERS
Normal file
9
staging/src/k8s.io/component-base/logs/api/OWNERS
Normal file
@ -0,0 +1,9 @@
|
||||
# Disable inheritance as this is an api owners file
|
||||
options:
|
||||
no_parent_owners: true
|
||||
approvers:
|
||||
- api-approvers
|
||||
reviewers:
|
||||
- api-reviewers
|
||||
labels:
|
||||
- kind/api-change
|
32
staging/src/k8s.io/component-base/logs/api/v1/doc.go
Normal file
32
staging/src/k8s.io/component-base/logs/api/v1/doc.go
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright 2022 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
|
||||
// Package v1 contains the configuration API for logging.
|
||||
//
|
||||
// The intention is to only have a single version of this API, potentially with
|
||||
// new fields added over time in a backwards-compatible manner. Fields for
|
||||
// alpha or beta features are allowed as long as they are defined so that not
|
||||
// changing the defaults leaves those features disabled.
|
||||
//
|
||||
// The "v1" package name is just a reminder that API compatibility rules apply,
|
||||
// not an indication of the stability of all features covered by it.
|
||||
|
||||
// The LoggingAlphaOptions and LoggingBetaOptions feature gates control whether
|
||||
// these unstable features can get enabled. This can be used to ensure that
|
||||
// command invocations do not accidentally rely on unstable features.
|
||||
package v1 // import "k8s.io/component-base/logs/api/v1"
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package logs
|
||||
package v1
|
||||
|
||||
import (
|
||||
"k8s.io/component-base/featuregate"
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
Copyright 2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -14,33 +14,44 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package logs
|
||||
package v1
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/component-base/config"
|
||||
"k8s.io/component-base/config/v1alpha1"
|
||||
"k8s.io/component-base/featuregate"
|
||||
"k8s.io/component-base/logs/registry"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
cliflag "k8s.io/component-base/cli/flag"
|
||||
"k8s.io/component-base/featuregate"
|
||||
)
|
||||
|
||||
// Options has klog format parameters
|
||||
type Options struct {
|
||||
Config config.LoggingConfiguration
|
||||
}
|
||||
const (
|
||||
// LogFlushFreqDefault is the default for the corresponding command line
|
||||
// parameter.
|
||||
LogFlushFreqDefault = 5 * time.Second
|
||||
)
|
||||
|
||||
// NewOptions return new klog options
|
||||
func NewOptions() *Options {
|
||||
c := v1alpha1.LoggingConfiguration{}
|
||||
v1alpha1.RecommendedLoggingConfiguration(&c)
|
||||
o := &Options{}
|
||||
v1alpha1.Convert_v1alpha1_LoggingConfiguration_To_config_LoggingConfiguration(&c, &o.Config, nil)
|
||||
return o
|
||||
const (
|
||||
// LogFlushFreqFlagName is the name of the command line parameter.
|
||||
// Depending on how flags get added, it is either a stand-alone
|
||||
// value (logs.AddFlags) or part of LoggingConfiguration.
|
||||
LogFlushFreqFlagName = "log-flush-frequency"
|
||||
)
|
||||
|
||||
// NewLoggingConfiguration returns a struct holding the default logging configuration.
|
||||
func NewLoggingConfiguration() *LoggingConfiguration {
|
||||
c := LoggingConfiguration{}
|
||||
c.SetRecommendedLoggingConfiguration()
|
||||
return &c
|
||||
}
|
||||
|
||||
// ValidateAndApply combines validation and application of the logging configuration.
|
||||
@ -50,58 +61,186 @@ func NewOptions() *Options {
|
||||
//
|
||||
// The optional FeatureGate controls logging features. If nil, the default for
|
||||
// these features is used.
|
||||
func (o *Options) ValidateAndApply(featureGate featuregate.FeatureGate) error {
|
||||
errs := o.validate()
|
||||
func (c *LoggingConfiguration) ValidateAndApply(featureGate featuregate.FeatureGate) error {
|
||||
return c.ValidateAndApplyAsField(featureGate, nil)
|
||||
}
|
||||
|
||||
// ValidateAndApplyAsField is a variant of ValidateAndApply that should be used
|
||||
// when the LoggingConfiguration is embedded in some larger configuration
|
||||
// structure.
|
||||
func (c *LoggingConfiguration) ValidateAndApplyAsField(featureGate featuregate.FeatureGate, fldPath *field.Path) error {
|
||||
errs := c.Validate(featureGate, fldPath)
|
||||
if len(errs) > 0 {
|
||||
return utilerrors.NewAggregate(errs)
|
||||
return errs.ToAggregate()
|
||||
}
|
||||
o.apply(featureGate)
|
||||
return nil
|
||||
return c.apply(featureGate)
|
||||
}
|
||||
|
||||
// validate verifies if any unsupported flag is set
|
||||
// for non-default logging format
|
||||
func (o *Options) validate() []error {
|
||||
errs := ValidateLoggingConfiguration(&o.Config, nil)
|
||||
if len(errs) != 0 {
|
||||
return errs.ToAggregate().Errors()
|
||||
// Validate can be used to check for invalid settings without applying them.
|
||||
// Most binaries should validate and apply the logging configuration as soon
|
||||
// as possible via ValidateAndApply. The field path is optional: nil
|
||||
// can be passed when the struct is not embedded in some larger struct.
|
||||
func (c *LoggingConfiguration) Validate(featureGate featuregate.FeatureGate, fldPath *field.Path) field.ErrorList {
|
||||
errs := field.ErrorList{}
|
||||
if c.Format != DefaultLogFormat {
|
||||
// WordSepNormalizeFunc is just a guess. Commands should use it,
|
||||
// but we cannot know for sure.
|
||||
allFlags := unsupportedLoggingFlags(cliflag.WordSepNormalizeFunc)
|
||||
for _, f := range allFlags {
|
||||
if f.DefValue != f.Value.String() {
|
||||
errs = append(errs, field.Invalid(fldPath.Child("format"), c.Format, fmt.Sprintf("Non-default format doesn't honor flag: %s", f.Name)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
if _, err := logRegistry.get(c.Format); err != nil {
|
||||
errs = append(errs, field.Invalid(fldPath.Child("format"), c.Format, "Unsupported log format"))
|
||||
}
|
||||
|
||||
// The type in our struct is uint32, but klog only accepts positive int32.
|
||||
if c.Verbosity > math.MaxInt32 {
|
||||
errs = append(errs, field.Invalid(fldPath.Child("verbosity"), c.Verbosity, fmt.Sprintf("Must be <= %d", math.MaxInt32)))
|
||||
}
|
||||
vmoduleFldPath := fldPath.Child("vmodule")
|
||||
if len(c.VModule) > 0 && c.Format != "" && c.Format != "text" {
|
||||
errs = append(errs, field.Forbidden(vmoduleFldPath, "Only supported for text log format"))
|
||||
}
|
||||
for i, item := range c.VModule {
|
||||
if item.FilePattern == "" {
|
||||
errs = append(errs, field.Required(vmoduleFldPath.Index(i), "File pattern must not be empty"))
|
||||
}
|
||||
if strings.ContainsAny(item.FilePattern, "=,") {
|
||||
errs = append(errs, field.Invalid(vmoduleFldPath.Index(i), item.FilePattern, "File pattern must not contain equal sign or comma"))
|
||||
}
|
||||
if item.Verbosity > math.MaxInt32 {
|
||||
errs = append(errs, field.Invalid(vmoduleFldPath.Index(i), item.Verbosity, fmt.Sprintf("Must be <= %d", math.MaxInt32)))
|
||||
}
|
||||
}
|
||||
|
||||
// Currently nothing to validate for c.Options.
|
||||
return errs
|
||||
}
|
||||
|
||||
// AddFlags add logging-format flag.
|
||||
//
|
||||
// Programs using LoggingConfiguration must use SkipLoggingConfigurationFlags
|
||||
// when calling AddFlags to avoid the duplicate registration of flags.
|
||||
func (o *Options) AddFlags(fs *pflag.FlagSet) {
|
||||
BindLoggingFlags(&o.Config, fs)
|
||||
}
|
||||
|
||||
// apply set klog logger from LogFormat type
|
||||
func (o *Options) apply(featureGate featuregate.FeatureGate) {
|
||||
func (c *LoggingConfiguration) apply(featureGate featuregate.FeatureGate) error {
|
||||
contextualLoggingEnabled := contextualLoggingDefault
|
||||
if featureGate != nil {
|
||||
contextualLoggingEnabled = featureGate.Enabled(ContextualLogging)
|
||||
}
|
||||
|
||||
// if log format not exists, use nil loggr
|
||||
factory, _ := registry.LogRegistry.Get(o.Config.Format)
|
||||
factory, _ := logRegistry.get(c.Format)
|
||||
if factory == nil {
|
||||
klog.ClearLogger()
|
||||
} else {
|
||||
// This logger will do its own verbosity checking, using the exact same
|
||||
// configuration as klog itself.
|
||||
log, flush := factory.Create(o.Config)
|
||||
// Therefore it can get called directly. However, we only allow that
|
||||
// when the feature is enabled.
|
||||
log, flush := factory.Create(*c)
|
||||
klog.SetLoggerWithOptions(log, klog.ContextualLogger(contextualLoggingEnabled), klog.FlushLogger(flush))
|
||||
}
|
||||
if err := loggingFlags.Lookup("v").Value.Set(o.Config.Verbosity.String()); err != nil {
|
||||
panic(fmt.Errorf("internal error while setting klog verbosity: %v", err))
|
||||
if err := loggingFlags.Lookup("v").Value.Set(VerbosityLevelPflag(&c.Verbosity).String()); err != nil {
|
||||
return fmt.Errorf("internal error while setting klog verbosity: %v", err)
|
||||
}
|
||||
if err := loggingFlags.Lookup("vmodule").Value.Set(o.Config.VModule.String()); err != nil {
|
||||
panic(fmt.Errorf("internal error while setting klog vmodule: %v", err))
|
||||
if err := loggingFlags.Lookup("vmodule").Value.Set(VModuleConfigurationPflag(&c.VModule).String()); err != nil {
|
||||
return fmt.Errorf("internal error while setting klog vmodule: %v", err)
|
||||
}
|
||||
klog.StartFlushDaemon(o.Config.FlushFrequency)
|
||||
klog.StartFlushDaemon(c.FlushFrequency)
|
||||
klog.EnableContextualLogging(contextualLoggingEnabled)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddFlags adds command line flags for the configuration.
|
||||
func (c *LoggingConfiguration) AddFlags(fs *pflag.FlagSet) {
|
||||
// The help text is generated assuming that flags will eventually use
|
||||
// hyphens, even if currently no normalization function is set for the
|
||||
// flag set yet.
|
||||
unsupportedFlags := strings.Join(unsupportedLoggingFlagNames(cliflag.WordSepNormalizeFunc), ", ")
|
||||
formats := fmt.Sprintf(`"%s"`, strings.Join(logRegistry.list(), `", "`))
|
||||
fs.StringVar(&c.Format, "logging-format", c.Format, fmt.Sprintf("Sets the log format. Permitted formats: %s.\nNon-default formats don't honor these flags: %s.\nNon-default choices are currently alpha and subject to change without warning.", formats, unsupportedFlags))
|
||||
// No new log formats should be added after generation is of flag options
|
||||
logRegistry.freeze()
|
||||
|
||||
fs.DurationVar(&c.FlushFrequency, LogFlushFreqFlagName, c.FlushFrequency, "Maximum number of seconds between log flushes")
|
||||
fs.VarP(VerbosityLevelPflag(&c.Verbosity), "v", "v", "number for the log level verbosity")
|
||||
fs.Var(VModuleConfigurationPflag(&c.VModule), "vmodule", "comma-separated list of pattern=N settings for file-filtered logging (only works for text log format)")
|
||||
|
||||
// JSON options. We only register them if "json" is a valid format. The
|
||||
// config file API however always has them.
|
||||
if _, err := logRegistry.get("json"); err == nil {
|
||||
fs.BoolVar(&c.Options.JSON.SplitStream, "log-json-split-stream", false, "[Experimental] In JSON format, write error messages to stderr and info messages to stdout. The default is to write a single stream to stdout.")
|
||||
fs.Var(&c.Options.JSON.InfoBufferSize, "log-json-info-buffer-size", "[Experimental] In JSON format with split output streams, the info messages can be buffered for a while to increase performance. The default value of zero bytes disables buffering. The size can be specified as number of bytes (512), multiples of 1000 (1K), multiples of 1024 (2Ki), or powers of those (3M, 4G, 5Mi, 6Gi).")
|
||||
}
|
||||
}
|
||||
|
||||
// SetRecommendedLoggingConfiguration sets the default logging configuration
|
||||
// for fields that are unset.
|
||||
//
|
||||
// Consumers who embed LoggingConfiguration in their own configuration structs
|
||||
// may set custom defaults and then should call this function to add the
|
||||
// global defaults.
|
||||
func (c *LoggingConfiguration) SetRecommendedLoggingConfiguration() {
|
||||
if c.Format == "" {
|
||||
c.Format = "text"
|
||||
}
|
||||
if c.FlushFrequency == 0 {
|
||||
c.FlushFrequency = LogFlushFreqDefault
|
||||
}
|
||||
var empty resource.QuantityValue
|
||||
if c.Options.JSON.InfoBufferSize == empty {
|
||||
c.Options.JSON.InfoBufferSize = resource.QuantityValue{
|
||||
// This is similar, but not quite the same as a default
|
||||
// constructed instance.
|
||||
Quantity: *resource.NewQuantity(0, resource.DecimalSI),
|
||||
}
|
||||
// This sets the unexported Quantity.s which will be compared
|
||||
// by reflect.DeepEqual in some tests.
|
||||
_ = c.Options.JSON.InfoBufferSize.String()
|
||||
}
|
||||
}
|
||||
|
||||
// loggingFlags captures the state of the logging flags, in particular their default value
|
||||
// before flag parsing. It is used by unsupportedLoggingFlags.
|
||||
var loggingFlags pflag.FlagSet
|
||||
|
||||
func init() {
|
||||
var fs flag.FlagSet
|
||||
klog.InitFlags(&fs)
|
||||
loggingFlags.AddGoFlagSet(&fs)
|
||||
}
|
||||
|
||||
// List of logs (k8s.io/klog + k8s.io/component-base/logs) flags supported by all logging formats
|
||||
var supportedLogsFlags = map[string]struct{}{
|
||||
"v": {},
|
||||
// TODO: support vmodule after 1.19 Alpha
|
||||
}
|
||||
|
||||
// unsupportedLoggingFlags lists unsupported logging flags. The normalize
|
||||
// function is optional.
|
||||
func unsupportedLoggingFlags(normalizeFunc func(f *pflag.FlagSet, name string) pflag.NormalizedName) []*pflag.Flag {
|
||||
// k8s.io/component-base/logs and klog flags
|
||||
pfs := &pflag.FlagSet{}
|
||||
loggingFlags.VisitAll(func(flag *pflag.Flag) {
|
||||
if _, found := supportedLogsFlags[flag.Name]; !found {
|
||||
// Normalization changes flag.Name, so make a copy.
|
||||
clone := *flag
|
||||
pfs.AddFlag(&clone)
|
||||
}
|
||||
})
|
||||
|
||||
// Apply normalization.
|
||||
pfs.SetNormalizeFunc(normalizeFunc)
|
||||
|
||||
var allFlags []*pflag.Flag
|
||||
pfs.VisitAll(func(flag *pflag.Flag) {
|
||||
allFlags = append(allFlags, flag)
|
||||
})
|
||||
return allFlags
|
||||
}
|
||||
|
||||
// unsupportedLoggingFlagNames lists unsupported logging flags by name, with
|
||||
// optional normalization and sorted.
|
||||
func unsupportedLoggingFlagNames(normalizeFunc func(f *pflag.FlagSet, name string) pflag.NormalizedName) []string {
|
||||
unsupportedFlags := unsupportedLoggingFlags(normalizeFunc)
|
||||
names := make([]string, 0, len(unsupportedFlags))
|
||||
for _, f := range unsupportedFlags {
|
||||
names = append(names, "--"+f.Name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package logs
|
||||
package v1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -32,10 +32,10 @@ import (
|
||||
)
|
||||
|
||||
func TestFlags(t *testing.T) {
|
||||
o := NewOptions()
|
||||
c := NewLoggingConfiguration()
|
||||
fs := pflag.NewFlagSet("addflagstest", pflag.ContinueOnError)
|
||||
output := bytes.Buffer{}
|
||||
o.AddFlags(fs)
|
||||
c.AddFlags(fs)
|
||||
fs.SetOutput(&output)
|
||||
fs.PrintDefaults()
|
||||
want := ` --log-flush-frequency duration Maximum number of seconds between log flushes (default 5s)
|
||||
@ -51,29 +51,30 @@ func TestFlags(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestOptions(t *testing.T) {
|
||||
newOptions := NewOptions()
|
||||
newOptions := NewLoggingConfiguration()
|
||||
testcases := []struct {
|
||||
name string
|
||||
args []string
|
||||
want *Options
|
||||
path *field.Path
|
||||
want *LoggingConfiguration
|
||||
errs field.ErrorList
|
||||
}{
|
||||
{
|
||||
name: "Default log format",
|
||||
want: newOptions,
|
||||
want: newOptions.DeepCopy(),
|
||||
},
|
||||
{
|
||||
name: "Text log format",
|
||||
args: []string{"--logging-format=text"},
|
||||
want: newOptions,
|
||||
want: newOptions.DeepCopy(),
|
||||
},
|
||||
{
|
||||
name: "Unsupported log format",
|
||||
args: []string{"--logging-format=test"},
|
||||
want: func() *Options {
|
||||
c := newOptions.Config.DeepCopy()
|
||||
want: func() *LoggingConfiguration {
|
||||
c := newOptions.DeepCopy()
|
||||
c.Format = "test"
|
||||
return &Options{*c}
|
||||
return c
|
||||
}(),
|
||||
errs: field.ErrorList{&field.Error{
|
||||
Type: "FieldValueInvalid",
|
||||
@ -86,19 +87,17 @@ func TestOptions(t *testing.T) {
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
o := NewOptions()
|
||||
c := NewLoggingConfiguration()
|
||||
fs := pflag.NewFlagSet("addflagstest", pflag.ContinueOnError)
|
||||
o.AddFlags(fs)
|
||||
c.AddFlags(fs)
|
||||
fs.Parse(tc.args)
|
||||
if !assert.Equal(t, tc.want, o) {
|
||||
t.Errorf("Wrong Validate() result for %q. expect %v, got %v", tc.name, tc.want, o)
|
||||
if !assert.Equal(t, tc.want, c) {
|
||||
t.Errorf("Wrong Validate() result for %q. expect %v, got %v", tc.name, tc.want, c)
|
||||
}
|
||||
err := o.ValidateAndApply(nil /* We don't care about feature gates here. */)
|
||||
errs := c.ValidateAndApply(nil /* We don't care about feature gates here. */)
|
||||
defer klog.StopFlushDaemon()
|
||||
|
||||
if !assert.ElementsMatch(t, tc.errs.ToAggregate(), err) {
|
||||
t.Errorf("Wrong Validate() result for %q.\n expect:\t%+v\n got:\t%+v", tc.name, tc.errs, err)
|
||||
|
||||
if !assert.ElementsMatch(t, tc.errs, errs) {
|
||||
t.Errorf("Wrong Validate() result for %q.\n expect:\t%+v\n got:\t%+v", tc.name, tc.errs, errs)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -117,12 +116,12 @@ func TestContextualLogging(t *testing.T) {
|
||||
func testContextualLogging(t *testing.T, enabled bool) {
|
||||
var err error
|
||||
|
||||
o := NewOptions()
|
||||
c := NewLoggingConfiguration()
|
||||
featureGate := featuregate.NewFeatureGate()
|
||||
AddFeatureGates(featureGate)
|
||||
err = featureGate.SetFromMap(map[string]bool{string(ContextualLogging): enabled})
|
||||
require.NoError(t, err)
|
||||
err = o.ValidateAndApply(featureGate)
|
||||
err = c.ValidateAndApply(featureGate)
|
||||
require.NoError(t, err)
|
||||
defer klog.StopFlushDaemon()
|
||||
defer klog.EnableContextualLogging(true)
|
104
staging/src/k8s.io/component-base/logs/api/v1/pflags.go
Normal file
104
staging/src/k8s.io/component-base/logs/api/v1/pflags.go
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// VModuleConfigurationPflag implements the pflag.Value interface for a
|
||||
// VModuleConfiguration. The value pointer must not be nil.
|
||||
func VModuleConfigurationPflag(value *VModuleConfiguration) pflag.Value {
|
||||
return vmoduleConfigurationPFlag{value}
|
||||
}
|
||||
|
||||
type vmoduleConfigurationPFlag struct {
|
||||
value *VModuleConfiguration
|
||||
}
|
||||
|
||||
// String returns the -vmodule parameter (comma-separated list of pattern=N).
|
||||
func (wrapper vmoduleConfigurationPFlag) String() string {
|
||||
var patterns []string
|
||||
for _, item := range *wrapper.value {
|
||||
patterns = append(patterns, fmt.Sprintf("%s=%d", item.FilePattern, item.Verbosity))
|
||||
}
|
||||
return strings.Join(patterns, ",")
|
||||
}
|
||||
|
||||
// Set parses the -vmodule parameter (comma-separated list of pattern=N).
|
||||
func (wrapper vmoduleConfigurationPFlag) Set(value string) error {
|
||||
// This code mirrors https://github.com/kubernetes/klog/blob/9ad246211af1ed84621ee94a26fcce0038b69cd1/klog.go#L287-L313
|
||||
|
||||
for _, pat := range strings.Split(value, ",") {
|
||||
if len(pat) == 0 {
|
||||
// Empty strings such as from a trailing comma can be ignored.
|
||||
continue
|
||||
}
|
||||
patLev := strings.Split(pat, "=")
|
||||
if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 {
|
||||
return fmt.Errorf("%q does not have the pattern=N format", pat)
|
||||
}
|
||||
pattern := patLev[0]
|
||||
// 31 instead of 32 to ensure that it also fits into int32.
|
||||
v, err := strconv.ParseUint(patLev[1], 10, 31)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing verbosity in %q: %v", pat, err)
|
||||
}
|
||||
*wrapper.value = append(*wrapper.value, VModuleItem{FilePattern: pattern, Verbosity: VerbosityLevel(v)})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wrapper vmoduleConfigurationPFlag) Type() string {
|
||||
return "pattern=N,..."
|
||||
}
|
||||
|
||||
// VerbosityLevelPflag implements the pflag.Value interface for a verbosity
|
||||
// level value.
|
||||
func VerbosityLevelPflag(value *VerbosityLevel) pflag.Value {
|
||||
return verbosityLevelPflag{value}
|
||||
}
|
||||
|
||||
type verbosityLevelPflag struct {
|
||||
value *VerbosityLevel
|
||||
}
|
||||
|
||||
func (wrapper verbosityLevelPflag) String() string {
|
||||
return strconv.FormatInt(int64(*wrapper.value), 10)
|
||||
}
|
||||
|
||||
func (wrapper verbosityLevelPflag) Get() interface{} {
|
||||
return *wrapper.value
|
||||
}
|
||||
|
||||
func (wrapper verbosityLevelPflag) Set(value string) error {
|
||||
// Limited to int32 for compatibility with klog.
|
||||
v, err := strconv.ParseUint(value, 10, 31)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*wrapper.value = VerbosityLevel(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wrapper verbosityLevelPflag) Type() string {
|
||||
return "Level"
|
||||
}
|
@ -14,22 +14,19 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package registry
|
||||
package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
|
||||
"k8s.io/component-base/config"
|
||||
)
|
||||
|
||||
// LogRegistry is new init LogFormatRegistry struct
|
||||
var LogRegistry = NewLogFormatRegistry()
|
||||
var logRegistry = newLogFormatRegistry()
|
||||
|
||||
// LogFormatRegistry store klog format registry
|
||||
type LogFormatRegistry struct {
|
||||
// logFormatRegistry stores factories for all supported logging formats.
|
||||
type logFormatRegistry struct {
|
||||
registry map[string]LogFormatFactory
|
||||
frozen bool
|
||||
}
|
||||
@ -41,22 +38,28 @@ type LogFormatFactory interface {
|
||||
// Returning a flush function for the logger is optional.
|
||||
// If provided, the caller must ensure that it is called
|
||||
// periodically (if desired) and at program exit.
|
||||
Create(c config.LoggingConfiguration) (log logr.Logger, flush func())
|
||||
Create(c LoggingConfiguration) (log logr.Logger, flush func())
|
||||
}
|
||||
|
||||
// NewLogFormatRegistry return new init LogFormatRegistry struct
|
||||
func NewLogFormatRegistry() *LogFormatRegistry {
|
||||
return &LogFormatRegistry{
|
||||
// RegisterLogFormat registers support for a new logging format. This must be called
|
||||
// before using any of the methods in LoggingConfiguration.
|
||||
func RegisterLogFormat(name string, factory LogFormatFactory) error {
|
||||
return logRegistry.register(name, factory)
|
||||
}
|
||||
|
||||
func newLogFormatRegistry() *logFormatRegistry {
|
||||
registry := &logFormatRegistry{
|
||||
registry: make(map[string]LogFormatFactory),
|
||||
frozen: false,
|
||||
}
|
||||
registry.register("text", nil)
|
||||
return registry
|
||||
}
|
||||
|
||||
// Register new log format registry to global logRegistry.
|
||||
// nil is valid and selects the default klog output.
|
||||
func (lfr *LogFormatRegistry) Register(name string, factory LogFormatFactory) error {
|
||||
// register adds a new log format. It's an error to modify an existing one.
|
||||
func (lfr *logFormatRegistry) register(name string, factory LogFormatFactory) error {
|
||||
if lfr.frozen {
|
||||
return fmt.Errorf("log format is frozen, unable to register log format")
|
||||
return fmt.Errorf("log format registry is frozen, unable to register log format %s", name)
|
||||
}
|
||||
if _, ok := lfr.registry[name]; ok {
|
||||
return fmt.Errorf("log format: %s already exists", name)
|
||||
@ -65,8 +68,8 @@ func (lfr *LogFormatRegistry) Register(name string, factory LogFormatFactory) er
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get specified log format logger
|
||||
func (lfr *LogFormatRegistry) Get(name string) (LogFormatFactory, error) {
|
||||
// get specified log format factory
|
||||
func (lfr *logFormatRegistry) get(name string) (LogFormatFactory, error) {
|
||||
re, ok := lfr.registry[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("log format: %s does not exists", name)
|
||||
@ -74,28 +77,8 @@ func (lfr *LogFormatRegistry) Get(name string) (LogFormatFactory, error) {
|
||||
return re, nil
|
||||
}
|
||||
|
||||
// Set specified log format logger
|
||||
func (lfr *LogFormatRegistry) Set(name string, factory LogFormatFactory) error {
|
||||
if lfr.frozen {
|
||||
return fmt.Errorf("log format is frozen, unable to set log format")
|
||||
}
|
||||
|
||||
lfr.registry[name] = factory
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete specified log format logger
|
||||
func (lfr *LogFormatRegistry) Delete(name string) error {
|
||||
if lfr.frozen {
|
||||
return fmt.Errorf("log format is frozen, unable to delete log format")
|
||||
}
|
||||
|
||||
delete(lfr.registry, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// List names of registered log formats (sorted)
|
||||
func (lfr *LogFormatRegistry) List() []string {
|
||||
// list names of registered log formats (sorted)
|
||||
func (lfr *logFormatRegistry) list() []string {
|
||||
formats := make([]string, 0, len(lfr.registry))
|
||||
for f := range lfr.registry {
|
||||
formats = append(formats, f)
|
||||
@ -104,7 +87,7 @@ func (lfr *LogFormatRegistry) List() []string {
|
||||
return formats
|
||||
}
|
||||
|
||||
// Freeze freezes the log format registry
|
||||
func (lfr *LogFormatRegistry) Freeze() {
|
||||
// freeze prevents further modifications of the registered log formats.
|
||||
func (lfr *logFormatRegistry) freeze() {
|
||||
lfr.frozen = true
|
||||
}
|
||||
|
92
staging/src/k8s.io/component-base/logs/api/v1/types.go
Normal file
92
staging/src/k8s.io/component-base/logs/api/v1/types.go
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
)
|
||||
|
||||
// Supported output formats.
|
||||
const (
|
||||
// DefaultLogFormat is the traditional klog output format.
|
||||
DefaultLogFormat = "text"
|
||||
|
||||
// JSONLogFormat emits each log message as a JSON struct.
|
||||
JSONLogFormat = "json"
|
||||
)
|
||||
|
||||
// LoggingConfiguration contains logging options.
|
||||
type LoggingConfiguration struct {
|
||||
// Format Flag specifies the structure of log messages.
|
||||
// default value of format is `text`
|
||||
Format string `json:"format,omitempty"`
|
||||
// Maximum number of nanoseconds (i.e. 1s = 1000000000) between log
|
||||
// flushes. Ignored if the selected logging backend writes log
|
||||
// messages without buffering.
|
||||
FlushFrequency time.Duration `json:"flushFrequency"`
|
||||
// Verbosity is the threshold that determines which log messages are
|
||||
// logged. Default is zero which logs only the most important
|
||||
// messages. Higher values enable additional messages. Error messages
|
||||
// are always logged.
|
||||
Verbosity VerbosityLevel `json:"verbosity"`
|
||||
// VModule overrides the verbosity threshold for individual files.
|
||||
// Only supported for "text" log format.
|
||||
VModule VModuleConfiguration `json:"vmodule,omitempty"`
|
||||
// [Experimental] Options holds additional parameters that are specific
|
||||
// to the different logging formats. Only the options for the selected
|
||||
// format get used, but all of them get validated.
|
||||
Options FormatOptions `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
// FormatOptions contains options for the different logging formats.
|
||||
type FormatOptions struct {
|
||||
// [Experimental] JSON contains options for logging format "json".
|
||||
JSON JSONOptions `json:"json,omitempty"`
|
||||
}
|
||||
|
||||
// JSONOptions contains options for logging format "json".
|
||||
type JSONOptions struct {
|
||||
// [Experimental] SplitStream redirects error messages to stderr while
|
||||
// info messages go to stdout, with buffering. The default is to write
|
||||
// both to stdout, without buffering.
|
||||
SplitStream bool `json:"splitStream,omitempty"`
|
||||
// [Experimental] InfoBufferSize sets the size of the info stream when
|
||||
// using split streams. The default is zero, which disables buffering.
|
||||
InfoBufferSize resource.QuantityValue `json:"infoBufferSize,omitempty"`
|
||||
}
|
||||
|
||||
// VModuleConfiguration is a collection of individual file names or patterns
|
||||
// and the corresponding verbosity threshold.
|
||||
type VModuleConfiguration []VModuleItem
|
||||
|
||||
// VModuleItem defines verbosity for one or more files which match a certain
|
||||
// glob pattern.
|
||||
type VModuleItem struct {
|
||||
// FilePattern is a base file name (i.e. minus the ".go" suffix and
|
||||
// directory) or a "glob" pattern for such a name. It must not contain
|
||||
// comma and equal signs because those are separators for the
|
||||
// corresponding klog command line argument.
|
||||
FilePattern string `json:"filePattern"`
|
||||
// Verbosity is the threshold for log messages emitted inside files
|
||||
// that match the pattern.
|
||||
Verbosity VerbosityLevel `json:"verbosity"`
|
||||
}
|
||||
|
||||
// VerbosityLevel represents a klog or logr verbosity threshold.
|
||||
type VerbosityLevel uint32
|
266
staging/src/k8s.io/component-base/logs/api/v1/types_test.go
Normal file
266
staging/src/k8s.io/component-base/logs/api/v1/types_test.go
Normal file
@ -0,0 +1,266 @@
|
||||
/*
|
||||
Copyright 2022 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"sigs.k8s.io/json"
|
||||
)
|
||||
|
||||
func TestVModule(t *testing.T) {
|
||||
testcases := []struct {
|
||||
arg string
|
||||
expectError string
|
||||
expectValue VModuleConfiguration
|
||||
expectParam string
|
||||
}{
|
||||
{
|
||||
arg: "gopher*=1",
|
||||
expectValue: VModuleConfiguration{
|
||||
{
|
||||
FilePattern: "gopher*",
|
||||
Verbosity: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
arg: "foo=1,bar=2",
|
||||
expectValue: VModuleConfiguration{
|
||||
{
|
||||
FilePattern: "foo",
|
||||
Verbosity: 1,
|
||||
},
|
||||
{
|
||||
FilePattern: "bar",
|
||||
Verbosity: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
arg: "foo=1,bar=2,",
|
||||
expectValue: VModuleConfiguration{
|
||||
{
|
||||
FilePattern: "foo",
|
||||
Verbosity: 1,
|
||||
},
|
||||
{
|
||||
FilePattern: "bar",
|
||||
Verbosity: 2,
|
||||
},
|
||||
},
|
||||
expectParam: "foo=1,bar=2",
|
||||
},
|
||||
{
|
||||
arg: "gopher*",
|
||||
expectError: `"gopher*" does not have the pattern=N format`,
|
||||
},
|
||||
{
|
||||
arg: "=1",
|
||||
expectError: `"=1" does not have the pattern=N format`,
|
||||
},
|
||||
{
|
||||
arg: "foo=-1",
|
||||
expectError: `parsing verbosity in "foo=-1": strconv.ParseUint: parsing "-1": invalid syntax`,
|
||||
},
|
||||
{
|
||||
arg: fmt.Sprintf("validint32=%d", math.MaxInt32),
|
||||
expectValue: VModuleConfiguration{
|
||||
{
|
||||
FilePattern: "validint32",
|
||||
Verbosity: math.MaxInt32,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
arg: fmt.Sprintf("invalidint32=%d", math.MaxInt32+1),
|
||||
expectError: `parsing verbosity in "invalidint32=2147483648": strconv.ParseUint: parsing "2147483648": value out of range`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testcases {
|
||||
t.Run(test.arg, func(t *testing.T) {
|
||||
var actual VModuleConfiguration
|
||||
value := VModuleConfigurationPflag(&actual)
|
||||
err := value.Set(test.arg)
|
||||
if test.expectError != "" {
|
||||
if err == nil {
|
||||
t.Fatal("parsing should have failed")
|
||||
}
|
||||
assert.Equal(t, test.expectError, err.Error(), "parse error")
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
param := value.String()
|
||||
expectParam := test.expectParam
|
||||
if expectParam == "" {
|
||||
expectParam = test.arg
|
||||
}
|
||||
assert.Equal(t, expectParam, param, "encoded parameter value not identical")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestCompatibility ensures that a) valid JSON remains valid and has the same
|
||||
// effect and b) that new fields are covered by the test data.
|
||||
func TestCompatibility(t *testing.T) {
|
||||
testcases := map[string]struct {
|
||||
// fixture holds a representation of a LoggingConfiguration struct in JSON format.
|
||||
fixture string
|
||||
// baseConfig is the struct that Unmarshal writes into.
|
||||
baseConfig LoggingConfiguration
|
||||
// expectAllFields enables a reflection check to ensure that the
|
||||
// result has all fields set.
|
||||
expectAllFields bool
|
||||
// expectConfig is the intended result.
|
||||
expectConfig LoggingConfiguration
|
||||
}{
|
||||
"defaults": {
|
||||
// No changes when nothing is specified.
|
||||
fixture: "{}",
|
||||
baseConfig: *NewLoggingConfiguration(),
|
||||
expectConfig: *NewLoggingConfiguration(),
|
||||
},
|
||||
"all-fields": {
|
||||
// The JSON fixture includes all fields. The result
|
||||
// must have all fields as non-empty when starting with
|
||||
// an empty base, otherwise the fixture is incomplete
|
||||
// and must be updated for the test case to pass.
|
||||
fixture: `{
|
||||
"format": "json",
|
||||
"flushFrequency": 1,
|
||||
"verbosity": 5,
|
||||
"vmodule": [
|
||||
{"filePattern": "someFile", "verbosity": 10},
|
||||
{"filePattern": "anotherFile", "verbosity": 1}
|
||||
],
|
||||
"options": {
|
||||
"json": {
|
||||
"splitStream": true,
|
||||
"infoBufferSize": "1024"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
baseConfig: LoggingConfiguration{},
|
||||
expectAllFields: true,
|
||||
expectConfig: LoggingConfiguration{
|
||||
Format: JSONLogFormat,
|
||||
FlushFrequency: time.Nanosecond,
|
||||
Verbosity: VerbosityLevel(5),
|
||||
VModule: VModuleConfiguration{
|
||||
{
|
||||
FilePattern: "someFile",
|
||||
Verbosity: VerbosityLevel(10),
|
||||
},
|
||||
{
|
||||
FilePattern: "anotherFile",
|
||||
Verbosity: VerbosityLevel(1),
|
||||
},
|
||||
},
|
||||
Options: FormatOptions{
|
||||
JSON: JSONOptions{
|
||||
SplitStream: true,
|
||||
InfoBufferSize: resource.QuantityValue{
|
||||
Quantity: *resource.NewQuantity(1024, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testcases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// Beware, not a deep copy. Different test cases must
|
||||
// not share anything.
|
||||
config := tc.baseConfig
|
||||
if strictErr, err := json.UnmarshalStrict([]byte(tc.fixture), &config); err != nil {
|
||||
t.Fatalf("unexpected unmarshal error: %v", err)
|
||||
} else if strictErr != nil {
|
||||
t.Fatalf("unexpected strict unmarshal error: %v", strictErr)
|
||||
}
|
||||
// This sets the internal "s" field just like unmarshaling does.
|
||||
// Required for assert.Equal to pass.
|
||||
_ = tc.expectConfig.Options.JSON.InfoBufferSize.String()
|
||||
assert.Equal(t, tc.expectConfig, config)
|
||||
if tc.expectAllFields {
|
||||
notZeroRecursive(t, config, "LoggingConfiguration")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// notZero asserts that i is not the zero value for its type
|
||||
// and repeats that check recursively for all pointers,
|
||||
// structs, maps, arrays, and slices.
|
||||
func notZeroRecursive(t *testing.T, i interface{}, path string) bool {
|
||||
typeOfI := reflect.TypeOf(i)
|
||||
|
||||
if i == nil || reflect.DeepEqual(i, reflect.Zero(typeOfI).Interface()) {
|
||||
t.Errorf("%s: should not have been zero, but was %v", path, i)
|
||||
return false
|
||||
}
|
||||
|
||||
valid := true
|
||||
kind := typeOfI.Kind()
|
||||
value := reflect.ValueOf(i)
|
||||
switch kind {
|
||||
case reflect.Ptr:
|
||||
if !notZeroRecursive(t, value.Elem().Interface(), path) {
|
||||
valid = false
|
||||
}
|
||||
case reflect.Struct:
|
||||
for i := 0; i < typeOfI.NumField(); i++ {
|
||||
if !typeOfI.Field(i).IsExported() {
|
||||
// Cannot access value.
|
||||
continue
|
||||
}
|
||||
if !notZeroRecursive(t, value.Field(i).Interface(), path+"."+typeOfI.Field(i).Name) {
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
iter := value.MapRange()
|
||||
for iter.Next() {
|
||||
k := iter.Key()
|
||||
v := iter.Value()
|
||||
if !notZeroRecursive(t, k.Interface(), path+"."+"<key>") {
|
||||
valid = false
|
||||
}
|
||||
if !notZeroRecursive(t, v.Interface(), path+"["+fmt.Sprintf("%v", k.Interface())+"]") {
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
case reflect.Slice, reflect.Array:
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
if !notZeroRecursive(t, value.Index(i).Interface(), path+"["+fmt.Sprintf("%d", i)+"]") {
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return valid
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package logs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
cliflag "k8s.io/component-base/cli/flag"
|
||||
"k8s.io/component-base/config"
|
||||
"k8s.io/component-base/logs/registry"
|
||||
)
|
||||
|
||||
func ValidateLoggingConfiguration(c *config.LoggingConfiguration, fldPath *field.Path) field.ErrorList {
|
||||
errs := field.ErrorList{}
|
||||
if c.Format != DefaultLogFormat {
|
||||
// WordSepNormalizeFunc is just a guess. Commands should use it,
|
||||
// but we cannot know for sure.
|
||||
allFlags := UnsupportedLoggingFlags(cliflag.WordSepNormalizeFunc)
|
||||
for _, f := range allFlags {
|
||||
if f.DefValue != f.Value.String() {
|
||||
errs = append(errs, field.Invalid(fldPath.Child("format"), c.Format, fmt.Sprintf("Non-default format doesn't honor flag: %s", f.Name)))
|
||||
}
|
||||
}
|
||||
}
|
||||
_, err := registry.LogRegistry.Get(c.Format)
|
||||
if err != nil {
|
||||
errs = append(errs, field.Invalid(fldPath.Child("format"), c.Format, "Unsupported log format"))
|
||||
}
|
||||
|
||||
// The type in our struct is uint32, but klog only accepts positive int32.
|
||||
if c.Verbosity > math.MaxInt32 {
|
||||
errs = append(errs, field.Invalid(fldPath.Child("verbosity"), c.Verbosity, fmt.Sprintf("Must be <= %d", math.MaxInt32)))
|
||||
}
|
||||
vmoduleFldPath := fldPath.Child("vmodule")
|
||||
if len(c.VModule) > 0 && c.Format != "" && c.Format != "text" {
|
||||
errs = append(errs, field.Forbidden(vmoduleFldPath, "Only supported for text log format"))
|
||||
}
|
||||
for i, item := range c.VModule {
|
||||
if item.FilePattern == "" {
|
||||
errs = append(errs, field.Required(vmoduleFldPath.Index(i), "File pattern must not be empty"))
|
||||
}
|
||||
if strings.ContainsAny(item.FilePattern, "=,") {
|
||||
errs = append(errs, field.Invalid(vmoduleFldPath.Index(i), item.FilePattern, "File pattern must not contain equal sign or comma"))
|
||||
}
|
||||
if item.Verbosity > math.MaxInt32 {
|
||||
errs = append(errs, field.Invalid(vmoduleFldPath.Index(i), item.Verbosity, fmt.Sprintf("Must be <= %d", math.MaxInt32)))
|
||||
}
|
||||
}
|
||||
|
||||
// Currently nothing to validate for c.Options.
|
||||
|
||||
return errs
|
||||
}
|
@ -14,27 +14,27 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package logs
|
||||
package v1
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"k8s.io/component-base/config"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func TestValidateLoggingConfiguration(t *testing.T) {
|
||||
func TestValidation(t *testing.T) {
|
||||
testcases := map[string]struct {
|
||||
config config.LoggingConfiguration
|
||||
config LoggingConfiguration
|
||||
path *field.Path
|
||||
expectErrors string
|
||||
}{
|
||||
"okay": {
|
||||
config: config.LoggingConfiguration{
|
||||
config: LoggingConfiguration{
|
||||
Format: "text",
|
||||
Verbosity: 10,
|
||||
VModule: config.VModuleConfiguration{
|
||||
VModule: VModuleConfiguration{
|
||||
{
|
||||
FilePattern: "gopher*",
|
||||
Verbosity: 100,
|
||||
@ -43,22 +43,29 @@ func TestValidateLoggingConfiguration(t *testing.T) {
|
||||
},
|
||||
},
|
||||
"wrong-format": {
|
||||
config: config.LoggingConfiguration{
|
||||
config: LoggingConfiguration{
|
||||
Format: "no-such-format",
|
||||
},
|
||||
expectErrors: `format: Invalid value: "no-such-format": Unsupported log format`,
|
||||
},
|
||||
"embedded": {
|
||||
config: LoggingConfiguration{
|
||||
Format: "no-such-format",
|
||||
},
|
||||
path: field.NewPath("config"),
|
||||
expectErrors: `config.format: Invalid value: "no-such-format": Unsupported log format`,
|
||||
},
|
||||
"verbosity-overflow": {
|
||||
config: config.LoggingConfiguration{
|
||||
config: LoggingConfiguration{
|
||||
Format: "text",
|
||||
Verbosity: math.MaxInt32 + 1,
|
||||
},
|
||||
expectErrors: `verbosity: Invalid value: 0x80000000: Must be <= 2147483647`,
|
||||
},
|
||||
"vmodule-verbosity-overflow": {
|
||||
config: config.LoggingConfiguration{
|
||||
config: LoggingConfiguration{
|
||||
Format: "text",
|
||||
VModule: config.VModuleConfiguration{
|
||||
VModule: VModuleConfiguration{
|
||||
{
|
||||
FilePattern: "gopher*",
|
||||
Verbosity: math.MaxInt32 + 1,
|
||||
@ -68,9 +75,9 @@ func TestValidateLoggingConfiguration(t *testing.T) {
|
||||
expectErrors: `vmodule[0]: Invalid value: 0x80000000: Must be <= 2147483647`,
|
||||
},
|
||||
"vmodule-empty-pattern": {
|
||||
config: config.LoggingConfiguration{
|
||||
config: LoggingConfiguration{
|
||||
Format: "text",
|
||||
VModule: config.VModuleConfiguration{
|
||||
VModule: VModuleConfiguration{
|
||||
{
|
||||
FilePattern: "",
|
||||
Verbosity: 1,
|
||||
@ -80,9 +87,9 @@ func TestValidateLoggingConfiguration(t *testing.T) {
|
||||
expectErrors: `vmodule[0]: Required value: File pattern must not be empty`,
|
||||
},
|
||||
"vmodule-pattern-with-special-characters": {
|
||||
config: config.LoggingConfiguration{
|
||||
config: LoggingConfiguration{
|
||||
Format: "text",
|
||||
VModule: config.VModuleConfiguration{
|
||||
VModule: VModuleConfiguration{
|
||||
{
|
||||
FilePattern: "foo,bar",
|
||||
Verbosity: 1,
|
||||
@ -96,9 +103,9 @@ func TestValidateLoggingConfiguration(t *testing.T) {
|
||||
expectErrors: `[vmodule[0]: Invalid value: "foo,bar": File pattern must not contain equal sign or comma, vmodule[1]: Invalid value: "foo=bar": File pattern must not contain equal sign or comma]`,
|
||||
},
|
||||
"vmodule-unsupported": {
|
||||
config: config.LoggingConfiguration{
|
||||
config: LoggingConfiguration{
|
||||
Format: "json",
|
||||
VModule: config.VModuleConfiguration{
|
||||
VModule: VModuleConfiguration{
|
||||
{
|
||||
FilePattern: "foo",
|
||||
Verbosity: 1,
|
||||
@ -111,13 +118,13 @@ func TestValidateLoggingConfiguration(t *testing.T) {
|
||||
|
||||
for name, test := range testcases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
errs := ValidateLoggingConfiguration(&test.config, nil)
|
||||
if len(errs) == 0 {
|
||||
err := test.config.Validate(nil, test.path)
|
||||
if len(err) == 0 {
|
||||
if test.expectErrors != "" {
|
||||
t.Fatalf("did not get expected error(s): %s", test.expectErrors)
|
||||
}
|
||||
} else {
|
||||
assert.Equal(t, test.expectErrors, errs.ToAggregate().Error())
|
||||
assert.Equal(t, test.expectErrors, err.ToAggregate().Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
114
staging/src/k8s.io/component-base/logs/api/v1/zz_generated.deepcopy.go
generated
Normal file
114
staging/src/k8s.io/component-base/logs/api/v1/zz_generated.deepcopy.go
generated
Normal file
@ -0,0 +1,114 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v1
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *FormatOptions) DeepCopyInto(out *FormatOptions) {
|
||||
*out = *in
|
||||
in.JSON.DeepCopyInto(&out.JSON)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FormatOptions.
|
||||
func (in *FormatOptions) DeepCopy() *FormatOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(FormatOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *JSONOptions) DeepCopyInto(out *JSONOptions) {
|
||||
*out = *in
|
||||
in.InfoBufferSize.DeepCopyInto(&out.InfoBufferSize)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONOptions.
|
||||
func (in *JSONOptions) DeepCopy() *JSONOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(JSONOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *LoggingConfiguration) DeepCopyInto(out *LoggingConfiguration) {
|
||||
*out = *in
|
||||
if in.VModule != nil {
|
||||
in, out := &in.VModule, &out.VModule
|
||||
*out = make(VModuleConfiguration, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
in.Options.DeepCopyInto(&out.Options)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoggingConfiguration.
|
||||
func (in *LoggingConfiguration) DeepCopy() *LoggingConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(LoggingConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in VModuleConfiguration) DeepCopyInto(out *VModuleConfiguration) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(VModuleConfiguration, len(*in))
|
||||
copy(*out, *in)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VModuleConfiguration.
|
||||
func (in VModuleConfiguration) DeepCopy() VModuleConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(VModuleConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *VModuleItem) DeepCopyInto(out *VModuleItem) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VModuleItem.
|
||||
func (in *VModuleItem) DeepCopy() *VModuleItem {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(VModuleItem)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package logs
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
cliflag "k8s.io/component-base/cli/flag"
|
||||
"k8s.io/component-base/config"
|
||||
"k8s.io/component-base/logs/registry"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// Supported klog formats
|
||||
const (
|
||||
DefaultLogFormat = "text"
|
||||
JSONLogFormat = "json"
|
||||
)
|
||||
|
||||
// loggingFlags captures the state of the logging flags, in particular their default value
|
||||
// before flag parsing. It is used by UnsupportedLoggingFlags.
|
||||
var loggingFlags pflag.FlagSet
|
||||
|
||||
func init() {
|
||||
// Text format is default klog format
|
||||
registry.LogRegistry.Register(DefaultLogFormat, nil)
|
||||
|
||||
var fs flag.FlagSet
|
||||
klog.InitFlags(&fs)
|
||||
loggingFlags.AddGoFlagSet(&fs)
|
||||
}
|
||||
|
||||
// List of logs (k8s.io/klog + k8s.io/component-base/logs) flags supported by all logging formats
|
||||
var supportedLogsFlags = map[string]struct{}{
|
||||
"v": {},
|
||||
}
|
||||
|
||||
// BindLoggingFlags binds the Options struct fields to a flagset.
|
||||
//
|
||||
// Programs using LoggingConfiguration must use SkipLoggingConfigurationFlags
|
||||
// when calling AddFlags to avoid the duplicate registration of flags.
|
||||
func BindLoggingFlags(c *config.LoggingConfiguration, fs *pflag.FlagSet) {
|
||||
// The help text is generated assuming that flags will eventually use
|
||||
// hyphens, even if currently no normalization function is set for the
|
||||
// flag set yet.
|
||||
unsupportedFlags := strings.Join(unsupportedLoggingFlagNames(cliflag.WordSepNormalizeFunc), ", ")
|
||||
formats := fmt.Sprintf(`"%s"`, strings.Join(registry.LogRegistry.List(), `", "`))
|
||||
fs.StringVar(&c.Format, "logging-format", c.Format, fmt.Sprintf("Sets the log format. Permitted formats: %s.\nNon-default formats don't honor these flags: %s.\nNon-default choices are currently alpha and subject to change without warning.", formats, unsupportedFlags))
|
||||
// No new log formats should be added after generation is of flag options
|
||||
registry.LogRegistry.Freeze()
|
||||
|
||||
fs.DurationVar(&c.FlushFrequency, logFlushFreqFlagName, logFlushFreq, "Maximum number of seconds between log flushes")
|
||||
fs.VarP(&c.Verbosity, "v", "v", "number for the log level verbosity")
|
||||
fs.Var(&c.VModule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging (only works for text log format)")
|
||||
|
||||
// JSON options. We only register them if "json" is a valid format. The
|
||||
// config file API however always has them.
|
||||
if _, err := registry.LogRegistry.Get("json"); err == nil {
|
||||
fs.BoolVar(&c.Options.JSON.SplitStream, "log-json-split-stream", false, "[Experimental] In JSON format, write error messages to stderr and info messages to stdout. The default is to write a single stream to stdout.")
|
||||
fs.Var(&c.Options.JSON.InfoBufferSize, "log-json-info-buffer-size", "[Experimental] In JSON format with split output streams, the info messages can be buffered for a while to increase performance. The default value of zero bytes disables buffering. The size can be specified as number of bytes (512), multiples of 1000 (1K), multiples of 1024 (2Ki), or powers of those (3M, 4G, 5Mi, 6Gi).")
|
||||
}
|
||||
}
|
||||
|
||||
// UnsupportedLoggingFlags lists unsupported logging flags. The normalize
|
||||
// function is optional.
|
||||
func UnsupportedLoggingFlags(normalizeFunc func(f *pflag.FlagSet, name string) pflag.NormalizedName) []*pflag.Flag {
|
||||
// k8s.io/component-base/logs and klog flags
|
||||
pfs := &pflag.FlagSet{}
|
||||
loggingFlags.VisitAll(func(flag *pflag.Flag) {
|
||||
if _, found := supportedLogsFlags[flag.Name]; !found {
|
||||
// Normalization changes flag.Name, so make a copy.
|
||||
clone := *flag
|
||||
pfs.AddFlag(&clone)
|
||||
}
|
||||
})
|
||||
|
||||
// Apply normalization.
|
||||
pfs.SetNormalizeFunc(normalizeFunc)
|
||||
|
||||
var allFlags []*pflag.Flag
|
||||
pfs.VisitAll(func(flag *pflag.Flag) {
|
||||
allFlags = append(allFlags, flag)
|
||||
})
|
||||
return allFlags
|
||||
}
|
||||
|
||||
// unsupportedLoggingFlagNames lists unsupported logging flags by name, with
|
||||
// optional normalization and sorted.
|
||||
func unsupportedLoggingFlagNames(normalizeFunc func(f *pflag.FlagSet, name string) pflag.NormalizedName) []string {
|
||||
unsupportedFlags := UnsupportedLoggingFlags(normalizeFunc)
|
||||
names := make([]string, 0, len(unsupportedFlags))
|
||||
for _, f := range unsupportedFlags {
|
||||
names = append(names, "--"+f.Name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
@ -29,6 +29,7 @@ import (
|
||||
"k8s.io/component-base/cli"
|
||||
"k8s.io/component-base/featuregate"
|
||||
"k8s.io/component-base/logs"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
_ "k8s.io/component-base/logs/json/register"
|
||||
@ -37,7 +38,7 @@ import (
|
||||
var featureGate = featuregate.NewFeatureGate()
|
||||
|
||||
func main() {
|
||||
runtime.Must(logs.AddFeatureGates(featureGate))
|
||||
runtime.Must(logsapi.AddFeatureGates(featureGate))
|
||||
command := NewLoggerCommand()
|
||||
|
||||
// Intentionally broken: logging is not initialized yet.
|
||||
@ -48,11 +49,11 @@ func main() {
|
||||
}
|
||||
|
||||
func NewLoggerCommand() *cobra.Command {
|
||||
o := logs.NewOptions()
|
||||
c := logsapi.NewLoggingConfiguration()
|
||||
cmd := &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
logs.InitLogs()
|
||||
if err := o.ValidateAndApply(featureGate); err != nil {
|
||||
if err := c.ValidateAndApply(featureGate); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
@ -64,9 +65,9 @@ func NewLoggerCommand() *cobra.Command {
|
||||
runLogger(ctx)
|
||||
},
|
||||
}
|
||||
logs.AddFeatureGates(featureGate)
|
||||
logsapi.AddFeatureGates(featureGate)
|
||||
featureGate.AddFlag(cmd.Flags())
|
||||
o.AddFlags(cmd.Flags())
|
||||
c.AddFlags(cmd.Flags())
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,7 @@ import (
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
"k8s.io/component-base/config"
|
||||
"k8s.io/component-base/logs/registry"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -38,7 +37,7 @@ var (
|
||||
// NewJSONLogger creates a new json logr.Logger and its associated
|
||||
// flush function. The separate error stream is optional and may be nil.
|
||||
// The encoder config is also optional.
|
||||
func NewJSONLogger(v config.VerbosityLevel, infoStream, errorStream zapcore.WriteSyncer, encoderConfig *zapcore.EncoderConfig) (logr.Logger, func()) {
|
||||
func NewJSONLogger(v logsapi.VerbosityLevel, infoStream, errorStream zapcore.WriteSyncer, encoderConfig *zapcore.EncoderConfig) (logr.Logger, func()) {
|
||||
// zap levels are inverted: everything with a verbosity >= threshold gets logged.
|
||||
zapV := -zapcore.Level(v)
|
||||
|
||||
@ -85,9 +84,9 @@ func epochMillisTimeEncoder(_ time.Time, enc zapcore.PrimitiveArrayEncoder) {
|
||||
// Factory produces JSON logger instances.
|
||||
type Factory struct{}
|
||||
|
||||
var _ registry.LogFormatFactory = Factory{}
|
||||
var _ logsapi.LogFormatFactory = Factory{}
|
||||
|
||||
func (f Factory) Create(c config.LoggingConfiguration) (logr.Logger, func()) {
|
||||
func (f Factory) Create(c logsapi.LoggingConfiguration) (logr.Logger, func()) {
|
||||
// We intentionally avoid all os.File.Sync calls. Output is unbuffered,
|
||||
// therefore we don't need to flush, and calling the underlying fsync
|
||||
// would just slow down writing.
|
||||
|
@ -24,10 +24,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/component-base/config"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
)
|
||||
|
||||
// TestZapLoggerInfo test ZapLogger json info format
|
||||
@ -112,7 +112,7 @@ func TestZapLoggerInfo(t *testing.T) {
|
||||
// TestZapLoggerEnabled test ZapLogger enabled
|
||||
func TestZapLoggerEnabled(t *testing.T) {
|
||||
verbosityLevel := 10
|
||||
sampleInfoLogger, _ := NewJSONLogger(config.VerbosityLevel(verbosityLevel), nil, nil, nil)
|
||||
sampleInfoLogger, _ := NewJSONLogger(logsapi.VerbosityLevel(verbosityLevel), nil, nil, nil)
|
||||
for v := 0; v <= verbosityLevel; v++ {
|
||||
enabled := sampleInfoLogger.V(v).Enabled()
|
||||
expectEnabled := v <= verbosityLevel
|
||||
@ -135,7 +135,7 @@ func TestZapLoggerV(t *testing.T) {
|
||||
for v := 0; v <= verbosityLevel; v++ {
|
||||
var buffer bytes.Buffer
|
||||
writer := zapcore.AddSync(&buffer)
|
||||
sampleInfoLogger, _ := NewJSONLogger(config.VerbosityLevel(verbosityLevel), writer, nil, nil)
|
||||
sampleInfoLogger, _ := NewJSONLogger(logsapi.VerbosityLevel(verbosityLevel), writer, nil, nil)
|
||||
sampleInfoLogger.V(v).Info("test", "ns", "default", "podnum", 2, "time", time.Microsecond)
|
||||
logStr := buffer.String()
|
||||
|
||||
|
@ -28,7 +28,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
"k8s.io/component-base/config"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
@ -241,7 +241,7 @@ func TestKlogIntegration(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
writer := zapcore.AddSync(&buffer)
|
||||
// This level is high enough to enable all log messages from this test.
|
||||
verbosityLevel := config.VerbosityLevel(100)
|
||||
verbosityLevel := logsapi.VerbosityLevel(100)
|
||||
logger, _ := NewJSONLogger(verbosityLevel, writer, nil, nil)
|
||||
klog.SetLogger(logger)
|
||||
defer klog.ClearLogger()
|
||||
|
@ -17,12 +17,13 @@ limitations under the License.
|
||||
package register
|
||||
|
||||
import (
|
||||
"k8s.io/component-base/logs"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
json "k8s.io/component-base/logs/json"
|
||||
"k8s.io/component-base/logs/registry"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// JSON format is optional klog format
|
||||
registry.LogRegistry.Register(logs.JSONLogFormat, json.Factory{})
|
||||
if err := logsapi.RegisterLogFormat(logsapi.JSONLogFormat, json.Factory{}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
@ -26,15 +26,15 @@ import (
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/component-base/featuregate"
|
||||
"k8s.io/component-base/logs"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
func TestJSONFlag(t *testing.T) {
|
||||
o := logs.NewOptions()
|
||||
c := logsapi.NewLoggingConfiguration()
|
||||
fs := pflag.NewFlagSet("addflagstest", pflag.ContinueOnError)
|
||||
output := bytes.Buffer{}
|
||||
o.AddFlags(fs)
|
||||
c.AddFlags(fs)
|
||||
fs.SetOutput(&output)
|
||||
fs.PrintDefaults()
|
||||
wantSubstring := `Permitted formats: "json", "text".`
|
||||
@ -44,41 +44,41 @@ func TestJSONFlag(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestJSONFormatRegister(t *testing.T) {
|
||||
newOptions := logs.NewOptions()
|
||||
config := logsapi.NewLoggingConfiguration()
|
||||
klogr := klog.Background()
|
||||
testcases := []struct {
|
||||
name string
|
||||
args []string
|
||||
contextualLogging bool
|
||||
want *logs.Options
|
||||
want *logsapi.LoggingConfiguration
|
||||
errs field.ErrorList
|
||||
}{
|
||||
{
|
||||
name: "JSON log format",
|
||||
args: []string{"--logging-format=json"},
|
||||
want: func() *logs.Options {
|
||||
c := newOptions.Config.DeepCopy()
|
||||
c.Format = logs.JSONLogFormat
|
||||
return &logs.Options{*c}
|
||||
want: func() *logsapi.LoggingConfiguration {
|
||||
c := config.DeepCopy()
|
||||
c.Format = logsapi.JSONLogFormat
|
||||
return c
|
||||
}(),
|
||||
},
|
||||
{
|
||||
name: "JSON direct",
|
||||
args: []string{"--logging-format=json"},
|
||||
contextualLogging: true,
|
||||
want: func() *logs.Options {
|
||||
c := newOptions.Config.DeepCopy()
|
||||
c.Format = logs.JSONLogFormat
|
||||
return &logs.Options{*c}
|
||||
want: func() *logsapi.LoggingConfiguration {
|
||||
c := config.DeepCopy()
|
||||
c.Format = logsapi.JSONLogFormat
|
||||
return c
|
||||
}(),
|
||||
},
|
||||
{
|
||||
name: "Unsupported log format",
|
||||
args: []string{"--logging-format=test"},
|
||||
want: func() *logs.Options {
|
||||
c := newOptions.Config.DeepCopy()
|
||||
want: func() *logsapi.LoggingConfiguration {
|
||||
c := config.DeepCopy()
|
||||
c.Format = "test"
|
||||
return &logs.Options{*c}
|
||||
return c
|
||||
}(),
|
||||
errs: field.ErrorList{&field.Error{
|
||||
Type: "FieldValueInvalid",
|
||||
@ -91,18 +91,18 @@ func TestJSONFormatRegister(t *testing.T) {
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
o := logs.NewOptions()
|
||||
c := logsapi.NewLoggingConfiguration()
|
||||
fs := pflag.NewFlagSet("addflagstest", pflag.ContinueOnError)
|
||||
o.AddFlags(fs)
|
||||
c.AddFlags(fs)
|
||||
fs.Parse(tc.args)
|
||||
if !assert.Equal(t, tc.want, o) {
|
||||
t.Errorf("Wrong Validate() result for %q. expect %v, got %v", tc.name, tc.want, o)
|
||||
if !assert.Equal(t, tc.want, c) {
|
||||
t.Errorf("Wrong Validate() result for %q. expect %v, got %v", tc.name, tc.want, c)
|
||||
}
|
||||
featureGate := featuregate.NewFeatureGate()
|
||||
logs.AddFeatureGates(featureGate)
|
||||
err := featureGate.SetFromMap(map[string]bool{string(logs.ContextualLogging): tc.contextualLogging})
|
||||
logsapi.AddFeatureGates(featureGate)
|
||||
err := featureGate.SetFromMap(map[string]bool{string(logsapi.ContextualLogging): tc.contextualLogging})
|
||||
require.NoError(t, err)
|
||||
errs := o.ValidateAndApply(featureGate)
|
||||
errs := c.ValidateAndApply(featureGate)
|
||||
defer klog.ClearLogger()
|
||||
if !assert.ElementsMatch(t, tc.errs, errs) {
|
||||
t.Errorf("Wrong Validate() result for %q.\n expect:\t%+v\n got:\t%+v", tc.name, tc.errs, errs)
|
||||
|
@ -26,10 +26,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
const logFlushFreqFlagName = "log-flush-frequency"
|
||||
const deprecated = "will be removed in a future release, see https://github.com/kubernetes/enhancements/tree/master/keps/sig-instrumentation/2845-deprecate-klog-specific-flags-in-k8s-components"
|
||||
|
||||
// TODO (https://github.com/kubernetes/kubernetes/issues/105310): once klog
|
||||
@ -48,7 +48,7 @@ var (
|
||||
|
||||
func init() {
|
||||
klog.InitFlags(packageFlags)
|
||||
packageFlags.DurationVar(&logFlushFreq, logFlushFreqFlagName, 5*time.Second, "Maximum number of seconds between log flushes")
|
||||
packageFlags.DurationVar(&logFlushFreq, logsapi.LogFlushFreqFlagName, logsapi.LogFlushFreqDefault, "Maximum number of seconds between log flushes")
|
||||
}
|
||||
|
||||
type addFlagsOptions struct {
|
||||
@ -66,6 +66,13 @@ func SkipLoggingConfigurationFlags() Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Options is an alias for LoggingConfiguration to comply with component-base
|
||||
// conventions.
|
||||
type Options = logsapi.LoggingConfiguration
|
||||
|
||||
// NewOptions is an alias for NewLoggingConfiguration.
|
||||
var NewOptions = logsapi.NewLoggingConfiguration
|
||||
|
||||
// AddFlags registers this package's flags on arbitrary FlagSets. This includes
|
||||
// the klog flags, with the original underscore as separator between. If
|
||||
// commands want hyphens as separators, they can set
|
||||
@ -94,7 +101,7 @@ func AddFlags(fs *pflag.FlagSet, opts ...Option) {
|
||||
if o.skipLoggingConfigurationFlags {
|
||||
return
|
||||
}
|
||||
case logFlushFreqFlagName:
|
||||
case logsapi.LogFlushFreqFlagName:
|
||||
// unchanged, potentially skip it
|
||||
if o.skipLoggingConfigurationFlags {
|
||||
return
|
||||
@ -135,7 +142,7 @@ func AddGoFlags(fs *flag.FlagSet, opts ...Option) {
|
||||
if o.skipLoggingConfigurationFlags {
|
||||
return
|
||||
}
|
||||
case logFlushFreqFlagName:
|
||||
case logsapi.LogFlushFreqFlagName:
|
||||
// unchanged
|
||||
if o.skipLoggingConfigurationFlags {
|
||||
return
|
||||
|
@ -17,7 +17,6 @@ require (
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
|
@ -28,7 +28,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
|
@ -17,7 +17,6 @@ require (
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
|
1
staging/src/k8s.io/kube-proxy/go.sum
generated
1
staging/src/k8s.io/kube-proxy/go.sum
generated
@ -29,7 +29,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
|
@ -19,7 +19,6 @@ require (
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
|
1
staging/src/k8s.io/kube-scheduler/go.sum
generated
1
staging/src/k8s.io/kube-scheduler/go.sum
generated
@ -29,7 +29,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
|
@ -19,7 +19,7 @@ package v1beta1
|
||||
import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
componentbaseconfigv1alpha1 "k8s.io/component-base/config/v1alpha1"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
)
|
||||
|
||||
// HairpinMode denotes how the kubelet should configure networking to handle
|
||||
@ -676,7 +676,7 @@ type KubeletConfiguration struct {
|
||||
// Default:
|
||||
// Format: text
|
||||
// + optional
|
||||
Logging componentbaseconfigv1alpha1.LoggingConfiguration `json:"logging,omitempty"`
|
||||
Logging logsapi.LoggingConfiguration `json:"logging,omitempty"`
|
||||
// enableSystemLogHandler enables system logs via web interface host:port/logs/
|
||||
// Default: true
|
||||
// +optional
|
||||
|
@ -18,9 +18,11 @@ require (
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/go-cmp v0.5.6 // indirect
|
||||
github.com/google/gofuzz v1.1.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/spf13/cobra v1.4.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
|
6
staging/src/k8s.io/kubelet/go.sum
generated
6
staging/src/k8s.io/kubelet/go.sum
generated
@ -12,6 +12,7 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -58,6 +59,8 @@ github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
@ -75,7 +78,10 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
|
||||
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
|
@ -33,7 +33,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"k8s.io/component-base/logs"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
logsjson "k8s.io/component-base/logs/json"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
@ -175,7 +175,7 @@ func benchmarkOutputFormats(b *testing.B, config loadGeneratorConfig, discard bo
|
||||
generateOutput(b, config, nil, out)
|
||||
})
|
||||
b.Run("JSON", func(b *testing.B) {
|
||||
options := logs.NewOptions()
|
||||
c := logsapi.NewLoggingConfiguration()
|
||||
var logger logr.Logger
|
||||
var flush func()
|
||||
var out1, out2 *os.File
|
||||
@ -194,14 +194,14 @@ func benchmarkOutputFormats(b *testing.B, config loadGeneratorConfig, discard bo
|
||||
}
|
||||
b.Run("single-stream", func(b *testing.B) {
|
||||
if discard {
|
||||
logger, flush = logsjson.NewJSONLogger(options.Config.Verbosity, logsjson.AddNopSync(&output), nil, nil)
|
||||
logger, flush = logsjson.NewJSONLogger(c.Verbosity, logsjson.AddNopSync(&output), nil, nil)
|
||||
} else {
|
||||
stderr := os.Stderr
|
||||
os.Stderr = out1
|
||||
defer func() {
|
||||
os.Stderr = stderr
|
||||
}()
|
||||
logger, flush = logsjson.Factory{}.Create(options.Config)
|
||||
logger, flush = logsjson.Factory{}.Create(*c)
|
||||
}
|
||||
klog.SetLogger(logger)
|
||||
defer klog.ClearLogger()
|
||||
@ -210,16 +210,16 @@ func benchmarkOutputFormats(b *testing.B, config loadGeneratorConfig, discard bo
|
||||
|
||||
b.Run("split-stream", func(b *testing.B) {
|
||||
if discard {
|
||||
logger, flush = logsjson.NewJSONLogger(options.Config.Verbosity, logsjson.AddNopSync(&output), logsjson.AddNopSync(&output), nil)
|
||||
logger, flush = logsjson.NewJSONLogger(c.Verbosity, logsjson.AddNopSync(&output), logsjson.AddNopSync(&output), nil)
|
||||
} else {
|
||||
stdout, stderr := os.Stdout, os.Stderr
|
||||
os.Stdout, os.Stderr = out1, out2
|
||||
defer func() {
|
||||
os.Stdout, os.Stderr = stdout, stderr
|
||||
}()
|
||||
options := logs.NewOptions()
|
||||
options.Config.Options.JSON.SplitStream = true
|
||||
logger, flush = logsjson.Factory{}.Create(options.Config)
|
||||
c := logsapi.NewLoggingConfiguration()
|
||||
c.Options.JSON.SplitStream = true
|
||||
logger, flush = logsjson.Factory{}.Create(*c)
|
||||
}
|
||||
klog.SetLogger(logger)
|
||||
defer klog.ClearLogger()
|
||||
|
@ -23,7 +23,7 @@ import (
|
||||
"github.com/go-logr/logr"
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
"k8s.io/component-base/logs"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
logsjson "k8s.io/component-base/logs/json"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
@ -61,8 +61,8 @@ func newJSONLogger(out io.Writer) logr.Logger {
|
||||
encoderConfig := &zapcore.EncoderConfig{
|
||||
MessageKey: "msg",
|
||||
}
|
||||
options := logs.NewOptions()
|
||||
logger, _ := logsjson.NewJSONLogger(options.Config.Verbosity, zapcore.AddSync(out), nil, encoderConfig)
|
||||
c := logsapi.NewLoggingConfiguration()
|
||||
logger, _ := logsjson.NewJSONLogger(c.Verbosity, zapcore.AddSync(out), nil, encoderConfig)
|
||||
return logger
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ import (
|
||||
"github.com/go-logr/logr"
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
"k8s.io/component-base/config"
|
||||
logsapi "k8s.io/component-base/logs/api/v1"
|
||||
logsjson "k8s.io/component-base/logs/json"
|
||||
"k8s.io/klog/v2/test"
|
||||
)
|
||||
@ -37,7 +37,7 @@ func init() {
|
||||
// TestJsonOutput tests the JSON logger, directly and as backend for klog.
|
||||
func TestJSONOutput(t *testing.T) {
|
||||
newLogger := func(out io.Writer, v int, vmodule string) logr.Logger {
|
||||
logger, _ := logsjson.NewJSONLogger(config.VerbosityLevel(v), logsjson.AddNopSync(out), nil,
|
||||
logger, _ := logsjson.NewJSONLogger(logsapi.VerbosityLevel(v), logsjson.AddNopSync(out), nil,
|
||||
&zapcore.EncoderConfig{
|
||||
MessageKey: "msg",
|
||||
CallerKey: "caller",
|
||||
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@ -2063,10 +2063,10 @@ k8s.io/component-base/configz
|
||||
k8s.io/component-base/featuregate
|
||||
k8s.io/component-base/featuregate/testing
|
||||
k8s.io/component-base/logs
|
||||
k8s.io/component-base/logs/api/v1
|
||||
k8s.io/component-base/logs/json
|
||||
k8s.io/component-base/logs/json/register
|
||||
k8s.io/component-base/logs/logreduction
|
||||
k8s.io/component-base/logs/registry
|
||||
k8s.io/component-base/logs/testinit
|
||||
k8s.io/component-base/metrics
|
||||
k8s.io/component-base/metrics/legacyregistry
|
||||
|
Loading…
Reference in New Issue
Block a user