mirror of
https://github.com/open-cluster-management-io/ocm.git
synced 2026-08-23 22:26:49 +00:00
🌱 Bump the k8s-io group across 1 directory with 3 updates (#1418)
Bumps the k8s-io group with 3 updates in the / directory: [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver), [k8s.io/klog/v2](https://github.com/kubernetes/klog) and [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime). Updates `k8s.io/apiextensions-apiserver` from 0.35.0 to 0.35.2 - [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases) - [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.35.0...v0.35.2) Updates `k8s.io/klog/v2` from 2.130.1 to 2.140.0 - [Release notes](https://github.com/kubernetes/klog/releases) - [Changelog](https://github.com/kubernetes/klog/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes/klog/compare/v2.130.1...2.140.0) Updates `sigs.k8s.io/controller-runtime` from 0.23.1 to 0.23.3 - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.23.1...v0.23.3) --- updated-dependencies: - dependency-name: k8s.io/apiextensions-apiserver dependency-version: 0.35.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s-io - dependency-name: k8s.io/klog/v2 dependency-version: 2.140.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: k8s-io - dependency-name: sigs.k8s.io/controller-runtime dependency-version: 0.23.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s-io ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
@@ -30,12 +30,12 @@ require (
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
helm.sh/helm/v3 v3.19.5
|
||||
k8s.io/api v0.35.2
|
||||
k8s.io/apiextensions-apiserver v0.35.0
|
||||
k8s.io/apiextensions-apiserver v0.35.2
|
||||
k8s.io/apimachinery v0.35.2
|
||||
k8s.io/apiserver v0.35.2
|
||||
k8s.io/client-go v0.35.2
|
||||
k8s.io/component-base v0.35.2
|
||||
k8s.io/klog/v2 v2.130.1
|
||||
k8s.io/klog/v2 v2.140.0
|
||||
k8s.io/kube-aggregator v0.35.2
|
||||
k8s.io/kubectl v0.35.2
|
||||
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4
|
||||
@@ -44,7 +44,7 @@ require (
|
||||
open-cluster-management.io/sdk-go v1.2.1-0.20260306024852-c0938d15158a
|
||||
sigs.k8s.io/about-api v0.0.0-20250131010323-518069c31c03
|
||||
sigs.k8s.io/cluster-inventory-api v0.0.0-20251124125836-445319b6307a
|
||||
sigs.k8s.io/controller-runtime v0.23.1
|
||||
sigs.k8s.io/controller-runtime v0.23.3
|
||||
sigs.k8s.io/kube-storage-version-migrator v0.0.6-0.20230721195810-5c8923c5ff96
|
||||
sigs.k8s.io/yaml v1.6.0
|
||||
)
|
||||
|
||||
@@ -559,8 +559,8 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.35.2 h1:tW7mWc2RpxW7HS4CoRXhtYHSzme1PN1UjGHJ1bdrtdw=
|
||||
k8s.io/api v0.35.2/go.mod h1:7AJfqGoAZcwSFhOjcGM7WV05QxMMgUaChNfLTXDRE60=
|
||||
k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4=
|
||||
k8s.io/apiextensions-apiserver v0.35.0/go.mod h1:E1Ahk9SADaLQ4qtzYFkwUqusXTcaV2uw3l14aqpL2LU=
|
||||
k8s.io/apiextensions-apiserver v0.35.2 h1:iyStXHoJZsUXPh/nFAsjC29rjJWdSgUmG1XpApE29c0=
|
||||
k8s.io/apiextensions-apiserver v0.35.2/go.mod h1:OdyGvcO1FtMDWQ+rRh/Ei3b6X3g2+ZDHd0MSRGeS8rU=
|
||||
k8s.io/apimachinery v0.35.2 h1:NqsM/mmZA7sHW02JZ9RTtk3wInRgbVxL8MPfzSANAK8=
|
||||
k8s.io/apimachinery v0.35.2/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
|
||||
k8s.io/apiserver v0.35.2 h1:rb52v0CZGEL0FkhjS+I6jHflAp7fZ4MIaKcEHX7wmDk=
|
||||
@@ -569,8 +569,8 @@ k8s.io/client-go v0.35.2 h1:YUfPefdGJA4aljDdayAXkc98DnPkIetMl4PrKX97W9o=
|
||||
k8s.io/client-go v0.35.2/go.mod h1:4QqEwh4oQpeK8AaefZ0jwTFJw/9kIjdQi0jpKeYvz7g=
|
||||
k8s.io/component-base v0.35.2 h1:btgR+qNrpWuRSuvWSnQYsZy88yf5gVwemvz0yw79pGc=
|
||||
k8s.io/component-base v0.35.2/go.mod h1:B1iBJjooe6xIJYUucAxb26RwhAjzx0gHnqO9htWIX+0=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
|
||||
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
|
||||
k8s.io/kms v0.35.2 h1:XPlj7QmLBfzm8gGQnc3+Y95hZLiJs3DjA0IyFOV5Z7g=
|
||||
k8s.io/kms v0.35.2/go.mod h1:VT+4ekZAdrZDMgShK37vvlyHUVhwI9t/9tvh0AyCWmQ=
|
||||
k8s.io/kube-aggregator v0.35.2 h1:bnF7E238wUOVaPpTyKrqGCAEXOAJ6HRTARvJTZ0UIC0=
|
||||
@@ -593,8 +593,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUo
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
|
||||
sigs.k8s.io/cluster-inventory-api v0.0.0-20251124125836-445319b6307a h1:EFfnrAOUotV1XAfTavkqdH8jEnMMRFJe7Nt/znItVGA=
|
||||
sigs.k8s.io/cluster-inventory-api v0.0.0-20251124125836-445319b6307a/go.mod h1:guwenlZ9iIfYlNxn7ExCfugOLTh6wjjRX3adC36YCmQ=
|
||||
sigs.k8s.io/controller-runtime v0.23.1 h1:TjJSM80Nf43Mg21+RCy3J70aj/W6KyvDtOlpKf+PupE=
|
||||
sigs.k8s.io/controller-runtime v0.23.1/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0=
|
||||
sigs.k8s.io/controller-runtime v0.23.3 h1:VjB/vhoPoA9l1kEKZHBMnQF33tdCLQKJtydy4iqwZ80=
|
||||
sigs.k8s.io/controller-runtime v0.23.3/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0=
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/kube-storage-version-migrator v0.0.6-0.20230721195810-5c8923c5ff96 h1:PFWFSkpArPNJxFX4ZKWAk9NSeRoZaXschn+ULa4xVek=
|
||||
|
||||
-2
@@ -48,8 +48,6 @@ How to use klog
|
||||
- For more logging conventions (See [Logging Conventions](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md))
|
||||
- See our documentation on [pkg.go.dev/k8s.io](https://pkg.go.dev/k8s.io/klog).
|
||||
|
||||
**NOTE**: please use the newer go versions that support semantic import versioning in modules, ideally go 1.11.4 or greater.
|
||||
|
||||
### Coexisting with klog/v2
|
||||
|
||||
See [this example](examples/coexist_klog_v1_and_v2/) to see how to coexist with both klog/v1 and klog/v2.
|
||||
|
||||
+131
-111
@@ -20,7 +20,9 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
@@ -51,139 +53,157 @@ func WithValues(oldKV, newKV []interface{}) []interface{} {
|
||||
return kv
|
||||
}
|
||||
|
||||
// MergeKVs deduplicates elements provided in two key/value slices.
|
||||
//
|
||||
// Keys in each slice are expected to be unique, so duplicates can only occur
|
||||
// when the first and second slice contain the same key. When that happens, the
|
||||
// key/value pair from the second slice is used. The first slice must be well-formed
|
||||
// (= even key/value pairs). The second one may have a missing value, in which
|
||||
// case the special "missing value" is added to the result.
|
||||
func MergeKVs(first, second []interface{}) []interface{} {
|
||||
maxLength := len(first) + (len(second)+1)/2*2
|
||||
if maxLength == 0 {
|
||||
// Nothing to do at all.
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(first) == 0 && len(second)%2 == 0 {
|
||||
// Nothing to be overridden, second slice is well-formed
|
||||
// and can be used directly.
|
||||
return second
|
||||
}
|
||||
|
||||
// Determine which keys are in the second slice so that we can skip
|
||||
// them when iterating over the first one. The code intentionally
|
||||
// favors performance over completeness: we assume that keys are string
|
||||
// constants and thus compare equal when the string values are equal. A
|
||||
// string constant being overridden by, for example, a fmt.Stringer is
|
||||
// not handled.
|
||||
overrides := map[interface{}]bool{}
|
||||
for i := 0; i < len(second); i += 2 {
|
||||
overrides[second[i]] = true
|
||||
}
|
||||
merged := make([]interface{}, 0, maxLength)
|
||||
for i := 0; i+1 < len(first); i += 2 {
|
||||
key := first[i]
|
||||
if overrides[key] {
|
||||
continue
|
||||
}
|
||||
merged = append(merged, key, first[i+1])
|
||||
}
|
||||
merged = append(merged, second...)
|
||||
if len(merged)%2 != 0 {
|
||||
merged = append(merged, missingValue)
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
type Formatter struct {
|
||||
AnyToStringHook AnyToStringFunc
|
||||
}
|
||||
|
||||
type AnyToStringFunc func(v interface{}) string
|
||||
|
||||
// MergeKVsInto is a variant of MergeKVs which directly formats the key/value
|
||||
// pairs into a buffer.
|
||||
func (f Formatter) MergeAndFormatKVs(b *bytes.Buffer, first, second []interface{}) {
|
||||
if len(first) == 0 && len(second) == 0 {
|
||||
// Nothing to do at all.
|
||||
return
|
||||
}
|
||||
|
||||
if len(first) == 0 && len(second)%2 == 0 {
|
||||
// Nothing to be overridden, second slice is well-formed
|
||||
// and can be used directly.
|
||||
for i := 0; i < len(second); i += 2 {
|
||||
f.KVFormat(b, second[i], second[i+1])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Determine which keys are in the second slice so that we can skip
|
||||
// them when iterating over the first one. The code intentionally
|
||||
// favors performance over completeness: we assume that keys are string
|
||||
// constants and thus compare equal when the string values are equal. A
|
||||
// string constant being overridden by, for example, a fmt.Stringer is
|
||||
// not handled.
|
||||
overrides := map[interface{}]bool{}
|
||||
for i := 0; i < len(second); i += 2 {
|
||||
overrides[second[i]] = true
|
||||
}
|
||||
for i := 0; i < len(first); i += 2 {
|
||||
key := first[i]
|
||||
if overrides[key] {
|
||||
continue
|
||||
}
|
||||
f.KVFormat(b, key, first[i+1])
|
||||
}
|
||||
// Round down.
|
||||
l := len(second)
|
||||
l = l / 2 * 2
|
||||
for i := 1; i < l; i += 2 {
|
||||
f.KVFormat(b, second[i-1], second[i])
|
||||
}
|
||||
if len(second)%2 == 1 {
|
||||
f.KVFormat(b, second[len(second)-1], missingValue)
|
||||
}
|
||||
}
|
||||
|
||||
func MergeAndFormatKVs(b *bytes.Buffer, first, second []interface{}) {
|
||||
Formatter{}.MergeAndFormatKVs(b, first, second)
|
||||
}
|
||||
|
||||
const missingValue = "(MISSING)"
|
||||
|
||||
// KVListFormat serializes all key/value pairs into the provided buffer.
|
||||
// A space gets inserted before the first pair and between each pair.
|
||||
func (f Formatter) KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
|
||||
for i := 0; i < len(keysAndValues); i += 2 {
|
||||
var v interface{}
|
||||
k := keysAndValues[i]
|
||||
if i+1 < len(keysAndValues) {
|
||||
v = keysAndValues[i+1]
|
||||
} else {
|
||||
v = missingValue
|
||||
func FormatKVs(b *bytes.Buffer, kvs ...[]interface{}) {
|
||||
Formatter{}.FormatKVs(b, kvs...)
|
||||
}
|
||||
|
||||
// FormatKVs formats all key/value pairs such that the output contains no
|
||||
// duplicates ("last one wins").
|
||||
func (f Formatter) FormatKVs(b *bytes.Buffer, kvs ...[]interface{}) {
|
||||
// De-duplication is done by optimistically formatting all key value
|
||||
// pairs and then cutting out the output of those key/value pairs which
|
||||
// got overwritten later.
|
||||
//
|
||||
// In the common case of no duplicates, the only overhead is tracking
|
||||
// previous keys. This uses a slice with a simple linear search because
|
||||
// the number of entries is typically so low that allocating a map or
|
||||
// keeping a sorted slice with binary search aren't justified.
|
||||
//
|
||||
// Using a fixed size here makes the Go compiler use the stack as
|
||||
// initial backing store for the slice, which is crucial for
|
||||
// performance.
|
||||
existing := make([]obsoleteKV, 0, 32)
|
||||
obsolete := make([]interval, 0, 32) // Sorted by start index.
|
||||
for _, keysAndValues := range kvs {
|
||||
for i := 0; i < len(keysAndValues); i += 2 {
|
||||
var v interface{}
|
||||
k := keysAndValues[i]
|
||||
if i+1 < len(keysAndValues) {
|
||||
v = keysAndValues[i+1]
|
||||
} else {
|
||||
v = missingValue
|
||||
}
|
||||
var e obsoleteKV
|
||||
e.start = b.Len()
|
||||
e.key = f.KVFormat(b, k, v)
|
||||
e.end = b.Len()
|
||||
i := findObsoleteEntry(existing, e.key)
|
||||
if i >= 0 {
|
||||
data := b.Bytes()
|
||||
if bytes.Compare(data[existing[i].start:existing[i].end], data[e.start:e.end]) == 0 {
|
||||
// The new entry gets obsoleted because it's identical.
|
||||
// This has the advantage that key/value pairs from
|
||||
// a WithValues call always come first, even if the same
|
||||
// pair gets added again later. This makes different log
|
||||
// entries more consistent.
|
||||
//
|
||||
// The new entry has a higher start index and thus can be appended.
|
||||
obsolete = append(obsolete, e.interval)
|
||||
} else {
|
||||
// The old entry gets obsoleted because it's value is different.
|
||||
//
|
||||
// Sort order is not guaranteed, we have to insert at the right place.
|
||||
index, _ := slices.BinarySearchFunc(obsolete, existing[i].interval, func(a, b interval) int { return a.start - b.start })
|
||||
obsolete = slices.Insert(obsolete, index, existing[i].interval)
|
||||
existing[i].interval = e.interval
|
||||
}
|
||||
} else {
|
||||
// Instead of appending at the end and doing a
|
||||
// linear search in findEntry, we could keep
|
||||
// the slice sorted by key and do a binary search.
|
||||
//
|
||||
// Above:
|
||||
// i, ok := slices.BinarySearchFunc(existing, e, func(a, b entry) int { return strings.Compare(a.key, b.key) })
|
||||
// Here:
|
||||
// existing = slices.Insert(existing, i, e)
|
||||
//
|
||||
// But that adds a dependency on the slices package
|
||||
// and made performance slightly worse, presumably
|
||||
// because the cost of shifting entries around
|
||||
// did not pay of with faster lookups.
|
||||
existing = append(existing, e)
|
||||
}
|
||||
}
|
||||
f.KVFormat(b, k, v)
|
||||
}
|
||||
|
||||
// If we need to remove some obsolete key/value pairs then move the memory.
|
||||
if len(obsolete) > 0 {
|
||||
// Potentially the next remaining output (might itself be obsolete).
|
||||
from := obsolete[0].end
|
||||
// Next obsolete entry.
|
||||
nextObsolete := 1
|
||||
// This is the source buffer, before truncation.
|
||||
all := b.Bytes()
|
||||
b.Truncate(obsolete[0].start)
|
||||
|
||||
for nextObsolete < len(obsolete) {
|
||||
if from == obsolete[nextObsolete].start {
|
||||
// Skip also the next obsolete key/value.
|
||||
from = obsolete[nextObsolete].end
|
||||
nextObsolete++
|
||||
continue
|
||||
}
|
||||
|
||||
// Preserve some output. Write uses copy, which
|
||||
// explicitly allows source and destination to overlap.
|
||||
// That could happen here.
|
||||
valid := all[from:obsolete[nextObsolete].start]
|
||||
b.Write(valid)
|
||||
from = obsolete[nextObsolete].end
|
||||
nextObsolete++
|
||||
}
|
||||
// Copy end of buffer.
|
||||
valid := all[from:]
|
||||
b.Write(valid)
|
||||
}
|
||||
}
|
||||
|
||||
func KVListFormat(b *bytes.Buffer, keysAndValues ...interface{}) {
|
||||
Formatter{}.KVListFormat(b, keysAndValues...)
|
||||
type obsoleteKV struct {
|
||||
key string
|
||||
interval
|
||||
}
|
||||
|
||||
func KVFormat(b *bytes.Buffer, k, v interface{}) {
|
||||
Formatter{}.KVFormat(b, k, v)
|
||||
// interval includes the start and excludes the end.
|
||||
type interval struct {
|
||||
start int
|
||||
end int
|
||||
}
|
||||
|
||||
func findObsoleteEntry(entries []obsoleteKV, key string) int {
|
||||
for i, entry := range entries {
|
||||
if entry.key == key {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// formatAny is the fallback formatter for a value. It supports a hook (for
|
||||
// example, for YAML encoding) and itself uses JSON encoding.
|
||||
func (f Formatter) formatAny(b *bytes.Buffer, v interface{}) {
|
||||
b.WriteRune('=')
|
||||
if f.AnyToStringHook != nil {
|
||||
b.WriteString(f.AnyToStringHook(v))
|
||||
str := f.AnyToStringHook(v)
|
||||
if strings.Contains(str, "\n") {
|
||||
// If it's multi-line, then pass it through writeStringValue to get start/end delimiters,
|
||||
// which separates it better from any following key/value pair.
|
||||
writeStringValue(b, str)
|
||||
return
|
||||
}
|
||||
// Otherwise put it directly after the separator, on the same lime,
|
||||
// The assumption is that the hook returns something where start/end are obvious.
|
||||
b.WriteRune('=')
|
||||
b.WriteString(str)
|
||||
return
|
||||
}
|
||||
b.WriteRune('=')
|
||||
formatAsJSON(b, v)
|
||||
}
|
||||
|
||||
|
||||
+7
-3
@@ -28,7 +28,7 @@ import (
|
||||
|
||||
// KVFormat serializes one key/value pair into the provided buffer.
|
||||
// A space gets inserted before the pair.
|
||||
func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) {
|
||||
func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) string {
|
||||
// This is the version without slog support. Must be kept in sync with
|
||||
// the version in keyvalues_slog.go.
|
||||
|
||||
@@ -37,13 +37,15 @@ func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) {
|
||||
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments
|
||||
// for the sake of performance. Keys with spaces,
|
||||
// special characters, etc. will break parsing.
|
||||
var key string
|
||||
if sK, ok := k.(string); ok {
|
||||
// Avoid one allocation when the key is a string, which
|
||||
// normally it should be.
|
||||
b.WriteString(sK)
|
||||
key = sK
|
||||
} else {
|
||||
b.WriteString(fmt.Sprintf("%s", k))
|
||||
key = fmt.Sprintf("%s", k)
|
||||
}
|
||||
b.WriteString(key)
|
||||
|
||||
// The type checks are sorted so that more frequently used ones
|
||||
// come first because that is then faster in the common
|
||||
@@ -94,4 +96,6 @@ func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) {
|
||||
default:
|
||||
f.formatAny(b, v)
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
+8
-4
@@ -29,8 +29,8 @@ import (
|
||||
)
|
||||
|
||||
// KVFormat serializes one key/value pair into the provided buffer.
|
||||
// A space gets inserted before the pair.
|
||||
func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) {
|
||||
// A space gets inserted before the pair. It returns the key.
|
||||
func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) string {
|
||||
// This is the version without slog support. Must be kept in sync with
|
||||
// the version in keyvalues_slog.go.
|
||||
|
||||
@@ -39,13 +39,15 @@ func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) {
|
||||
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments
|
||||
// for the sake of performance. Keys with spaces,
|
||||
// special characters, etc. will break parsing.
|
||||
var key string
|
||||
if sK, ok := k.(string); ok {
|
||||
// Avoid one allocation when the key is a string, which
|
||||
// normally it should be.
|
||||
b.WriteString(sK)
|
||||
key = sK
|
||||
} else {
|
||||
b.WriteString(fmt.Sprintf("%s", k))
|
||||
key = fmt.Sprintf("%s", k)
|
||||
}
|
||||
b.WriteString(key)
|
||||
|
||||
// The type checks are sorted so that more frequently used ones
|
||||
// come first because that is then faster in the common
|
||||
@@ -112,6 +114,8 @@ func (f Formatter) KVFormat(b *bytes.Buffer, k, v interface{}) {
|
||||
default:
|
||||
f.formatAny(b, v)
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
// generateJSON has the same preference for plain strings as KVFormat.
|
||||
|
||||
+66
-21
@@ -58,15 +58,30 @@
|
||||
//
|
||||
// -logtostderr=true
|
||||
// Logs are written to standard error instead of to files.
|
||||
// This shortcuts most of the usual output routing:
|
||||
// -alsologtostderr, -stderrthreshold and -log_dir have no
|
||||
// effect and output redirection at runtime with SetOutput is
|
||||
// ignored.
|
||||
// By default, all logs are written regardless of severity
|
||||
// (legacy behavior). To filter logs by severity when
|
||||
// -logtostderr=true, set -legacy_stderr_threshold_behavior=false
|
||||
// and use -stderrthreshold.
|
||||
// With -legacy_stderr_threshold_behavior=true,
|
||||
// -stderrthreshold has no effect.
|
||||
//
|
||||
// The following flags always have no effect:
|
||||
// -alsologtostderr, -alsologtostderrthreshold, and -log_dir.
|
||||
// Output redirection at runtime with SetOutput is also ignored.
|
||||
// -alsologtostderr=false
|
||||
// Logs are written to standard error as well as to files.
|
||||
// -alsologtostderrthreshold=INFO
|
||||
// Log events at or above this severity are logged to standard
|
||||
// error when -alsologtostderr=true (no effect when -logtostderr=true).
|
||||
// Default is INFO to maintain backward compatibility.
|
||||
// -stderrthreshold=ERROR
|
||||
// Log events at or above this severity are logged to standard
|
||||
// error as well as to files.
|
||||
// error as well as to files. When -logtostderr=true, this flag
|
||||
// has no effect unless -legacy_stderr_threshold_behavior=false.
|
||||
// -legacy_stderr_threshold_behavior=true
|
||||
// If true, -stderrthreshold is ignored when -logtostderr=true
|
||||
// (legacy behavior). If false, -stderrthreshold is honored even
|
||||
// when -logtostderr=true, allowing severity-based filtering.
|
||||
// -log_dir=""
|
||||
// Log files will be written to this directory instead of the
|
||||
// default temporary directory.
|
||||
@@ -156,7 +171,7 @@ func (s *severityValue) Set(value string) error {
|
||||
}
|
||||
threshold = severity.Severity(v)
|
||||
}
|
||||
logging.stderrThreshold.set(threshold)
|
||||
s.set(threshold)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -409,6 +424,15 @@ var commandLine flag.FlagSet
|
||||
|
||||
// init sets up the defaults and creates command line flags.
|
||||
func init() {
|
||||
// Initialize severity thresholds
|
||||
logging.stderrThreshold = severityValue{
|
||||
Severity: severity.ErrorLog, // Default stderrThreshold is ERROR.
|
||||
}
|
||||
logging.alsologtostderrthreshold = severityValue{
|
||||
Severity: severity.InfoLog, // Default alsologtostderrthreshold is INFO (to maintain backward compatibility).
|
||||
}
|
||||
logging.setVState(0, nil, false)
|
||||
|
||||
commandLine.StringVar(&logging.logDir, "log_dir", "", "If non-empty, write log files in this directory (no effect when -logtostderr=true)")
|
||||
commandLine.StringVar(&logging.logFile, "log_file", "", "If non-empty, use this log file (no effect when -logtostderr=true)")
|
||||
commandLine.Uint64Var(&logging.logFileMaxSizeMB, "log_file_max_size", 1800,
|
||||
@@ -416,16 +440,14 @@ func init() {
|
||||
"If the value is 0, the maximum file size is unlimited.")
|
||||
commandLine.BoolVar(&logging.toStderr, "logtostderr", true, "log to standard error instead of files")
|
||||
commandLine.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files (no effect when -logtostderr=true)")
|
||||
logging.setVState(0, nil, false)
|
||||
commandLine.BoolVar(&logging.legacyStderrThresholdBehavior, "legacy_stderr_threshold_behavior", true, "If true, stderrthreshold is ignored when logtostderr=true (legacy behavior). If false, stderrthreshold is honored even when logtostderr=true")
|
||||
commandLine.Var(&logging.verbosity, "v", "number for the log level verbosity")
|
||||
commandLine.BoolVar(&logging.addDirHeader, "add_dir_header", false, "If true, adds the file directory to the header of the log messages")
|
||||
commandLine.BoolVar(&logging.skipHeaders, "skip_headers", false, "If true, avoid header prefixes in the log messages")
|
||||
commandLine.BoolVar(&logging.oneOutput, "one_output", false, "If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true)")
|
||||
commandLine.BoolVar(&logging.skipLogHeaders, "skip_log_headers", false, "If true, avoid headers when opening log files (no effect when -logtostderr=true)")
|
||||
logging.stderrThreshold = severityValue{
|
||||
Severity: severity.ErrorLog, // Default stderrThreshold is ERROR.
|
||||
}
|
||||
commandLine.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=true)")
|
||||
commandLine.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=true unless -legacy_stderr_threshold_behavior=false)")
|
||||
commandLine.Var(&logging.alsologtostderrthreshold, "alsologtostderrthreshold", "logs at or above this threshold go to stderr when -alsologtostderr=true (no effect when -logtostderr=true)")
|
||||
commandLine.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging")
|
||||
commandLine.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace")
|
||||
|
||||
@@ -470,11 +492,13 @@ type settings struct {
|
||||
// Boolean flags. Not handled atomically because the flag.Value interface
|
||||
// does not let us avoid the =true, and that shorthand is necessary for
|
||||
// compatibility. TODO: does this matter enough to fix? Seems unlikely.
|
||||
toStderr bool // The -logtostderr flag.
|
||||
alsoToStderr bool // The -alsologtostderr flag.
|
||||
toStderr bool // The -logtostderr flag.
|
||||
alsoToStderr bool // The -alsologtostderr flag.
|
||||
legacyStderrThresholdBehavior bool // The -legacy_stderr_threshold_behavior flag.
|
||||
|
||||
// Level flag. Handled atomically.
|
||||
stderrThreshold severityValue // The -stderrthreshold flag.
|
||||
stderrThreshold severityValue // The -stderrthreshold flag.
|
||||
alsologtostderrthreshold severityValue // The -alsologtostderrthreshold flag.
|
||||
|
||||
// Access to all of the following fields must be protected via a mutex.
|
||||
|
||||
@@ -809,16 +833,21 @@ func (l *loggingT) infoS(logger *logWriter, filter LogFilter, depth int, msg str
|
||||
// printS is called from infoS and errorS if logger is not specified.
|
||||
// set log severity by s
|
||||
func (l *loggingT) printS(err error, s severity.Severity, depth int, msg string, keysAndValues ...interface{}) {
|
||||
// Only create a new buffer if we don't have one cached.
|
||||
b := buffer.GetBuffer()
|
||||
// The message is always quoted, even if it contains line breaks.
|
||||
// If developers want multi-line output, they should use a small, fixed
|
||||
// message and put the multi-line output into a value.
|
||||
b.WriteString(strconv.Quote(msg))
|
||||
qMsg := make([]byte, 0, 1024)
|
||||
qMsg = strconv.AppendQuote(qMsg, msg)
|
||||
|
||||
// Only create a new buffer if we don't have one cached.
|
||||
b := buffer.GetBuffer()
|
||||
b.Write(qMsg)
|
||||
|
||||
var errKV []interface{}
|
||||
if err != nil {
|
||||
serialize.KVListFormat(&b.Buffer, "err", err)
|
||||
errKV = []interface{}{"err", err}
|
||||
}
|
||||
serialize.KVListFormat(&b.Buffer, keysAndValues...)
|
||||
serialize.FormatKVs(&b.Buffer, errKV, keysAndValues)
|
||||
l.printDepth(s, nil, nil, depth+1, &b.Buffer)
|
||||
// Make the buffer available for reuse.
|
||||
buffer.PutBuffer(b)
|
||||
@@ -885,9 +914,25 @@ func (l *loggingT) output(s severity.Severity, logger *logWriter, buf *buffer.Bu
|
||||
}
|
||||
}
|
||||
} else if l.toStderr {
|
||||
os.Stderr.Write(data)
|
||||
// When logging to stderr only, check if we should filter by severity.
|
||||
// This is controlled by the legacy_stderr_threshold_behavior flag.
|
||||
if l.legacyStderrThresholdBehavior {
|
||||
// Legacy behavior: always write to stderr, ignore stderrthreshold
|
||||
os.Stderr.Write(data)
|
||||
} else {
|
||||
// New behavior: honor stderrthreshold even when logtostderr=true
|
||||
if s >= l.stderrThreshold.get() {
|
||||
os.Stderr.Write(data)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() {
|
||||
// Write to stderr if any of these conditions are met:
|
||||
// - alsoToStderr is set (legacy behavior)
|
||||
// - alsologtostderr is set and severity meets alsologtostderrthreshold
|
||||
// - alsologtostderr is not set and severity meets stderrThreshold
|
||||
if alsoToStderr ||
|
||||
(l.alsoToStderr && s >= l.alsologtostderrthreshold.get()) ||
|
||||
(!l.alsoToStderr && s >= l.stderrThreshold.get()) {
|
||||
os.Stderr.Write(data)
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -53,7 +53,7 @@ func (l *klogger) Init(info logr.RuntimeInfo) {
|
||||
}
|
||||
|
||||
func (l *klogger) Info(level int, msg string, kvList ...interface{}) {
|
||||
merged := serialize.MergeKVs(l.values, kvList)
|
||||
merged := serialize.WithValues(l.values, kvList)
|
||||
// Skip this function.
|
||||
VDepth(l.callDepth+1, Level(level)).InfoSDepth(l.callDepth+1, msg, merged...)
|
||||
}
|
||||
@@ -63,7 +63,7 @@ func (l *klogger) Enabled(level int) bool {
|
||||
}
|
||||
|
||||
func (l *klogger) Error(err error, msg string, kvList ...interface{}) {
|
||||
merged := serialize.MergeKVs(l.values, kvList)
|
||||
merged := serialize.WithValues(l.values, kvList)
|
||||
ErrorSDepth(l.callDepth+1, err, msg, merged...)
|
||||
}
|
||||
|
||||
|
||||
+8
-3
@@ -63,12 +63,17 @@ func slogOutput(file string, line int, now time.Time, err error, s severity.Seve
|
||||
}
|
||||
|
||||
// See printS.
|
||||
qMsg := make([]byte, 0, 1024)
|
||||
qMsg = strconv.AppendQuote(qMsg, msg)
|
||||
|
||||
b := buffer.GetBuffer()
|
||||
b.WriteString(strconv.Quote(msg))
|
||||
b.Write(qMsg)
|
||||
|
||||
var errKV []interface{}
|
||||
if err != nil {
|
||||
serialize.KVListFormat(&b.Buffer, "err", err)
|
||||
errKV = []interface{}{"err", err}
|
||||
}
|
||||
serialize.KVListFormat(&b.Buffer, kvList...)
|
||||
serialize.FormatKVs(&b.Buffer, errKV, kvList)
|
||||
|
||||
// See print + header.
|
||||
buf := logging.formatHeader(s, file, line, now)
|
||||
|
||||
+5
-4
@@ -302,12 +302,12 @@ func (l tlogger) Info(level int, msg string, kvList ...interface{}) {
|
||||
|
||||
l.shared.t.Helper()
|
||||
buf := buffer.GetBuffer()
|
||||
l.shared.formatter.MergeAndFormatKVs(&buf.Buffer, l.values, kvList)
|
||||
l.shared.formatter.FormatKVs(&buf.Buffer, l.values, kvList)
|
||||
l.log(LogInfo, msg, level, buf, nil, kvList)
|
||||
}
|
||||
|
||||
func (l tlogger) Enabled(level int) bool {
|
||||
return l.shared.config.vstate.Enabled(verbosity.Level(level), 1)
|
||||
return l.shared.config.vstate.Enabled(verbosity.Level(level), l.shared.callDepth+1)
|
||||
}
|
||||
|
||||
func (l tlogger) Error(err error, msg string, kvList ...interface{}) {
|
||||
@@ -320,10 +320,11 @@ func (l tlogger) Error(err error, msg string, kvList ...interface{}) {
|
||||
|
||||
l.shared.t.Helper()
|
||||
buf := buffer.GetBuffer()
|
||||
var errKV []interface{}
|
||||
if err != nil {
|
||||
l.shared.formatter.KVFormat(&buf.Buffer, "err", err)
|
||||
errKV = []interface{}{"err", err}
|
||||
}
|
||||
l.shared.formatter.MergeAndFormatKVs(&buf.Buffer, l.values, kvList)
|
||||
l.shared.formatter.FormatKVs(&buf.Buffer, errKV, l.values, kvList)
|
||||
l.log(LogError, msg, 0, buf, err, kvList)
|
||||
}
|
||||
|
||||
|
||||
+18
@@ -59,6 +59,7 @@ type configOptions struct {
|
||||
verbosityDefault int
|
||||
fixedTime *time.Time
|
||||
unwind func(int) (string, int)
|
||||
withHeader bool
|
||||
output io.Writer
|
||||
}
|
||||
|
||||
@@ -106,6 +107,22 @@ func FixedTime(ts time.Time) ConfigOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeader controls whether the header (time, source code location, etc.)
|
||||
// is included in the output. The default is to include it.
|
||||
//
|
||||
// This can be useful in combination with redirection to a buffer to
|
||||
// turn structured log parameters into a string (see example).
|
||||
//
|
||||
// # Experimental
|
||||
//
|
||||
// Notice: This function is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
func WithHeader(enabled bool) ConfigOption {
|
||||
return func(co *configOptions) {
|
||||
co.withHeader = enabled
|
||||
}
|
||||
}
|
||||
|
||||
// Backtrace overrides the default mechanism for determining the call site.
|
||||
// The callback is invoked with the number of function calls between itself
|
||||
// and the call site. It must return the file name and line number. An empty
|
||||
@@ -131,6 +148,7 @@ func NewConfig(opts ...ConfigOption) *Config {
|
||||
vmoduleFlagName: "vmodule",
|
||||
verbosityDefault: 0,
|
||||
unwind: runtimeBacktrace,
|
||||
withHeader: true,
|
||||
output: os.Stderr,
|
||||
},
|
||||
}
|
||||
|
||||
+33
-20
@@ -92,17 +92,23 @@ func (l *tlogger) Error(err error, msg string, kvList ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *tlogger) print(err error, s severity.Severity, msg string, kvList []interface{}) {
|
||||
// Determine caller.
|
||||
// +1 for this frame, +1 for Info/Error.
|
||||
skip := l.callDepth + 2
|
||||
file, line := l.config.co.unwind(skip)
|
||||
if file == "" {
|
||||
file = "???"
|
||||
line = 1
|
||||
} else if slash := strings.LastIndex(file, "/"); slash >= 0 {
|
||||
file = file[slash+1:]
|
||||
var file string
|
||||
var line int
|
||||
var now time.Time
|
||||
if l.config.co.withHeader {
|
||||
// Determine caller.
|
||||
// +1 for this frame, +1 for Info/Error.
|
||||
skip := l.callDepth + 2
|
||||
file, line = l.config.co.unwind(skip)
|
||||
if file == "" {
|
||||
file = "???"
|
||||
line = 1
|
||||
} else if slash := strings.LastIndex(file, "/"); slash >= 0 {
|
||||
file = file[slash+1:]
|
||||
}
|
||||
now = time.Now()
|
||||
}
|
||||
l.printWithInfos(file, line, time.Now(), err, s, msg, kvList)
|
||||
l.printWithInfos(file, line, now, err, s, msg, kvList)
|
||||
}
|
||||
|
||||
func runtimeBacktrace(skip int) (string, int) {
|
||||
@@ -114,24 +120,31 @@ func runtimeBacktrace(skip int) (string, int) {
|
||||
}
|
||||
|
||||
func (l *tlogger) printWithInfos(file string, line int, now time.Time, err error, s severity.Severity, msg string, kvList []interface{}) {
|
||||
// The message is always quoted, even if it contains line breaks.
|
||||
// If developers want multi-line output, they should use a small, fixed
|
||||
// message and put the multi-line output into a value.
|
||||
qMsg := make([]byte, 0, 1024)
|
||||
qMsg = strconv.AppendQuote(qMsg, msg)
|
||||
|
||||
// Only create a new buffer if we don't have one cached.
|
||||
b := buffer.GetBuffer()
|
||||
defer buffer.PutBuffer(b)
|
||||
|
||||
// Format header.
|
||||
if l.config.co.fixedTime != nil {
|
||||
now = *l.config.co.fixedTime
|
||||
if l.config.co.withHeader {
|
||||
// Format header.
|
||||
if l.config.co.fixedTime != nil {
|
||||
now = *l.config.co.fixedTime
|
||||
}
|
||||
b.FormatHeader(s, file, line, now)
|
||||
}
|
||||
b.FormatHeader(s, file, line, now)
|
||||
|
||||
// The message is always quoted, even if it contains line breaks.
|
||||
// If developers want multi-line output, they should use a small, fixed
|
||||
// message and put the multi-line output into a value.
|
||||
b.WriteString(strconv.Quote(msg))
|
||||
b.Write(qMsg)
|
||||
|
||||
var errKV []interface{}
|
||||
if err != nil {
|
||||
serialize.KVFormat(&b.Buffer, "err", err)
|
||||
errKV = []interface{}{"err", err}
|
||||
}
|
||||
serialize.MergeAndFormatKVs(&b.Buffer, l.values, kvList)
|
||||
serialize.FormatKVs(&b.Buffer, errKV, l.values, kvList)
|
||||
if b.Len() == 0 || b.Bytes()[b.Len()-1] != '\n' {
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
|
||||
Vendored
+4
-4
@@ -1178,7 +1178,7 @@ k8s.io/api/storage/v1
|
||||
k8s.io/api/storage/v1alpha1
|
||||
k8s.io/api/storage/v1beta1
|
||||
k8s.io/api/storagemigration/v1beta1
|
||||
# k8s.io/apiextensions-apiserver v0.35.0
|
||||
# k8s.io/apiextensions-apiserver v0.35.2
|
||||
## explicit; go 1.25.0
|
||||
k8s.io/apiextensions-apiserver/pkg/apis/apiextensions
|
||||
k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1
|
||||
@@ -1808,8 +1808,8 @@ k8s.io/component-base/tracing
|
||||
k8s.io/component-base/tracing/api/v1
|
||||
k8s.io/component-base/version
|
||||
k8s.io/component-base/zpages/features
|
||||
# k8s.io/klog/v2 v2.130.1
|
||||
## explicit; go 1.18
|
||||
# k8s.io/klog/v2 v2.140.0
|
||||
## explicit; go 1.21
|
||||
k8s.io/klog/v2
|
||||
k8s.io/klog/v2/internal/buffer
|
||||
k8s.io/klog/v2/internal/clock
|
||||
@@ -2065,7 +2065,7 @@ sigs.k8s.io/cluster-inventory-api/client/informers/externalversions/apis
|
||||
sigs.k8s.io/cluster-inventory-api/client/informers/externalversions/apis/v1alpha1
|
||||
sigs.k8s.io/cluster-inventory-api/client/informers/externalversions/internalinterfaces
|
||||
sigs.k8s.io/cluster-inventory-api/client/listers/apis/v1alpha1
|
||||
# sigs.k8s.io/controller-runtime v0.23.1
|
||||
# sigs.k8s.io/controller-runtime v0.23.3
|
||||
## explicit; go 1.25.0
|
||||
sigs.k8s.io/controller-runtime
|
||||
sigs.k8s.io/controller-runtime/pkg/builder
|
||||
|
||||
+16
-5
@@ -129,11 +129,7 @@ func (h *defaulterForType[T]) Handle(ctx context.Context, req Request) Response
|
||||
return Errored(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
// Keep a copy of the object if needed
|
||||
var originalObj T
|
||||
if !h.removeUnknownOrOmitableFields {
|
||||
originalObj = obj.DeepCopyObject().(T)
|
||||
}
|
||||
originalObj := obj.DeepCopyObject().(T)
|
||||
|
||||
// Default the object
|
||||
if err := h.defaulter.Default(ctx, obj); err != nil {
|
||||
@@ -144,6 +140,21 @@ func (h *defaulterForType[T]) Handle(ctx context.Context, req Request) Response
|
||||
return Denied(err.Error())
|
||||
}
|
||||
|
||||
// If the object is not changed, there's no reason to go through the expensive patch calculation below.
|
||||
// Note: While jsonpatch.CreatePatch short-circuits if both byte arrays are equal this is likely never the case.
|
||||
// * json.Marshal that we use below sorts fields alphabetically
|
||||
// * for builtin types the apiserver also sorts alphabetically (but it seems like it adds an empty line at the end)
|
||||
// * for CRDs the apiserver uses the field order in the OpenAPI schema which very likely is not alphabetically sorted
|
||||
// Note: If removeUnknownOrOmitableFields is set we have to compute a patch to remove unknown or omitable fields even
|
||||
// if the objects are equal
|
||||
if !h.removeUnknownOrOmitableFields && reflect.DeepEqual(originalObj, obj) {
|
||||
return Response{
|
||||
AdmissionResponse: admissionv1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Create the patch
|
||||
marshalled, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user