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  }