| /* |
| * |
| * 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 |
| } |
| |
| // ChannelTrace provides tracing information for a channel. |
| // It tracks various events and metadata related to the channel's lifecycle |
| // and operations. |
| type ChannelTrace struct { |
| cm *channelMap |
| clearCalled bool |
| // The time when the trace was created. |
| CreationTime time.Time |
| // 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 |
| } |
| |
| 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", |
| } |
| |
| // String returns a string representation of the RefChannelType |
| 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) |
| } |
| } |