diff --git a/pkg/kubelet/apis/config/helpers_test.go b/pkg/kubelet/apis/config/helpers_test.go index b30613da572..85bcaaed388 100644 --- a/pkg/kubelet/apis/config/helpers_test.go +++ b/pkg/kubelet/apis/config/helpers_test.go @@ -188,6 +188,14 @@ var ( "HealthzBindAddress", "HealthzPort", "Logging.Format", + "Logging.Options.JSON.InfoBufferSize.Quantity.Format", + "Logging.Options.JSON.InfoBufferSize.Quantity.d.Dec.scale", + "Logging.Options.JSON.InfoBufferSize.Quantity.d.Dec.unscaled.abs[*]", + "Logging.Options.JSON.InfoBufferSize.Quantity.d.Dec.unscaled.neg", + "Logging.Options.JSON.InfoBufferSize.Quantity.i.scale", + "Logging.Options.JSON.InfoBufferSize.Quantity.i.value", + "Logging.Options.JSON.InfoBufferSize.Quantity.s", + "Logging.Options.JSON.SplitStream", "Logging.Sanitization", "TLSCipherSuites[*]", "TLSMinVersion", diff --git a/pkg/kubelet/apis/config/scheme/testdata/KubeletConfiguration/after/v1beta1.yaml b/pkg/kubelet/apis/config/scheme/testdata/KubeletConfiguration/after/v1beta1.yaml index 9f339642f9a..60104968075 100644 --- a/pkg/kubelet/apis/config/scheme/testdata/KubeletConfiguration/after/v1beta1.yaml +++ b/pkg/kubelet/apis/config/scheme/testdata/KubeletConfiguration/after/v1beta1.yaml @@ -54,6 +54,9 @@ kubeAPIBurst: 10 kubeAPIQPS: 5 logging: format: text + options: + json: + infoBufferSize: "0" makeIPTablesUtilChains: true maxOpenFiles: 1000000 maxPods: 110 diff --git a/pkg/kubelet/apis/config/scheme/testdata/KubeletConfiguration/roundtrip/default/v1beta1.yaml b/pkg/kubelet/apis/config/scheme/testdata/KubeletConfiguration/roundtrip/default/v1beta1.yaml index 9f339642f9a..60104968075 100644 --- a/pkg/kubelet/apis/config/scheme/testdata/KubeletConfiguration/roundtrip/default/v1beta1.yaml +++ b/pkg/kubelet/apis/config/scheme/testdata/KubeletConfiguration/roundtrip/default/v1beta1.yaml @@ -54,6 +54,9 @@ kubeAPIBurst: 10 kubeAPIQPS: 5 logging: format: text + options: + json: + infoBufferSize: "0" makeIPTablesUtilChains: true maxOpenFiles: 1000000 maxPods: 110 diff --git a/pkg/kubelet/apis/config/zz_generated.deepcopy.go b/pkg/kubelet/apis/config/zz_generated.deepcopy.go index 5316a3efd0c..82d958c6447 100644 --- a/pkg/kubelet/apis/config/zz_generated.deepcopy.go +++ b/pkg/kubelet/apis/config/zz_generated.deepcopy.go @@ -280,7 +280,7 @@ func (in *KubeletConfiguration) DeepCopyInto(out *KubeletConfiguration) { *out = make([]string, len(*in)) copy(*out, *in) } - out.Logging = in.Logging + in.Logging.DeepCopyInto(&out.Logging) out.ShutdownGracePeriod = in.ShutdownGracePeriod out.ShutdownGracePeriodCriticalPods = in.ShutdownGracePeriodCriticalPods if in.ReservedMemory != nil { diff --git a/staging/src/k8s.io/apimachinery/pkg/api/resource/generated.pb.go b/staging/src/k8s.io/apimachinery/pkg/api/resource/generated.pb.go index 2e09f4face7..172db57fac1 100644 --- a/staging/src/k8s.io/apimachinery/pkg/api/resource/generated.pb.go +++ b/staging/src/k8s.io/apimachinery/pkg/api/resource/generated.pb.go @@ -61,8 +61,32 @@ func (m *Quantity) XXX_DiscardUnknown() { var xxx_messageInfo_Quantity proto.InternalMessageInfo +func (m *QuantityValue) Reset() { *m = QuantityValue{} } +func (*QuantityValue) ProtoMessage() {} +func (*QuantityValue) Descriptor() ([]byte, []int) { + return fileDescriptor_612bba87bd70906c, []int{1} +} +func (m *QuantityValue) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_QuantityValue.Unmarshal(m, b) +} +func (m *QuantityValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_QuantityValue.Marshal(b, m, deterministic) +} +func (m *QuantityValue) XXX_Merge(src proto.Message) { + xxx_messageInfo_QuantityValue.Merge(m, src) +} +func (m *QuantityValue) XXX_Size() int { + return xxx_messageInfo_QuantityValue.Size(m) +} +func (m *QuantityValue) XXX_DiscardUnknown() { + xxx_messageInfo_QuantityValue.DiscardUnknown(m) +} + +var xxx_messageInfo_QuantityValue proto.InternalMessageInfo + func init() { proto.RegisterType((*Quantity)(nil), "k8s.io.apimachinery.pkg.api.resource.Quantity") + proto.RegisterType((*QuantityValue)(nil), "k8s.io.apimachinery.pkg.api.resource.QuantityValue") } func init() { @@ -70,20 +94,21 @@ func init() { } var fileDescriptor_612bba87bd70906c = []byte{ - // 237 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x8e, 0xb1, 0x4e, 0xc3, 0x30, - 0x10, 0x40, 0xcf, 0x0b, 0x2a, 0x19, 0x2b, 0x84, 0x10, 0xc3, 0xa5, 0x42, 0x0c, 0x2c, 0xd8, 0x6b, - 0xc5, 0xc8, 0xce, 0x00, 0x23, 0x5b, 0x92, 0x1e, 0xae, 0x15, 0xd5, 0x8e, 0x2e, 0x36, 0x52, 0xb7, - 0x8e, 0x8c, 0x1d, 0x19, 0x9b, 0xbf, 0xe9, 0xd8, 0xb1, 0x03, 0x03, 0x31, 0x3f, 0x82, 0xea, 0x36, - 0x52, 0xb7, 0x7b, 0xef, 0xf4, 0x4e, 0x97, 0xbd, 0xd4, 0xd3, 0x56, 0x1a, 0xa7, 0xea, 0x50, 0x12, - 0x5b, 0xf2, 0xd4, 0xaa, 0x4f, 0xb2, 0x33, 0xc7, 0xea, 0xb4, 0x28, 0x1a, 0xb3, 0x28, 0xaa, 0xb9, - 0xb1, 0xc4, 0x4b, 0xd5, 0xd4, 0xfa, 0x20, 0x14, 0x53, 0xeb, 0x02, 0x57, 0xa4, 0x34, 0x59, 0xe2, - 0xc2, 0xd3, 0x4c, 0x36, 0xec, 0xbc, 0x1b, 0xdf, 0x1f, 0x2b, 0x79, 0x5e, 0xc9, 0xa6, 0xd6, 0x07, - 0x21, 0x87, 0xea, 0xf6, 0x51, 0x1b, 0x3f, 0x0f, 0xa5, 0xac, 0xdc, 0x42, 0x69, 0xa7, 0x9d, 0x4a, - 0x71, 0x19, 0x3e, 0x12, 0x25, 0x48, 0xd3, 0xf1, 0xe8, 0xdd, 0x34, 0x1b, 0xbd, 0x86, 0xc2, 0x7a, - 0xe3, 0x97, 0xe3, 0xeb, 0xec, 0xa2, 0xf5, 0x6c, 0xac, 0xbe, 0x11, 0x13, 0xf1, 0x70, 0xf9, 0x76, - 0xa2, 0xa7, 0xab, 0xef, 0x4d, 0x0e, 0x5f, 0x5d, 0x0e, 0xeb, 0x2e, 0x87, 0x4d, 0x97, 0xc3, 0xea, - 0x67, 0x02, 0xcf, 0x72, 0xdb, 0x23, 0xec, 0x7a, 0x84, 0x7d, 0x8f, 0xb0, 0x8a, 0x28, 0xb6, 0x11, - 0xc5, 0x2e, 0xa2, 0xd8, 0x47, 0x14, 0xbf, 0x11, 0xc5, 0xfa, 0x0f, 0xe1, 0x7d, 0x34, 0x3c, 0xf6, - 0x1f, 0x00, 0x00, 0xff, 0xff, 0x3c, 0x08, 0x88, 0x49, 0x0e, 0x01, 0x00, 0x00, + // 254 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xf2, 0xcd, 0xb6, 0x28, 0xd6, + 0xcb, 0xcc, 0xd7, 0xcf, 0x2e, 0x4d, 0x4a, 0x2d, 0xca, 0x4b, 0x2d, 0x49, 0x2d, 0xd6, 0x2f, 0x4b, + 0xcd, 0x4b, 0xc9, 0x2f, 0xd2, 0x87, 0x4a, 0x24, 0x16, 0x64, 0xe6, 0x26, 0x26, 0x67, 0x64, 0xe6, + 0xa5, 0x16, 0x55, 0xea, 0x17, 0x64, 0xa7, 0x83, 0x04, 0xf4, 0x8b, 0x52, 0x8b, 0xf3, 0x4b, 0x8b, + 0x92, 0x53, 0xf5, 0xd3, 0x53, 0xf3, 0x52, 0x8b, 0x12, 0x4b, 0x52, 0x53, 0xf4, 0x0a, 0x8a, 0xf2, + 0x4b, 0xf2, 0x85, 0x54, 0x20, 0xba, 0xf4, 0x90, 0x75, 0xe9, 0x15, 0x64, 0xa7, 0x83, 0x04, 0xf4, + 0x60, 0xba, 0xa4, 0x74, 0xd3, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0xd3, + 0xf3, 0xd3, 0xf3, 0xf5, 0xc1, 0x9a, 0x93, 0x4a, 0xd3, 0xc0, 0x3c, 0x30, 0x07, 0xcc, 0x82, 0x18, + 0xaa, 0x64, 0xc1, 0xc5, 0x11, 0x58, 0x9a, 0x98, 0x57, 0x92, 0x59, 0x52, 0x29, 0x24, 0xc6, 0xc5, + 0x56, 0x5c, 0x52, 0x94, 0x99, 0x97, 0x2e, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x19, 0x04, 0xe5, 0x59, + 0x89, 0xcc, 0x58, 0x20, 0xcf, 0xd0, 0xb1, 0x50, 0x9e, 0x61, 0xc2, 0x42, 0x79, 0x86, 0x05, 0x0b, + 0xe5, 0x19, 0x1a, 0xee, 0x28, 0x30, 0x28, 0xd9, 0x72, 0xf1, 0xc2, 0x74, 0x86, 0x25, 0xe6, 0x94, + 0xa6, 0x92, 0xa6, 0xdd, 0x49, 0xef, 0xc4, 0x43, 0x39, 0x86, 0x0b, 0x0f, 0xe5, 0x18, 0x6e, 0x3c, + 0x94, 0x63, 0x68, 0x78, 0x24, 0xc7, 0x78, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x37, + 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0x43, 0x14, 0x07, 0xcc, 0x5f, + 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x21, 0x76, 0x9f, 0x66, 0x4d, 0x01, 0x00, 0x00, } diff --git a/staging/src/k8s.io/apimachinery/pkg/api/resource/generated.proto b/staging/src/k8s.io/apimachinery/pkg/api/resource/generated.proto index 472104d5429..54240b7b5f2 100644 --- a/staging/src/k8s.io/apimachinery/pkg/api/resource/generated.proto +++ b/staging/src/k8s.io/apimachinery/pkg/api/resource/generated.proto @@ -86,3 +86,15 @@ message Quantity { optional string string = 1; } +// QuantityValue makes it possible to use a Quantity as value for a command +// line parameter. +// +// +protobuf=true +// +protobuf.embed=string +// +protobuf.options.marshal=false +// +protobuf.options.(gogoproto.goproto_stringer)=false +// +k8s:deepcopy-gen=true +message QuantityValue { + optional string string = 1; +} + diff --git a/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go b/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go index 000f96eb9d1..6d43868ba80 100644 --- a/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go +++ b/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go @@ -764,3 +764,30 @@ func (q *Quantity) SetScaled(value int64, scale Scale) { q.d.Dec = nil q.i = int64Amount{value: value, scale: scale} } + +// QuantityValue makes it possible to use a Quantity as value for a command +// line parameter. +// +// +protobuf=true +// +protobuf.embed=string +// +protobuf.options.marshal=false +// +protobuf.options.(gogoproto.goproto_stringer)=false +// +k8s:deepcopy-gen=true +type QuantityValue struct { + Quantity +} + +// Set implements pflag.Value.Set and Go flag.Value.Set. +func (q *QuantityValue) Set(s string) error { + quantity, err := ParseQuantity(s) + if err != nil { + return err + } + q.Quantity = quantity + return nil +} + +// Type implements pflag.Value.Type. +func (q QuantityValue) Type() string { + return "quantity" +} diff --git a/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity_test.go b/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity_test.go index 3442689c45c..320477358c8 100644 --- a/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity_test.go @@ -21,11 +21,13 @@ import ( "fmt" "math" "math/rand" + "os" "strings" "testing" "unicode" fuzz "github.com/google/gofuzz" + "github.com/spf13/pflag" inf "gopkg.in/inf.v0" ) @@ -1475,3 +1477,42 @@ func BenchmarkQuantityAsApproximateFloat64(b *testing.B) { } b.StopTimer() } + +var _ pflag.Value = &QuantityValue{} + +func TestQuantityValueSet(t *testing.T) { + q := QuantityValue{} + + if err := q.Set("invalid"); err == nil { + + t.Error("'invalid' did not trigger a parse error") + } + + if err := q.Set("1Mi"); err != nil { + t.Errorf("parsing 1Mi should have worked, got: %v", err) + } + if q.Value() != 1024*1024 { + t.Errorf("quantity should have been set to 1Mi, got: %v", q) + } + + data, err := json.Marshal(q) + if err != nil { + t.Errorf("unexpected encoding error: %v", err) + } + expected := `"1Mi"` + if string(data) != expected { + t.Errorf("expected 1Mi value to be encoded as %q, got: %q", expected, string(data)) + } +} + +func ExampleQuantityValue() { + q := QuantityValue{ + Quantity: MustParse("1Mi"), + } + fs := pflag.FlagSet{} + fs.SetOutput(os.Stdout) + fs.Var(&q, "mem", "sets amount of memory") + fs.PrintDefaults() + // Output: + // --mem quantity sets amount of memory (default 1Mi) +} diff --git a/staging/src/k8s.io/apimachinery/pkg/api/resource/zz_generated.deepcopy.go b/staging/src/k8s.io/apimachinery/pkg/api/resource/zz_generated.deepcopy.go index 5bb530eb840..abb00f38e22 100644 --- a/staging/src/k8s.io/apimachinery/pkg/api/resource/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/apimachinery/pkg/api/resource/zz_generated.deepcopy.go @@ -26,3 +26,20 @@ func (in *Quantity) DeepCopyInto(out *Quantity) { *out = in.DeepCopy() return } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *QuantityValue) DeepCopyInto(out *QuantityValue) { + *out = *in + out.Quantity = in.Quantity.DeepCopy() + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QuantityValue. +func (in *QuantityValue) DeepCopy() *QuantityValue { + if in == nil { + return nil + } + out := new(QuantityValue) + in.DeepCopyInto(out) + return out +} diff --git a/staging/src/k8s.io/component-base/config/types.go b/staging/src/k8s.io/component-base/config/types.go index c6cd112af13..7f42aa2b318 100644 --- a/staging/src/k8s.io/component-base/config/types.go +++ b/staging/src/k8s.io/component-base/config/types.go @@ -17,6 +17,7 @@ limitations under the License. package config import ( + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -88,4 +89,25 @@ type LoggingConfiguration struct { // [Experimental] When enabled prevents logging of fields tagged as sensitive (passwords, keys, tokens). // Runtime log sanitization may introduce significant computation overhead and therefore should not be enabled in production.`) Sanitization bool + // [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 } diff --git a/staging/src/k8s.io/component-base/config/v1alpha1/defaults.go b/staging/src/k8s.io/component-base/config/v1alpha1/defaults.go index 098c5739d38..e37d14114e4 100644 --- a/staging/src/k8s.io/component-base/config/v1alpha1/defaults.go +++ b/staging/src/k8s.io/component-base/config/v1alpha1/defaults.go @@ -19,6 +19,7 @@ package v1alpha1 import ( "time" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilpointer "k8s.io/utils/pointer" ) @@ -110,4 +111,15 @@ 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() + } } diff --git a/staging/src/k8s.io/component-base/config/v1alpha1/types.go b/staging/src/k8s.io/component-base/config/v1alpha1/types.go index cd56c1fcfc6..4bdc4622d9a 100644 --- a/staging/src/k8s.io/component-base/config/v1alpha1/types.go +++ b/staging/src/k8s.io/component-base/config/v1alpha1/types.go @@ -17,6 +17,7 @@ limitations under the License. package v1alpha1 import ( + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -90,4 +91,25 @@ type LoggingConfiguration struct { // [Experimental] When enabled prevents logging of fields tagged as sensitive (passwords, keys, tokens). // Runtime log sanitization may introduce significant computation overhead and therefore should not be enabled in production.`) Sanitization bool `json:"sanitization,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"` } diff --git a/staging/src/k8s.io/component-base/config/v1alpha1/zz_generated.conversion.go b/staging/src/k8s.io/component-base/config/v1alpha1/zz_generated.conversion.go index ff820e63e23..dac5a9d8808 100644 --- a/staging/src/k8s.io/component-base/config/v1alpha1/zz_generated.conversion.go +++ b/staging/src/k8s.io/component-base/config/v1alpha1/zz_generated.conversion.go @@ -35,6 +35,26 @@ 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.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 { @@ -116,6 +136,52 @@ 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 @@ -145,11 +211,17 @@ func autoConvert_config_LeaderElectionConfiguration_To_v1alpha1_LeaderElectionCo func autoConvert_v1alpha1_LoggingConfiguration_To_config_LoggingConfiguration(in *LoggingConfiguration, out *config.LoggingConfiguration, s conversion.Scope) error { out.Format = in.Format out.Sanitization = in.Sanitization + 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.Sanitization = in.Sanitization + if err := Convert_config_FormatOptions_To_v1alpha1_FormatOptions(&in.Options, &out.Options, s); err != nil { + return err + } return nil } diff --git a/staging/src/k8s.io/component-base/config/v1alpha1/zz_generated.deepcopy.go b/staging/src/k8s.io/component-base/config/v1alpha1/zz_generated.deepcopy.go index 909c49b1124..3542d2561f1 100644 --- a/staging/src/k8s.io/component-base/config/v1alpha1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/component-base/config/v1alpha1/zz_generated.deepcopy.go @@ -63,6 +63,40 @@ 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 @@ -90,6 +124,7 @@ func (in *LeaderElectionConfiguration) DeepCopy() *LeaderElectionConfiguration { // 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 + in.Options.DeepCopyInto(&out.Options) return } diff --git a/staging/src/k8s.io/component-base/config/zz_generated.deepcopy.go b/staging/src/k8s.io/component-base/config/zz_generated.deepcopy.go index b592ded0089..e18b9d38a0f 100644 --- a/staging/src/k8s.io/component-base/config/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/component-base/config/zz_generated.deepcopy.go @@ -53,6 +53,40 @@ 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 @@ -75,6 +109,7 @@ func (in *LeaderElectionConfiguration) DeepCopy() *LeaderElectionConfiguration { // 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 + in.Options.DeepCopyInto(&out.Options) return } diff --git a/staging/src/k8s.io/component-base/logs/config.go b/staging/src/k8s.io/component-base/logs/config.go index c24bf19db59..6bc3a018527 100644 --- a/staging/src/k8s.io/component-base/logs/config.go +++ b/staging/src/k8s.io/component-base/logs/config.go @@ -67,6 +67,13 @@ func BindLoggingFlags(c *config.LoggingConfiguration, fs *pflag.FlagSet) { registry.LogRegistry.Freeze() fs.BoolVar(&c.Sanitization, "experimental-logging-sanitization", c.Sanitization, `[Experimental] When enabled prevents logging of fields tagged as sensitive (passwords, keys, tokens). Runtime log sanitization may introduce significant computation overhead and therefore should not be enabled in production.`) + + // 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 diff --git a/staging/src/k8s.io/component-base/logs/json/json.go b/staging/src/k8s.io/component-base/logs/json/json.go index 03e29f66111..37983a1ba05 100644 --- a/staging/src/k8s.io/component-base/logs/json/json.go +++ b/staging/src/k8s.io/component-base/logs/json/json.go @@ -25,6 +25,7 @@ import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" + "k8s.io/component-base/config" "k8s.io/component-base/logs/registry" ) @@ -33,15 +34,41 @@ var ( timeNow = time.Now ) -// NewJSONLogger creates a new json logr.Logger using the given Zap Logger to log. -func NewJSONLogger(w zapcore.WriteSyncer) logr.Logger { +// NewJSONLogger creates a new json logr.Logger and its associated +// flush function. The separate error stream is optional and may be nil. +func NewJSONLogger(infoStream, errorStream zapcore.WriteSyncer) (logr.Logger, func()) { encoder := zapcore.NewJSONEncoder(encoderConfig) - // The log level intentionally gets set as low as possible to - // ensure that all messages are printed when this logger gets - // called by klog. The actual verbosity check happens in klog. - core := zapcore.NewCore(encoder, zapcore.AddSync(w), zapcore.Level(-127)) + var core zapcore.Core + if errorStream == nil { + core = zapcore.NewCore(encoder, zapcore.AddSync(infoStream), zapcore.Level(-127)) + } else { + // Set up writing of error messages to stderr and info messages + // to stdout. Info messages get optionally buffered and flushed + // - through klog.FlushLogs -> zapr Flush -> zap Sync + // - when an error gets logged + // + // The later is important when both streams get merged into a single + // stream by the consumer (same console for a command line tool, pod + // log for a container) because without it, messages get reordered. + flushError := writeWithFlushing{ + WriteSyncer: errorStream, + other: infoStream, + } + highPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { + return lvl >= zapcore.ErrorLevel + }) + lowPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { + return lvl < zapcore.ErrorLevel + }) + core = zapcore.NewTee( + zapcore.NewCore(encoder, flushError, highPriority), + zapcore.NewCore(encoder, infoStream, lowPriority), + ) + } l := zap.New(core, zap.WithCaller(true)) - return zapr.NewLoggerWithOptions(l, zapr.LogInfoLevel("v"), zapr.ErrorKey("err")) + return zapr.NewLoggerWithOptions(l, zapr.LogInfoLevel("v"), zapr.ErrorKey("err")), func() { + l.Sync() + } } var encoderConfig = zapcore.EncoderConfig{ @@ -62,8 +89,36 @@ func epochMillisTimeEncoder(_ time.Time, enc zapcore.PrimitiveArrayEncoder) { // Factory produces JSON logger instances. type Factory struct{} -func (f Factory) Create() logr.Logger { - return NewJSONLogger(zapcore.Lock(os.Stdout)) +var _ registry.LogFormatFactory = Factory{} + +func (f Factory) Create(options config.FormatOptions) (logr.Logger, func()) { + if options.JSON.SplitStream { + infoStream := zapcore.Lock(os.Stdout) + size := options.JSON.InfoBufferSize.Value() + if size > 0 { + // Prevent integer overflow. + if size > 2*1024*1024*1024 { + size = 2 * 1024 * 1024 * 1024 + } + infoStream = &zapcore.BufferedWriteSyncer{ + WS: infoStream, + Size: int(size), + } + } + return NewJSONLogger(infoStream, zapcore.Lock(os.Stderr)) + } + out := zapcore.Lock(os.Stdout) + return NewJSONLogger(out, out) } -var _ registry.LogFormatFactory = Factory{} +// writeWithFlushing is a wrapper around an output stream which flushes another +// output stream before each write. +type writeWithFlushing struct { + zapcore.WriteSyncer + other zapcore.WriteSyncer +} + +func (f writeWithFlushing) Write(bs []byte) (int, error) { + f.other.Sync() + return f.WriteSyncer.Write(bs) +} diff --git a/staging/src/k8s.io/component-base/logs/json/json_benchmark_test.go b/staging/src/k8s.io/component-base/logs/json/json_benchmark_test.go index 2eeebfcae6f..7d894896bcd 100644 --- a/staging/src/k8s.io/component-base/logs/json/json_benchmark_test.go +++ b/staging/src/k8s.io/component-base/logs/json/json_benchmark_test.go @@ -23,8 +23,10 @@ import ( "go.uber.org/zap/zapcore" ) +var writer = zapcore.AddSync(&writeSyncer{}) + func BenchmarkInfoLoggerInfo(b *testing.B) { - logger := NewJSONLogger(zapcore.AddSync(&writeSyncer{})) + logger, _ := NewJSONLogger(writer, writer) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { @@ -53,7 +55,7 @@ func BenchmarkInfoLoggerInfo(b *testing.B) { } func BenchmarkZapLoggerError(b *testing.B) { - logger := NewJSONLogger(zapcore.AddSync(&writeSyncer{})) + logger, _ := NewJSONLogger(writer, writer) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { @@ -83,7 +85,7 @@ func BenchmarkZapLoggerError(b *testing.B) { } func BenchmarkZapLoggerV(b *testing.B) { - logger := NewJSONLogger(zapcore.AddSync(&writeSyncer{})) + logger, _ := NewJSONLogger(writer, writer) b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { diff --git a/staging/src/k8s.io/component-base/logs/json/json_test.go b/staging/src/k8s.io/component-base/logs/json/json_test.go index 743b19c1c8b..75edaad0986 100644 --- a/staging/src/k8s.io/component-base/logs/json/json_test.go +++ b/staging/src/k8s.io/component-base/logs/json/json_test.go @@ -17,7 +17,6 @@ limitations under the License. package logs import ( - "bufio" "bytes" "fmt" "strings" @@ -64,10 +63,9 @@ func TestZapLoggerInfo(t *testing.T) { for _, data := range testDataInfo { var buffer bytes.Buffer - writer := bufio.NewWriter(&buffer) - var sampleInfoLogger = NewJSONLogger(zapcore.AddSync(writer)) + writer := zapcore.AddSync(&buffer) + sampleInfoLogger, _ := NewJSONLogger(writer, nil) sampleInfoLogger.Info(data.msg, data.keysValues...) - writer.Flush() logStr := buffer.String() logStrLines := strings.Split(logStr, "\n") @@ -96,7 +94,7 @@ func TestZapLoggerInfo(t *testing.T) { // TestZapLoggerEnabled test ZapLogger enabled func TestZapLoggerEnabled(t *testing.T) { - var sampleInfoLogger = NewJSONLogger(nil) + sampleInfoLogger, _ := NewJSONLogger(nil, nil) for i := 0; i < 11; i++ { if !sampleInfoLogger.V(i).Enabled() { t.Errorf("V(%d).Info should be enabled", i) @@ -112,10 +110,9 @@ func TestZapLoggerV(t *testing.T) { for i := 0; i < 11; i++ { var buffer bytes.Buffer - writer := bufio.NewWriter(&buffer) - var sampleInfoLogger = NewJSONLogger(zapcore.AddSync(writer)) + writer := zapcore.AddSync(&buffer) + sampleInfoLogger, _ := NewJSONLogger(writer, nil) sampleInfoLogger.V(i).Info("test", "ns", "default", "podnum", 2, "time", time.Microsecond) - writer.Flush() logStr := buffer.String() var v, lineNo int expectFormat := "{\"ts\":0.000123,\"caller\":\"json/json_test.go:%d\",\"msg\":\"test\",\"v\":%d,\"ns\":\"default\",\"podnum\":2,\"time\":\"1µs\"}\n" @@ -137,13 +134,12 @@ func TestZapLoggerV(t *testing.T) { // TestZapLoggerError test ZapLogger json error format func TestZapLoggerError(t *testing.T) { var buffer bytes.Buffer - writer := bufio.NewWriter(&buffer) + writer := zapcore.AddSync(&buffer) timeNow = func() time.Time { return time.Date(1970, time.January, 1, 0, 0, 0, 123, time.UTC) } - var sampleInfoLogger = NewJSONLogger(zapcore.AddSync(writer)) + sampleInfoLogger, _ := NewJSONLogger(writer, nil) sampleInfoLogger.Error(fmt.Errorf("invalid namespace:%s", "default"), "wrong namespace", "ns", "default", "podnum", 2, "time", time.Microsecond) - writer.Flush() logStr := buffer.String() var ts float64 var lineNo int @@ -158,6 +154,38 @@ func TestZapLoggerError(t *testing.T) { } } +func TestZapLoggerStreams(t *testing.T) { + var infoBuffer, errorBuffer bytes.Buffer + log, _ := NewJSONLogger(zapcore.AddSync(&infoBuffer), zapcore.AddSync(&errorBuffer)) + + log.Error(fmt.Errorf("some error"), "failed") + log.Info("hello world") + + logStr := errorBuffer.String() + var ts float64 + var lineNo int + expectFormat := `{"ts":%f,"caller":"json/json_test.go:%d","msg":"failed","err":"some error"}` + n, err := fmt.Sscanf(logStr, expectFormat, &ts, &lineNo) + if n != 2 || err != nil { + t.Errorf("error log format error: %d elements, error %s:\n%s", n, err, logStr) + } + expect := fmt.Sprintf(expectFormat, ts, lineNo) + if !assert.JSONEq(t, expect, logStr) { + t.Errorf("error log has wrong format \n expect:%s\n got:%s", expect, logStr) + } + + logStr = infoBuffer.String() + expectFormat = `{"ts":%f,"caller":"json/json_test.go:%d","msg":"hello world","v":0}` + n, err = fmt.Sscanf(logStr, expectFormat, &ts, &lineNo) + if n != 2 || err != nil { + t.Errorf("info log format error: %d elements, error %s:\n%s", n, err, logStr) + } + expect = fmt.Sprintf(expectFormat, ts, lineNo) + if !assert.JSONEq(t, expect, logStr) { + t.Errorf("info has wrong format \n expect:%s\n got:%s", expect, logStr) + } +} + type testBuff struct { writeCount int } diff --git a/staging/src/k8s.io/component-base/logs/json/klog_test.go b/staging/src/k8s.io/component-base/logs/json/klog_test.go index 564a0e634c8..3b0f31ce12e 100644 --- a/staging/src/k8s.io/component-base/logs/json/klog_test.go +++ b/staging/src/k8s.io/component-base/logs/json/klog_test.go @@ -206,7 +206,8 @@ func TestKlogIntegration(t *testing.T) { for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { var buffer bytes.Buffer - var logger = NewJSONLogger(zapcore.AddSync(&buffer)) + writer := zapcore.AddSync(&buffer) + logger, _ := NewJSONLogger(writer, writer) klog.SetLogger(logger) defer klog.ClearLogger() @@ -236,7 +237,8 @@ func TestKlogIntegration(t *testing.T) { // TestKlogV test klog -v(--verbose) func available with json logger func TestKlogV(t *testing.T) { var buffer testBuff - logger := NewJSONLogger(&buffer) + writer := zapcore.AddSync(&buffer) + logger, _ := NewJSONLogger(writer, writer) klog.SetLogger(logger) defer klog.ClearLogger() fs := flag.FlagSet{} diff --git a/staging/src/k8s.io/component-base/logs/json/register/register_test.go b/staging/src/k8s.io/component-base/logs/json/register/register_test.go index 346a7715c6e..6f5610f7f27 100644 --- a/staging/src/k8s.io/component-base/logs/json/register/register_test.go +++ b/staging/src/k8s.io/component-base/logs/json/register/register_test.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/pflag" "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/component-base/config" "k8s.io/component-base/logs" @@ -42,6 +43,14 @@ func TestJSONFlag(t *testing.T) { } func TestJSONFormatRegister(t *testing.T) { + defaultOptions := config.FormatOptions{ + JSON: config.JSONOptions{ + InfoBufferSize: resource.QuantityValue{ + Quantity: *resource.NewQuantity(0, resource.DecimalSI), + }, + }, + } + _ = defaultOptions.JSON.InfoBufferSize.String() testcases := []struct { name string args []string @@ -53,7 +62,8 @@ func TestJSONFormatRegister(t *testing.T) { args: []string{"--logging-format=json"}, want: &logs.Options{ Config: config.LoggingConfiguration{ - Format: logs.JSONLogFormat, + Format: logs.JSONLogFormat, + Options: defaultOptions, }, }, }, @@ -62,7 +72,8 @@ func TestJSONFormatRegister(t *testing.T) { args: []string{"--logging-format=test"}, want: &logs.Options{ Config: config.LoggingConfiguration{ - Format: "test", + Format: "test", + Options: defaultOptions, }, }, errs: field.ErrorList{&field.Error{ diff --git a/staging/src/k8s.io/component-base/logs/logs.go b/staging/src/k8s.io/component-base/logs/logs.go index 2708fc24da4..04a91279abb 100644 --- a/staging/src/k8s.io/component-base/logs/logs.go +++ b/staging/src/k8s.io/component-base/logs/logs.go @@ -42,6 +42,7 @@ const deprecated = "will be removed in a future release, see https://github.com/ var ( packageFlags = flag.NewFlagSet("logging", flag.ContinueOnError) logFlushFreq time.Duration + logrFlush func() ) func init() { @@ -128,6 +129,9 @@ func InitLogs() { // are printed before exiting the program. func FlushLogs() { klog.Flush() + if logrFlush != nil { + logrFlush() + } } // NewLogger creates a new log.Logger which sends logs to klog.Info. diff --git a/staging/src/k8s.io/component-base/logs/options.go b/staging/src/k8s.io/component-base/logs/options.go index 7f23207fd10..93898c5a618 100644 --- a/staging/src/k8s.io/component-base/logs/options.go +++ b/staging/src/k8s.io/component-base/logs/options.go @@ -63,7 +63,9 @@ func (o *Options) Apply() { if factory == nil { klog.ClearLogger() } else { - klog.SetLogger(factory.Create()) + log, flush := factory.Create(o.Config.Options) + klog.SetLogger(log) + logrFlush = flush } if o.Config.Sanitization { klog.SetLogFilter(&sanitization.SanitizingFilter{}) diff --git a/staging/src/k8s.io/component-base/logs/options_test.go b/staging/src/k8s.io/component-base/logs/options_test.go index f33d060f2d4..eec36abf59d 100644 --- a/staging/src/k8s.io/component-base/logs/options_test.go +++ b/staging/src/k8s.io/component-base/logs/options_test.go @@ -68,6 +68,7 @@ func TestOptions(t *testing.T) { Config: config.LoggingConfiguration{ Format: DefaultLogFormat, Sanitization: true, + Options: NewOptions().Config.Options, }, }, }, @@ -76,7 +77,8 @@ func TestOptions(t *testing.T) { args: []string{"--logging-format=test"}, want: &Options{ Config: config.LoggingConfiguration{ - Format: "test", + Format: "test", + Options: NewOptions().Config.Options, }, }, errs: field.ErrorList{&field.Error{ diff --git a/staging/src/k8s.io/component-base/logs/registry/registry.go b/staging/src/k8s.io/component-base/logs/registry/registry.go index 09213736efc..145c0b8fd03 100644 --- a/staging/src/k8s.io/component-base/logs/registry/registry.go +++ b/staging/src/k8s.io/component-base/logs/registry/registry.go @@ -21,6 +21,8 @@ import ( "sort" "github.com/go-logr/logr" + + "k8s.io/component-base/config" ) // LogRegistry is new init LogFormatRegistry struct @@ -35,8 +37,11 @@ type LogFormatRegistry struct { // LogFormatFactory provides support for a certain additional, // non-default log format. type LogFormatFactory interface { - // Create returns a logger. - Create() logr.Logger + // Create returns a logger with the requested configuration. + // 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(options config.FormatOptions) (log logr.Logger, flush func()) } // NewLogFormatRegistry return new init LogFormatRegistry struct diff --git a/staging/src/k8s.io/component-base/logs/validate.go b/staging/src/k8s.io/component-base/logs/validate.go index f123c819b7c..528f94ea5a7 100644 --- a/staging/src/k8s.io/component-base/logs/validate.go +++ b/staging/src/k8s.io/component-base/logs/validate.go @@ -37,8 +37,12 @@ func ValidateLoggingConfiguration(c *config.LoggingConfiguration, fldPath *field } } } - if _, err := registry.LogRegistry.Get(c.Format); err != nil { + _, err := registry.LogRegistry.Get(c.Format) + if err != nil { errs = append(errs, field.Invalid(fldPath.Child("format"), c.Format, "Unsupported log format")) } + + // Currently nothing to validate for c.Options. + return errs } diff --git a/staging/src/k8s.io/kubelet/config/v1beta1/zz_generated.deepcopy.go b/staging/src/k8s.io/kubelet/config/v1beta1/zz_generated.deepcopy.go index 9bd95cbe570..de561fd5996 100644 --- a/staging/src/k8s.io/kubelet/config/v1beta1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/kubelet/config/v1beta1/zz_generated.deepcopy.go @@ -310,7 +310,7 @@ func (in *KubeletConfiguration) DeepCopyInto(out *KubeletConfiguration) { *out = make([]string, len(*in)) copy(*out, *in) } - out.Logging = in.Logging + in.Logging.DeepCopyInto(&out.Logging) if in.EnableSystemLogHandler != nil { in, out := &in.EnableSystemLogHandler, &out.EnableSystemLogHandler *out = new(bool)