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}