100
vendor/k8s.io/utils/net/ipnet.go
generated
vendored
100
vendor/k8s.io/utils/net/ipnet.go
generated
vendored
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package net
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
@@ -119,3 +120,102 @@ func (s IPNetSet) Equal(s2 IPNetSet) bool {
|
||||
func (s IPNetSet) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
// IPSet maps string to net.IP
|
||||
type IPSet map[string]net.IP
|
||||
|
||||
// ParseIPSet parses string slice to IPSet
|
||||
func ParseIPSet(items ...string) (IPSet, error) {
|
||||
ipset := make(IPSet)
|
||||
for _, item := range items {
|
||||
ip := net.ParseIP(strings.TrimSpace(item))
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("error parsing IP %q", item)
|
||||
}
|
||||
|
||||
ipset[ip.String()] = ip
|
||||
}
|
||||
|
||||
return ipset, nil
|
||||
}
|
||||
|
||||
// Insert adds items to the set.
|
||||
func (s IPSet) Insert(items ...net.IP) {
|
||||
for _, item := range items {
|
||||
s[item.String()] = item
|
||||
}
|
||||
}
|
||||
|
||||
// Delete removes all items from the set.
|
||||
func (s IPSet) Delete(items ...net.IP) {
|
||||
for _, item := range items {
|
||||
delete(s, item.String())
|
||||
}
|
||||
}
|
||||
|
||||
// Has returns true if and only if item is contained in the set.
|
||||
func (s IPSet) Has(item net.IP) bool {
|
||||
_, contained := s[item.String()]
|
||||
return contained
|
||||
}
|
||||
|
||||
// HasAll returns true if and only if all items are contained in the set.
|
||||
func (s IPSet) HasAll(items ...net.IP) bool {
|
||||
for _, item := range items {
|
||||
if !s.Has(item) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Difference returns a set of objects that are not in s2
|
||||
// For example:
|
||||
// s1 = {a1, a2, a3}
|
||||
// s2 = {a1, a2, a4, a5}
|
||||
// s1.Difference(s2) = {a3}
|
||||
// s2.Difference(s1) = {a4, a5}
|
||||
func (s IPSet) Difference(s2 IPSet) IPSet {
|
||||
result := make(IPSet)
|
||||
for k, i := range s {
|
||||
_, found := s2[k]
|
||||
if found {
|
||||
continue
|
||||
}
|
||||
result[k] = i
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// StringSlice returns a []string with the String representation of each element in the set.
|
||||
// Order is undefined.
|
||||
func (s IPSet) StringSlice() []string {
|
||||
a := make([]string, 0, len(s))
|
||||
for k := range s {
|
||||
a = append(a, k)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsSuperset returns true if and only if s1 is a superset of s2.
|
||||
func (s IPSet) IsSuperset(s2 IPSet) bool {
|
||||
for k := range s2 {
|
||||
_, found := s[k]
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Equal returns true if and only if s1 is equal (as a set) to s2.
|
||||
// Two sets are equal if their membership is identical.
|
||||
// (In practice, this means same elements, order doesn't matter)
|
||||
func (s IPSet) Equal(s2 IPSet) bool {
|
||||
return len(s) == len(s2) && s.IsSuperset(s2)
|
||||
}
|
||||
|
||||
// Len returns the size of the set.
|
||||
func (s IPSet) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
16
vendor/k8s.io/utils/net/net.go
generated
vendored
16
vendor/k8s.io/utils/net/net.go
generated
vendored
@@ -152,17 +152,17 @@ func ParsePort(port string, allowZero bool) (int, error) {
|
||||
|
||||
// BigForIP creates a big.Int based on the provided net.IP
|
||||
func BigForIP(ip net.IP) *big.Int {
|
||||
b := ip.To4()
|
||||
if b == nil {
|
||||
b = ip.To16()
|
||||
}
|
||||
return big.NewInt(0).SetBytes(b)
|
||||
// NOTE: Convert to 16-byte representation so we can
|
||||
// handle v4 and v6 values the same way.
|
||||
return big.NewInt(0).SetBytes(ip.To16())
|
||||
}
|
||||
|
||||
// AddIPOffset adds the provided integer offset to a base big.Int representing a
|
||||
// net.IP
|
||||
// AddIPOffset adds the provided integer offset to a base big.Int representing a net.IP
|
||||
// NOTE: If you started with a v4 address and overflow it, you get a v6 result.
|
||||
func AddIPOffset(base *big.Int, offset int) net.IP {
|
||||
return net.IP(big.NewInt(0).Add(base, big.NewInt(int64(offset))).Bytes())
|
||||
r := big.NewInt(0).Add(base, big.NewInt(int64(offset))).Bytes()
|
||||
r = append(make([]byte, 16), r...)
|
||||
return net.IP(r[len(r)-16:])
|
||||
}
|
||||
|
||||
// RangeSize returns the size of a range in valid addresses.
|
||||
|
||||
137
vendor/k8s.io/utils/net/port.go
generated
vendored
Normal file
137
vendor/k8s.io/utils/net/port.go
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
Copyright 2020 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 net
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IPFamily refers to a specific family if not empty, i.e. "4" or "6".
|
||||
type IPFamily string
|
||||
|
||||
// Constants for valid IPFamilys:
|
||||
const (
|
||||
IPv4 IPFamily = "4"
|
||||
IPv6 = "6"
|
||||
)
|
||||
|
||||
// Protocol is a network protocol support by LocalPort.
|
||||
type Protocol string
|
||||
|
||||
// Constants for valid protocols:
|
||||
const (
|
||||
TCP Protocol = "TCP"
|
||||
UDP Protocol = "UDP"
|
||||
)
|
||||
|
||||
// LocalPort represents an IP address and port pair along with a protocol
|
||||
// and potentially a specific IP family.
|
||||
// A LocalPort can be opened and subsequently closed.
|
||||
type LocalPort struct {
|
||||
// Description is an arbitrary string.
|
||||
Description string
|
||||
// IP is the IP address part of a given local port.
|
||||
// If this string is empty, the port binds to all local IP addresses.
|
||||
IP string
|
||||
// If IPFamily is not empty, the port binds only to addresses of this
|
||||
// family.
|
||||
// IF empty along with IP, bind to local addresses of any family.
|
||||
IPFamily IPFamily
|
||||
// Port is the port number.
|
||||
// A value of 0 causes a port to be automatically chosen.
|
||||
Port int
|
||||
// Protocol is the protocol, e.g. TCP
|
||||
Protocol Protocol
|
||||
}
|
||||
|
||||
// NewLocalPort returns a LocalPort instance and ensures IPFamily and IP are
|
||||
// consistent and that the given protocol is valid.
|
||||
func NewLocalPort(desc, ip string, ipFamily IPFamily, port int, protocol Protocol) (*LocalPort, error) {
|
||||
if protocol != TCP && protocol != UDP {
|
||||
return nil, fmt.Errorf("Unsupported protocol %s", protocol)
|
||||
}
|
||||
if ipFamily != "" && ipFamily != "4" && ipFamily != "6" {
|
||||
return nil, fmt.Errorf("Invalid IP family %s", ipFamily)
|
||||
}
|
||||
if ip != "" {
|
||||
parsedIP := net.ParseIP(ip)
|
||||
if parsedIP == nil {
|
||||
return nil, fmt.Errorf("invalid ip address %s", ip)
|
||||
}
|
||||
asIPv4 := parsedIP.To4()
|
||||
if asIPv4 == nil && ipFamily == IPv4 || asIPv4 != nil && ipFamily == IPv6 {
|
||||
return nil, fmt.Errorf("ip address and family mismatch %s, %s", ip, ipFamily)
|
||||
}
|
||||
}
|
||||
return &LocalPort{Description: desc, IP: ip, IPFamily: ipFamily, Port: port, Protocol: protocol}, nil
|
||||
}
|
||||
|
||||
func (lp *LocalPort) String() string {
|
||||
ipPort := net.JoinHostPort(lp.IP, strconv.Itoa(lp.Port))
|
||||
return fmt.Sprintf("%q (%s/%s%s)", lp.Description, ipPort, strings.ToLower(string(lp.Protocol)), lp.IPFamily)
|
||||
}
|
||||
|
||||
// Closeable closes an opened LocalPort.
|
||||
type Closeable interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
// PortOpener can open a LocalPort and allows later closing it.
|
||||
type PortOpener interface {
|
||||
OpenLocalPort(lp *LocalPort) (Closeable, error)
|
||||
}
|
||||
|
||||
type listenPortOpener struct{}
|
||||
|
||||
// ListenPortOpener opens ports by calling bind() and listen().
|
||||
var ListenPortOpener listenPortOpener
|
||||
|
||||
// OpenLocalPort holds the given local port open.
|
||||
func (l *listenPortOpener) OpenLocalPort(lp *LocalPort) (Closeable, error) {
|
||||
return openLocalPort(lp)
|
||||
}
|
||||
|
||||
func openLocalPort(lp *LocalPort) (Closeable, error) {
|
||||
var socket Closeable
|
||||
hostPort := net.JoinHostPort(lp.IP, strconv.Itoa(lp.Port))
|
||||
switch lp.Protocol {
|
||||
case TCP:
|
||||
network := "tcp" + string(lp.IPFamily)
|
||||
listener, err := net.Listen(network, hostPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
socket = listener
|
||||
case UDP:
|
||||
network := "udp" + string(lp.IPFamily)
|
||||
addr, err := net.ResolveUDPAddr(network, hostPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := net.ListenUDP(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
socket = conn
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown protocol %q", lp.Protocol)
|
||||
}
|
||||
return socket, nil
|
||||
}
|
||||
2
vendor/k8s.io/utils/path/file.go
generated
vendored
2
vendor/k8s.io/utils/path/file.go
generated
vendored
@@ -30,7 +30,7 @@ const (
|
||||
// the symlink exists.
|
||||
CheckFollowSymlink LinkTreatment = iota
|
||||
|
||||
// CheckSymlinkOnly does not follow the symlink and verfies only that they
|
||||
// CheckSymlinkOnly does not follow the symlink and verifies only that they
|
||||
// symlink itself exists.
|
||||
CheckSymlinkOnly
|
||||
)
|
||||
|
||||
3
vendor/k8s.io/utils/pointer/README.md
generated
vendored
Normal file
3
vendor/k8s.io/utils/pointer/README.md
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Pointer
|
||||
|
||||
This package provides some functions for pointer-based operations.
|
||||
67
vendor/k8s.io/utils/trace/README.md
generated
vendored
Normal file
67
vendor/k8s.io/utils/trace/README.md
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
# Trace
|
||||
|
||||
This package provides an interface for recording the latency of operations and logging details
|
||||
about all operations where the latency exceeds a limit.
|
||||
|
||||
## Usage
|
||||
|
||||
To create a trace:
|
||||
|
||||
```go
|
||||
func doSomething() {
|
||||
opTrace := trace.New("operation", Field{Key: "fieldKey1", Value: "fieldValue1"})
|
||||
defer opTrace.LogIfLong(100 * time.Millisecond)
|
||||
// do something
|
||||
}
|
||||
```
|
||||
|
||||
To split an trace into multiple steps:
|
||||
|
||||
```go
|
||||
func doSomething() {
|
||||
opTrace := trace.New("operation")
|
||||
defer opTrace.LogIfLong(100 * time.Millisecond)
|
||||
// do step 1
|
||||
opTrace.Step("step1", Field{Key: "stepFieldKey1", Value: "stepFieldValue1"})
|
||||
// do step 2
|
||||
opTrace.Step("step2")
|
||||
}
|
||||
```
|
||||
|
||||
To nest traces:
|
||||
|
||||
```go
|
||||
func doSomething() {
|
||||
rootTrace := trace.New("rootOperation")
|
||||
defer rootTrace.LogIfLong(100 * time.Millisecond)
|
||||
|
||||
func() {
|
||||
nestedTrace := rootTrace.Nest("nested", Field{Key: "nestedFieldKey1", Value: "nestedFieldValue1"})
|
||||
defer nestedTrace.LogIfLong(50 * time.Millisecond)
|
||||
// do nested operation
|
||||
}()
|
||||
}
|
||||
```
|
||||
|
||||
Traces can also be logged unconditionally or introspected:
|
||||
|
||||
```go
|
||||
opTrace.TotalTime() // Duration since the Trace was created
|
||||
opTrace.Log() // unconditionally log the trace
|
||||
```
|
||||
|
||||
### Using context.Context to nest traces
|
||||
|
||||
`context.Context` can be used to manage nested traces. Create traces by calling `trace.GetTraceFromContext(ctx).Nest`.
|
||||
This is safe even if there is no parent trace already in the context because `(*(Trace)nil).Nest()` returns
|
||||
a top level trace.
|
||||
|
||||
```go
|
||||
func doSomething(ctx context.Context) {
|
||||
opTrace := trace.FromContext(ctx).Nest("operation") // create a trace, possibly nested
|
||||
ctx = trace.ContextWithTrace(ctx, opTrace) // make this trace the parent trace of the context
|
||||
defer opTrace.LogIfLong(50 * time.Millisecond)
|
||||
|
||||
doSomethingElse(ctx)
|
||||
}
|
||||
```
|
||||
243
vendor/k8s.io/utils/trace/trace.go
generated
vendored
243
vendor/k8s.io/utils/trace/trace.go
generated
vendored
@@ -18,11 +18,12 @@ package trace
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"k8s.io/klog"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
// Field is a key value pair that provides additional details about the trace.
|
||||
@@ -44,19 +45,83 @@ func writeFields(b *bytes.Buffer, l []Field) {
|
||||
}
|
||||
}
|
||||
|
||||
func writeTraceItemSummary(b *bytes.Buffer, msg string, totalTime time.Duration, startTime time.Time, fields []Field) {
|
||||
b.WriteString(fmt.Sprintf("%q ", msg))
|
||||
if len(fields) > 0 {
|
||||
writeFields(b, fields)
|
||||
b.WriteString(" ")
|
||||
}
|
||||
|
||||
b.WriteString(fmt.Sprintf("%vms (%v)", durationToMilliseconds(totalTime), startTime.Format("15:04:00.000")))
|
||||
}
|
||||
|
||||
func durationToMilliseconds(timeDuration time.Duration) int64 {
|
||||
return timeDuration.Nanoseconds() / 1e6
|
||||
}
|
||||
|
||||
type traceItem interface {
|
||||
// time returns when the trace was recorded as completed.
|
||||
time() time.Time
|
||||
// writeItem outputs the traceItem to the buffer. If stepThreshold is non-nil, only output the
|
||||
// traceItem if its the duration exceeds the stepThreshold.
|
||||
// Each line of output is prefixed by formatter to visually indent nested items.
|
||||
writeItem(b *bytes.Buffer, formatter string, startTime time.Time, stepThreshold *time.Duration)
|
||||
}
|
||||
|
||||
type traceStep struct {
|
||||
stepTime time.Time
|
||||
msg string
|
||||
fields []Field
|
||||
}
|
||||
|
||||
func (s traceStep) time() time.Time {
|
||||
return s.stepTime
|
||||
}
|
||||
|
||||
func (s traceStep) writeItem(b *bytes.Buffer, formatter string, startTime time.Time, stepThreshold *time.Duration) {
|
||||
stepDuration := s.stepTime.Sub(startTime)
|
||||
if stepThreshold == nil || *stepThreshold == 0 || stepDuration >= *stepThreshold {
|
||||
b.WriteString(fmt.Sprintf("%s---", formatter))
|
||||
writeTraceItemSummary(b, s.msg, stepDuration, s.stepTime, s.fields)
|
||||
}
|
||||
}
|
||||
|
||||
// Trace keeps track of a set of "steps" and allows us to log a specific
|
||||
// step if it took longer than its share of the total allowed time
|
||||
type Trace struct {
|
||||
name string
|
||||
fields []Field
|
||||
startTime time.Time
|
||||
steps []traceStep
|
||||
name string
|
||||
fields []Field
|
||||
threshold *time.Duration
|
||||
startTime time.Time
|
||||
endTime *time.Time
|
||||
traceItems []traceItem
|
||||
parentTrace *Trace
|
||||
}
|
||||
|
||||
func (t *Trace) time() time.Time {
|
||||
if t.endTime != nil {
|
||||
return *t.endTime
|
||||
}
|
||||
return t.startTime // if the trace is incomplete, don't assume an end time
|
||||
}
|
||||
|
||||
func (t *Trace) writeItem(b *bytes.Buffer, formatter string, startTime time.Time, stepThreshold *time.Duration) {
|
||||
if t.durationIsWithinThreshold() {
|
||||
b.WriteString(fmt.Sprintf("%v[", formatter))
|
||||
writeTraceItemSummary(b, t.name, t.TotalTime(), t.startTime, t.fields)
|
||||
if st := t.calculateStepThreshold(); st != nil {
|
||||
stepThreshold = st
|
||||
}
|
||||
t.writeTraceSteps(b, formatter+" ", stepThreshold)
|
||||
b.WriteString("]")
|
||||
return
|
||||
}
|
||||
// If the trace should not be written, still check for nested traces that should be written
|
||||
for _, s := range t.traceItems {
|
||||
if nestedTrace, ok := s.(*Trace); ok {
|
||||
nestedTrace.writeItem(b, formatter, startTime, stepThreshold)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a Trace with the specified name. The name identifies the operation to be traced. The
|
||||
@@ -69,63 +134,145 @@ func New(name string, fields ...Field) *Trace {
|
||||
// how long it took. The Fields add key value pairs to provide additional details about the trace
|
||||
// step.
|
||||
func (t *Trace) Step(msg string, fields ...Field) {
|
||||
if t.steps == nil {
|
||||
if t.traceItems == nil {
|
||||
// traces almost always have less than 6 steps, do this to avoid more than a single allocation
|
||||
t.steps = make([]traceStep, 0, 6)
|
||||
t.traceItems = make([]traceItem, 0, 6)
|
||||
}
|
||||
t.steps = append(t.steps, traceStep{stepTime: time.Now(), msg: msg, fields: fields})
|
||||
t.traceItems = append(t.traceItems, traceStep{stepTime: time.Now(), msg: msg, fields: fields})
|
||||
}
|
||||
|
||||
// Log is used to dump all the steps in the Trace
|
||||
// Nest adds a nested trace with the given message and fields and returns it.
|
||||
// As a convenience, if the receiver is nil, returns a top level trace. This allows
|
||||
// one to call FromContext(ctx).Nest without having to check if the trace
|
||||
// in the context is nil.
|
||||
func (t *Trace) Nest(msg string, fields ...Field) *Trace {
|
||||
newTrace := New(msg, fields...)
|
||||
if t != nil {
|
||||
newTrace.parentTrace = t
|
||||
t.traceItems = append(t.traceItems, newTrace)
|
||||
}
|
||||
return newTrace
|
||||
}
|
||||
|
||||
// Log is used to dump all the steps in the Trace. It also logs the nested trace messages using indentation.
|
||||
// If the Trace is nested it is not immediately logged. Instead, it is logged when the trace it is nested within
|
||||
// is logged.
|
||||
func (t *Trace) Log() {
|
||||
// an explicit logging request should dump all the steps out at the higher level
|
||||
t.logWithStepThreshold(0)
|
||||
}
|
||||
|
||||
func (t *Trace) logWithStepThreshold(stepThreshold time.Duration) {
|
||||
var buffer bytes.Buffer
|
||||
tracenum := rand.Int31()
|
||||
endTime := time.Now()
|
||||
|
||||
totalTime := endTime.Sub(t.startTime)
|
||||
buffer.WriteString(fmt.Sprintf("Trace[%d]: %q ", tracenum, t.name))
|
||||
if len(t.fields) > 0 {
|
||||
writeFields(&buffer, t.fields)
|
||||
buffer.WriteString(" ")
|
||||
t.endTime = &endTime
|
||||
// an explicit logging request should dump all the steps out at the higher level
|
||||
if t.parentTrace == nil { // We don't start logging until Log or LogIfLong is called on the root trace
|
||||
t.logTrace()
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf("(started: %v) (total time: %v):\n", t.startTime, totalTime))
|
||||
lastStepTime := t.startTime
|
||||
for _, step := range t.steps {
|
||||
stepDuration := step.stepTime.Sub(lastStepTime)
|
||||
if stepThreshold == 0 || stepDuration > stepThreshold || klog.V(4) {
|
||||
buffer.WriteString(fmt.Sprintf("Trace[%d]: [%v] [%v] ", tracenum, step.stepTime.Sub(t.startTime), stepDuration))
|
||||
buffer.WriteString(step.msg)
|
||||
if len(step.fields) > 0 {
|
||||
buffer.WriteString(" ")
|
||||
writeFields(&buffer, step.fields)
|
||||
}
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
lastStepTime = step.stepTime
|
||||
}
|
||||
stepDuration := endTime.Sub(lastStepTime)
|
||||
if stepThreshold == 0 || stepDuration > stepThreshold || klog.V(4) {
|
||||
buffer.WriteString(fmt.Sprintf("Trace[%d]: [%v] [%v] END\n", tracenum, endTime.Sub(t.startTime), stepDuration))
|
||||
}
|
||||
|
||||
klog.Info(buffer.String())
|
||||
}
|
||||
|
||||
// LogIfLong is used to dump steps that took longer than its share
|
||||
// LogIfLong only logs the trace if the duration of the trace exceeds the threshold.
|
||||
// Only steps that took longer than their share or the given threshold are logged.
|
||||
// If klog is at verbosity level 4 or higher, the trace and its steps are logged regardless of threshold.
|
||||
// If the Trace is nested it is not immediately logged. Instead, it is logged when the trace it is nested within
|
||||
// is logged.
|
||||
func (t *Trace) LogIfLong(threshold time.Duration) {
|
||||
if time.Since(t.startTime) >= threshold {
|
||||
// if any step took more than it's share of the total allowed time, it deserves a higher log level
|
||||
stepThreshold := threshold / time.Duration(len(t.steps)+1)
|
||||
t.logWithStepThreshold(stepThreshold)
|
||||
if !klog.V(4).Enabled() { // don't set threshold if verbosity is level 4 of higher
|
||||
t.threshold = &threshold
|
||||
}
|
||||
t.Log()
|
||||
}
|
||||
|
||||
// logTopLevelTraces finds all traces in a hierarchy of nested traces that should be logged but do not have any
|
||||
// parents that will be logged, due to threshold limits, and logs them as top level traces.
|
||||
func (t *Trace) logTrace() {
|
||||
if t.durationIsWithinThreshold() {
|
||||
var buffer bytes.Buffer
|
||||
traceNum := rand.Int31()
|
||||
|
||||
totalTime := t.endTime.Sub(t.startTime)
|
||||
buffer.WriteString(fmt.Sprintf("Trace[%d]: %q ", traceNum, t.name))
|
||||
if len(t.fields) > 0 {
|
||||
writeFields(&buffer, t.fields)
|
||||
buffer.WriteString(" ")
|
||||
}
|
||||
|
||||
// if any step took more than it's share of the total allowed time, it deserves a higher log level
|
||||
buffer.WriteString(fmt.Sprintf("(%v) (total time: %vms):", t.startTime.Format("02-Jan-2006 15:04:00.000"), totalTime.Milliseconds()))
|
||||
stepThreshold := t.calculateStepThreshold()
|
||||
t.writeTraceSteps(&buffer, fmt.Sprintf("\nTrace[%d]: ", traceNum), stepThreshold)
|
||||
buffer.WriteString(fmt.Sprintf("\nTrace[%d]: [%v] [%v] END\n", traceNum, t.endTime.Sub(t.startTime), totalTime))
|
||||
|
||||
klog.Info(buffer.String())
|
||||
return
|
||||
}
|
||||
|
||||
// If the trace should not be logged, still check if nested traces should be logged
|
||||
for _, s := range t.traceItems {
|
||||
if nestedTrace, ok := s.(*Trace); ok {
|
||||
nestedTrace.logTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Trace) writeTraceSteps(b *bytes.Buffer, formatter string, stepThreshold *time.Duration) {
|
||||
lastStepTime := t.startTime
|
||||
for _, stepOrTrace := range t.traceItems {
|
||||
stepOrTrace.writeItem(b, formatter, lastStepTime, stepThreshold)
|
||||
lastStepTime = stepOrTrace.time()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Trace) durationIsWithinThreshold() bool {
|
||||
if t.endTime == nil { // we don't assume incomplete traces meet the threshold
|
||||
return false
|
||||
}
|
||||
return t.threshold == nil || *t.threshold == 0 || t.endTime.Sub(t.startTime) >= *t.threshold
|
||||
}
|
||||
|
||||
// TotalTime can be used to figure out how long it took since the Trace was created
|
||||
func (t *Trace) TotalTime() time.Duration {
|
||||
return time.Since(t.startTime)
|
||||
}
|
||||
|
||||
// calculateStepThreshold returns a threshold for the individual steps of a trace, or nil if there is no threshold and
|
||||
// all steps should be written.
|
||||
func (t *Trace) calculateStepThreshold() *time.Duration {
|
||||
if t.threshold == nil {
|
||||
return nil
|
||||
}
|
||||
lenTrace := len(t.traceItems) + 1
|
||||
traceThreshold := *t.threshold
|
||||
for _, s := range t.traceItems {
|
||||
nestedTrace, ok := s.(*Trace)
|
||||
if ok && nestedTrace.threshold != nil {
|
||||
traceThreshold = traceThreshold - *nestedTrace.threshold
|
||||
lenTrace--
|
||||
}
|
||||
}
|
||||
|
||||
// the limit threshold is used when the threshold(
|
||||
//remaining after subtracting that of the child trace) is getting very close to zero to prevent unnecessary logging
|
||||
limitThreshold := *t.threshold / 4
|
||||
if traceThreshold < limitThreshold {
|
||||
traceThreshold = limitThreshold
|
||||
lenTrace = len(t.traceItems) + 1
|
||||
}
|
||||
|
||||
stepThreshold := traceThreshold / time.Duration(lenTrace)
|
||||
return &stepThreshold
|
||||
}
|
||||
|
||||
// ContextTraceKey provides a common key for traces in context.Context values.
|
||||
type ContextTraceKey struct{}
|
||||
|
||||
// FromContext returns the trace keyed by ContextTraceKey in the context values, if one
|
||||
// is present, or nil If there is no trace in the Context.
|
||||
// It is safe to call Nest() on the returned value even if it is nil because ((*Trace)nil).Nest returns a top level
|
||||
// trace.
|
||||
func FromContext(ctx context.Context) *Trace {
|
||||
if v, ok := ctx.Value(ContextTraceKey{}).(*Trace); ok {
|
||||
return v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextWithTrace returns a context with trace included in the context values, keyed by ContextTraceKey.
|
||||
func ContextWithTrace(ctx context.Context, trace *Trace) context.Context {
|
||||
return context.WithValue(ctx, ContextTraceKey{}, trace)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user