trace.go

  1/*
  2 *
  3 * Copyright 2018 gRPC authors.
  4 *
  5 * Licensed under the Apache License, Version 2.0 (the "License");
  6 * you may not use this file except in compliance with the License.
  7 * You may obtain a copy of the License at
  8 *
  9 *     http://www.apache.org/licenses/LICENSE-2.0
 10 *
 11 * Unless required by applicable law or agreed to in writing, software
 12 * distributed under the License is distributed on an "AS IS" BASIS,
 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14 * See the License for the specific language governing permissions and
 15 * limitations under the License.
 16 *
 17 */
 18
 19package channelz
 20
 21import (
 22	"fmt"
 23	"sync"
 24	"sync/atomic"
 25	"time"
 26
 27	"google.golang.org/grpc/grpclog"
 28)
 29
 30const (
 31	defaultMaxTraceEntry int32 = 30
 32)
 33
 34var maxTraceEntry = defaultMaxTraceEntry
 35
 36// SetMaxTraceEntry sets maximum number of trace entries per entity (i.e.
 37// channel/subchannel).  Setting it to 0 will disable channel tracing.
 38func SetMaxTraceEntry(i int32) {
 39	atomic.StoreInt32(&maxTraceEntry, i)
 40}
 41
 42// ResetMaxTraceEntryToDefault resets the maximum number of trace entries per
 43// entity to default.
 44func ResetMaxTraceEntryToDefault() {
 45	atomic.StoreInt32(&maxTraceEntry, defaultMaxTraceEntry)
 46}
 47
 48func getMaxTraceEntry() int {
 49	i := atomic.LoadInt32(&maxTraceEntry)
 50	return int(i)
 51}
 52
 53// traceEvent is an internal representation of a single trace event
 54type traceEvent struct {
 55	// Desc is a simple description of the trace event.
 56	Desc string
 57	// Severity states the severity of this trace event.
 58	Severity Severity
 59	// Timestamp is the event time.
 60	Timestamp time.Time
 61	// RefID is the id of the entity that gets referenced in the event. RefID is 0 if no other entity is
 62	// involved in this event.
 63	// e.g. SubChannel (id: 4[]) Created. --> RefID = 4, RefName = "" (inside [])
 64	RefID int64
 65	// RefName is the reference name for the entity that gets referenced in the event.
 66	RefName string
 67	// RefType indicates the referenced entity type, i.e Channel or SubChannel.
 68	RefType RefChannelType
 69}
 70
 71// TraceEvent is what the caller of AddTraceEvent should provide to describe the
 72// event to be added to the channel trace.
 73//
 74// The Parent field is optional. It is used for an event that will be recorded
 75// in the entity's parent trace.
 76type TraceEvent struct {
 77	Desc     string
 78	Severity Severity
 79	Parent   *TraceEvent
 80}
 81
 82// ChannelTrace provides tracing information for a channel.
 83// It tracks various events and metadata related to the channel's lifecycle
 84// and operations.
 85type ChannelTrace struct {
 86	cm          *channelMap
 87	clearCalled bool
 88	// The time when the trace was created.
 89	CreationTime time.Time
 90	// A counter for the number of events recorded in the
 91	// trace.
 92	EventNum int64
 93	mu       sync.Mutex
 94	// A slice of traceEvent pointers representing the events recorded for
 95	// this channel.
 96	Events []*traceEvent
 97}
 98
 99func (c *ChannelTrace) copy() *ChannelTrace {
100	return &ChannelTrace{
101		CreationTime: c.CreationTime,
102		EventNum:     c.EventNum,
103		Events:       append(([]*traceEvent)(nil), c.Events...),
104	}
105}
106
107func (c *ChannelTrace) append(e *traceEvent) {
108	c.mu.Lock()
109	if len(c.Events) == getMaxTraceEntry() {
110		del := c.Events[0]
111		c.Events = c.Events[1:]
112		if del.RefID != 0 {
113			// start recursive cleanup in a goroutine to not block the call originated from grpc.
114			go func() {
115				// need to acquire c.cm.mu lock to call the unlocked attemptCleanup func.
116				c.cm.mu.Lock()
117				c.cm.decrTraceRefCount(del.RefID)
118				c.cm.mu.Unlock()
119			}()
120		}
121	}
122	e.Timestamp = time.Now()
123	c.Events = append(c.Events, e)
124	c.EventNum++
125	c.mu.Unlock()
126}
127
128func (c *ChannelTrace) clear() {
129	if c.clearCalled {
130		return
131	}
132	c.clearCalled = true
133	c.mu.Lock()
134	for _, e := range c.Events {
135		if e.RefID != 0 {
136			// caller should have already held the c.cm.mu lock.
137			c.cm.decrTraceRefCount(e.RefID)
138		}
139	}
140	c.mu.Unlock()
141}
142
143// Severity is the severity level of a trace event.
144// The canonical enumeration of all valid values is here:
145// https://github.com/grpc/grpc-proto/blob/9b13d199cc0d4703c7ea26c9c330ba695866eb23/grpc/channelz/v1/channelz.proto#L126.
146type Severity int
147
148const (
149	// CtUnknown indicates unknown severity of a trace event.
150	CtUnknown Severity = iota
151	// CtInfo indicates info level severity of a trace event.
152	CtInfo
153	// CtWarning indicates warning level severity of a trace event.
154	CtWarning
155	// CtError indicates error level severity of a trace event.
156	CtError
157)
158
159// RefChannelType is the type of the entity being referenced in a trace event.
160type RefChannelType int
161
162const (
163	// RefUnknown indicates an unknown entity type, the zero value for this type.
164	RefUnknown RefChannelType = iota
165	// RefChannel indicates the referenced entity is a Channel.
166	RefChannel
167	// RefSubChannel indicates the referenced entity is a SubChannel.
168	RefSubChannel
169	// RefServer indicates the referenced entity is a Server.
170	RefServer
171	// RefListenSocket indicates the referenced entity is a ListenSocket.
172	RefListenSocket
173	// RefNormalSocket indicates the referenced entity is a NormalSocket.
174	RefNormalSocket
175)
176
177var refChannelTypeToString = map[RefChannelType]string{
178	RefUnknown:      "Unknown",
179	RefChannel:      "Channel",
180	RefSubChannel:   "SubChannel",
181	RefServer:       "Server",
182	RefListenSocket: "ListenSocket",
183	RefNormalSocket: "NormalSocket",
184}
185
186// String returns a string representation of the RefChannelType
187func (r RefChannelType) String() string {
188	return refChannelTypeToString[r]
189}
190
191// AddTraceEvent adds trace related to the entity with specified id, using the
192// provided TraceEventDesc.
193//
194// If channelz is not turned ON, this will simply log the event descriptions.
195func AddTraceEvent(l grpclog.DepthLoggerV2, e Entity, depth int, desc *TraceEvent) {
196	// Log only the trace description associated with the bottom most entity.
197	d := fmt.Sprintf("[%s]%s", e, desc.Desc)
198	switch desc.Severity {
199	case CtUnknown, CtInfo:
200		l.InfoDepth(depth+1, d)
201	case CtWarning:
202		l.WarningDepth(depth+1, d)
203	case CtError:
204		l.ErrorDepth(depth+1, d)
205	}
206
207	if getMaxTraceEntry() == 0 {
208		return
209	}
210	if IsOn() {
211		db.traceEvent(e.id(), desc)
212	}
213}