mirror of https://github.com/docker/cli.git
205 lines
5.5 KiB
Go
205 lines
5.5 KiB
Go
/*
|
|
*
|
|
* Copyright 2018 gRPC 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 channelz
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"google.golang.org/grpc/grpclog"
|
|
)
|
|
|
|
const (
|
|
defaultMaxTraceEntry int32 = 30
|
|
)
|
|
|
|
var maxTraceEntry = defaultMaxTraceEntry
|
|
|
|
// SetMaxTraceEntry sets maximum number of trace entries per entity (i.e.
|
|
// channel/subchannel). Setting it to 0 will disable channel tracing.
|
|
func SetMaxTraceEntry(i int32) {
|
|
atomic.StoreInt32(&maxTraceEntry, i)
|
|
}
|
|
|
|
// ResetMaxTraceEntryToDefault resets the maximum number of trace entries per
|
|
// entity to default.
|
|
func ResetMaxTraceEntryToDefault() {
|
|
atomic.StoreInt32(&maxTraceEntry, defaultMaxTraceEntry)
|
|
}
|
|
|
|
func getMaxTraceEntry() int {
|
|
i := atomic.LoadInt32(&maxTraceEntry)
|
|
return int(i)
|
|
}
|
|
|
|
// traceEvent is an internal representation of a single trace event
|
|
type traceEvent struct {
|
|
// Desc is a simple description of the trace event.
|
|
Desc string
|
|
// Severity states the severity of this trace event.
|
|
Severity Severity
|
|
// Timestamp is the event time.
|
|
Timestamp time.Time
|
|
// RefID is the id of the entity that gets referenced in the event. RefID is 0 if no other entity is
|
|
// involved in this event.
|
|
// e.g. SubChannel (id: 4[]) Created. --> RefID = 4, RefName = "" (inside [])
|
|
RefID int64
|
|
// RefName is the reference name for the entity that gets referenced in the event.
|
|
RefName string
|
|
// RefType indicates the referenced entity type, i.e Channel or SubChannel.
|
|
RefType RefChannelType
|
|
}
|
|
|
|
// TraceEvent is what the caller of AddTraceEvent should provide to describe the
|
|
// event to be added to the channel trace.
|
|
//
|
|
// The Parent field is optional. It is used for an event that will be recorded
|
|
// in the entity's parent trace.
|
|
type TraceEvent struct {
|
|
Desc string
|
|
Severity Severity
|
|
Parent *TraceEvent
|
|
}
|
|
|
|
type ChannelTrace struct {
|
|
cm *channelMap
|
|
clearCalled bool
|
|
CreationTime time.Time
|
|
EventNum int64
|
|
mu sync.Mutex
|
|
Events []*traceEvent
|
|
}
|
|
|
|
func (c *ChannelTrace) copy() *ChannelTrace {
|
|
return &ChannelTrace{
|
|
CreationTime: c.CreationTime,
|
|
EventNum: c.EventNum,
|
|
Events: append(([]*traceEvent)(nil), c.Events...),
|
|
}
|
|
}
|
|
|
|
func (c *ChannelTrace) append(e *traceEvent) {
|
|
c.mu.Lock()
|
|
if len(c.Events) == getMaxTraceEntry() {
|
|
del := c.Events[0]
|
|
c.Events = c.Events[1:]
|
|
if del.RefID != 0 {
|
|
// start recursive cleanup in a goroutine to not block the call originated from grpc.
|
|
go func() {
|
|
// need to acquire c.cm.mu lock to call the unlocked attemptCleanup func.
|
|
c.cm.mu.Lock()
|
|
c.cm.decrTraceRefCount(del.RefID)
|
|
c.cm.mu.Unlock()
|
|
}()
|
|
}
|
|
}
|
|
e.Timestamp = time.Now()
|
|
c.Events = append(c.Events, e)
|
|
c.EventNum++
|
|
c.mu.Unlock()
|
|
}
|
|
|
|
func (c *ChannelTrace) clear() {
|
|
if c.clearCalled {
|
|
return
|
|
}
|
|
c.clearCalled = true
|
|
c.mu.Lock()
|
|
for _, e := range c.Events {
|
|
if e.RefID != 0 {
|
|
// caller should have already held the c.cm.mu lock.
|
|
c.cm.decrTraceRefCount(e.RefID)
|
|
}
|
|
}
|
|
c.mu.Unlock()
|
|
}
|
|
|
|
// Severity is the severity level of a trace event.
|
|
// The canonical enumeration of all valid values is here:
|
|
// https://github.com/grpc/grpc-proto/blob/9b13d199cc0d4703c7ea26c9c330ba695866eb23/grpc/channelz/v1/channelz.proto#L126.
|
|
type Severity int
|
|
|
|
const (
|
|
// CtUnknown indicates unknown severity of a trace event.
|
|
CtUnknown Severity = iota
|
|
// CtInfo indicates info level severity of a trace event.
|
|
CtInfo
|
|
// CtWarning indicates warning level severity of a trace event.
|
|
CtWarning
|
|
// CtError indicates error level severity of a trace event.
|
|
CtError
|
|
)
|
|
|
|
// RefChannelType is the type of the entity being referenced in a trace event.
|
|
type RefChannelType int
|
|
|
|
const (
|
|
// RefUnknown indicates an unknown entity type, the zero value for this type.
|
|
RefUnknown RefChannelType = iota
|
|
// RefChannel indicates the referenced entity is a Channel.
|
|
RefChannel
|
|
// RefSubChannel indicates the referenced entity is a SubChannel.
|
|
RefSubChannel
|
|
// RefServer indicates the referenced entity is a Server.
|
|
RefServer
|
|
// RefListenSocket indicates the referenced entity is a ListenSocket.
|
|
RefListenSocket
|
|
// RefNormalSocket indicates the referenced entity is a NormalSocket.
|
|
RefNormalSocket
|
|
)
|
|
|
|
var refChannelTypeToString = map[RefChannelType]string{
|
|
RefUnknown: "Unknown",
|
|
RefChannel: "Channel",
|
|
RefSubChannel: "SubChannel",
|
|
RefServer: "Server",
|
|
RefListenSocket: "ListenSocket",
|
|
RefNormalSocket: "NormalSocket",
|
|
}
|
|
|
|
func (r RefChannelType) String() string {
|
|
return refChannelTypeToString[r]
|
|
}
|
|
|
|
// AddTraceEvent adds trace related to the entity with specified id, using the
|
|
// provided TraceEventDesc.
|
|
//
|
|
// If channelz is not turned ON, this will simply log the event descriptions.
|
|
func AddTraceEvent(l grpclog.DepthLoggerV2, e Entity, depth int, desc *TraceEvent) {
|
|
// Log only the trace description associated with the bottom most entity.
|
|
d := fmt.Sprintf("[%s]%s", e, desc.Desc)
|
|
switch desc.Severity {
|
|
case CtUnknown, CtInfo:
|
|
l.InfoDepth(depth+1, d)
|
|
case CtWarning:
|
|
l.WarningDepth(depth+1, d)
|
|
case CtError:
|
|
l.ErrorDepth(depth+1, d)
|
|
}
|
|
|
|
if getMaxTraceEntry() == 0 {
|
|
return
|
|
}
|
|
if IsOn() {
|
|
db.traceEvent(e.id(), desc)
|
|
}
|
|
}
|