github.com/cloudwego/kitex@v0.9.0/pkg/rpcinfo/tracer.go (about) 1 /* 2 * Copyright 2021 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package rpcinfo 18 19 import ( 20 "context" 21 "runtime/debug" 22 23 "github.com/cloudwego/kitex/internal" 24 "github.com/cloudwego/kitex/internal/stream" 25 "github.com/cloudwego/kitex/pkg/klog" 26 "github.com/cloudwego/kitex/pkg/stats" 27 ) 28 29 // StreamEventReporter should be implemented by any tracer that wants to report stream events 30 type StreamEventReporter interface { 31 // ReportStreamEvent is for collecting Recv/Send events on stream 32 // NOTE: The callee should NOT hold references to event, which may be recycled later 33 ReportStreamEvent(ctx context.Context, ri RPCInfo, event Event) 34 } 35 36 // TraceController controls tracers. 37 type TraceController struct { 38 tracers []stats.Tracer 39 streamEventReporters []StreamEventReporter 40 } 41 42 // Append appends a new tracer to the controller. 43 func (c *TraceController) Append(col stats.Tracer) { 44 c.tracers = append(c.tracers, col) 45 if reporter, ok := col.(StreamEventReporter); ok { 46 c.streamEventReporters = append(c.streamEventReporters, reporter) 47 } 48 } 49 50 // DoStart starts the tracers. 51 func (c *TraceController) DoStart(ctx context.Context, ri RPCInfo) context.Context { 52 defer c.tryRecover(ctx) 53 Record(ctx, ri, stats.RPCStart, nil) 54 55 for _, col := range c.tracers { 56 ctx = col.Start(ctx) 57 } 58 return ctx 59 } 60 61 // DoFinish calls the tracers in reversed order. 62 func (c *TraceController) DoFinish(ctx context.Context, ri RPCInfo, err error) { 63 defer c.tryRecover(ctx) 64 Record(ctx, ri, stats.RPCFinish, err) 65 if err != nil { 66 rpcStats := AsMutableRPCStats(ri.Stats()) 67 rpcStats.SetError(err) 68 } 69 70 // reverse the order 71 for i := len(c.tracers) - 1; i >= 0; i-- { 72 c.tracers[i].Finish(ctx) 73 } 74 } 75 76 func buildStreamingEvent(statsEvent stats.Event, err error) Event { 77 if err == nil { 78 return NewEvent(statsEvent, stats.StatusInfo, "") 79 } else { 80 return NewEvent(statsEvent, stats.StatusError, err.Error()) 81 } 82 } 83 84 // ReportStreamEvent is for collecting Recv/Send events on stream 85 func (c *TraceController) ReportStreamEvent(ctx context.Context, statsEvent stats.Event, err error) { 86 if !c.HasStreamEventReporter() { 87 return 88 } 89 defer c.tryRecover(ctx) 90 event := buildStreamingEvent(statsEvent, err) 91 defer func() { 92 if recyclable, ok := event.(internal.Reusable); ok { 93 recyclable.Recycle() 94 } 95 }() 96 // RPCInfo is likely to be used by each reporter 97 ri := GetRPCInfo(ctx) 98 for i := len(c.streamEventReporters) - 1; i >= 0; i-- { 99 c.streamEventReporters[i].ReportStreamEvent(ctx, ri, event) 100 } 101 } 102 103 // GetStreamEventHandler returns the stream event handler 104 // If there's no StreamEventReporter, nil is returned for client/server to skip adding tracing middlewares 105 func (c *TraceController) GetStreamEventHandler() stream.StreamEventHandler { 106 if c.HasStreamEventReporter() { 107 return c.ReportStreamEvent 108 } 109 return nil 110 } 111 112 // HasTracer reports whether there exists any tracer. 113 func (c *TraceController) HasTracer() bool { 114 return c != nil && len(c.tracers) > 0 115 } 116 117 // HasStreamEventReporter reports whether there exists any StreamEventReporter. 118 func (c *TraceController) HasStreamEventReporter() bool { 119 return c != nil && len(c.streamEventReporters) > 0 120 } 121 122 func (c *TraceController) tryRecover(ctx context.Context) { 123 if err := recover(); err != nil { 124 klog.CtxWarnf(ctx, "Panic happened during tracer call. This doesn't affect the rpc call, but may lead to lack of monitor data such as metrics and logs: error=%s, stack=%s", err, string(debug.Stack())) 125 } 126 }