| 
									
										
										
										
											2024-04-11 11:46:18 +02:00
										 |  |  | /* | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-06 12:14:37 +01:00
										 |  |  | // ChannelTrace provides tracing information for a channel. | 
					
						
							|  |  |  | // It tracks various events and metadata related to the channel's lifecycle | 
					
						
							|  |  |  | // and operations. | 
					
						
							| 
									
										
										
										
											2024-04-11 11:46:18 +02:00
										 |  |  | type ChannelTrace struct { | 
					
						
							| 
									
										
										
										
											2025-02-06 12:14:37 +01:00
										 |  |  | 	cm          *channelMap | 
					
						
							|  |  |  | 	clearCalled bool | 
					
						
							|  |  |  | 	// The time when the trace was created. | 
					
						
							| 
									
										
										
										
											2024-04-11 11:46:18 +02:00
										 |  |  | 	CreationTime time.Time | 
					
						
							| 
									
										
										
										
											2025-02-06 12:14:37 +01:00
										 |  |  | 	// A counter for the number of events recorded in the | 
					
						
							|  |  |  | 	// trace. | 
					
						
							|  |  |  | 	EventNum int64 | 
					
						
							|  |  |  | 	mu       sync.Mutex | 
					
						
							|  |  |  | 	// A slice of traceEvent pointers representing the events recorded for | 
					
						
							|  |  |  | 	// this channel. | 
					
						
							|  |  |  | 	Events []*traceEvent | 
					
						
							| 
									
										
										
										
											2024-04-11 11:46:18 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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", | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-06 12:14:37 +01:00
										 |  |  | // String returns a string representation of the RefChannelType | 
					
						
							| 
									
										
										
										
											2024-04-11 11:46:18 +02:00
										 |  |  | 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) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |