github.com/blend/go-sdk@v1.20220411.3/grpcutil/rpc_event.go (about)

     1  /*
     2  
     3  Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file.
     5  
     6  */
     7  
     8  package grpcutil
     9  
    10  import (
    11  	"context"
    12  	"fmt"
    13  	"io"
    14  	"time"
    15  
    16  	"google.golang.org/grpc/codes"
    17  	"google.golang.org/grpc/status"
    18  
    19  	"github.com/blend/go-sdk/ansi"
    20  	"github.com/blend/go-sdk/logger"
    21  	"github.com/blend/go-sdk/timeutil"
    22  )
    23  
    24  // Logger flags
    25  const (
    26  	FlagRPC = "rpc"
    27  )
    28  
    29  // these are compile time assertions
    30  var (
    31  	_ logger.Event        = (*RPCEvent)(nil)
    32  	_ logger.TextWritable = (*RPCEvent)(nil)
    33  	_ logger.JSONWritable = (*RPCEvent)(nil)
    34  )
    35  
    36  // NewRPCEvent creates a new rpc event.
    37  func NewRPCEvent(method string, elapsed time.Duration, options ...RPCEventOption) RPCEvent {
    38  	rpe := RPCEvent{
    39  		Engine:  EngineGRPC,
    40  		Method:  method,
    41  		Elapsed: elapsed,
    42  	}
    43  	for _, opt := range options {
    44  		opt(&rpe)
    45  	}
    46  	return rpe
    47  }
    48  
    49  // NewRPCEventListener returns a new web request event listener.
    50  func NewRPCEventListener(listener func(context.Context, RPCEvent)) logger.Listener {
    51  	return func(ctx context.Context, e logger.Event) {
    52  		if typed, isTyped := e.(RPCEvent); isTyped {
    53  			listener(ctx, typed)
    54  		}
    55  	}
    56  }
    57  
    58  // NewRPCEventFilter returns a new rpc event filter.
    59  func NewRPCEventFilter(filter func(context.Context, RPCEvent) (RPCEvent, bool)) logger.Filter {
    60  	return func(ctx context.Context, e logger.Event) (logger.Event, bool) {
    61  		if typed, isTyped := e.(RPCEvent); isTyped {
    62  			return filter(ctx, typed)
    63  		}
    64  		return e, false
    65  	}
    66  }
    67  
    68  // RPCEventOption is a mutator for RPCEvents.
    69  type RPCEventOption func(*RPCEvent)
    70  
    71  // OptRPCEngine sets a field on the event.
    72  func OptRPCEngine(value string) RPCEventOption {
    73  	return func(e *RPCEvent) { e.Engine = value }
    74  }
    75  
    76  // OptRPCPeer sets a field on the event.
    77  func OptRPCPeer(value string) RPCEventOption {
    78  	return func(e *RPCEvent) { e.Peer = value }
    79  }
    80  
    81  // OptRPCMethod sets a field on the event.
    82  func OptRPCMethod(value string) RPCEventOption {
    83  	return func(e *RPCEvent) { e.Method = value }
    84  }
    85  
    86  // OptRPCUserAgent sets a field on the event.
    87  func OptRPCUserAgent(value string) RPCEventOption {
    88  	return func(e *RPCEvent) { e.UserAgent = value }
    89  }
    90  
    91  // OptRPCAuthority sets a field on the event.
    92  func OptRPCAuthority(value string) RPCEventOption {
    93  	return func(e *RPCEvent) { e.Authority = value }
    94  }
    95  
    96  // OptRPCContentType sets a field on the event.
    97  func OptRPCContentType(value string) RPCEventOption {
    98  	return func(e *RPCEvent) { e.ContentType = value }
    99  }
   100  
   101  // OptRPCElapsed sets a field on the event.
   102  func OptRPCElapsed(value time.Duration) RPCEventOption {
   103  	return func(e *RPCEvent) { e.Elapsed = value }
   104  }
   105  
   106  // OptRPCErr sets a field on the event.
   107  func OptRPCErr(value error) RPCEventOption {
   108  	return func(e *RPCEvent) { e.Err = value }
   109  }
   110  
   111  // RPCEvent is an event type for rpc
   112  type RPCEvent struct {
   113  	Engine      string
   114  	Peer        string
   115  	Method      string
   116  	UserAgent   string
   117  	Authority   string
   118  	ContentType string
   119  	Elapsed     time.Duration
   120  	Err         error
   121  }
   122  
   123  // GetFlag implements Event.
   124  func (e RPCEvent) GetFlag() string { return FlagRPC }
   125  
   126  // WriteText implements TextWritable.
   127  func (e RPCEvent) WriteText(tf logger.TextFormatter, wr io.Writer) {
   128  	if e.Engine != "" {
   129  		fmt.Fprint(wr, "[")
   130  		fmt.Fprint(wr, tf.Colorize(e.Engine, ansi.ColorLightWhite))
   131  		fmt.Fprint(wr, "]")
   132  	}
   133  	if e.Method != "" {
   134  		if e.Engine != "" {
   135  			fmt.Fprint(wr, logger.Space)
   136  		}
   137  		fmt.Fprint(wr, tf.Colorize(e.Method, ansi.ColorBlue))
   138  	}
   139  	if e.Peer != "" {
   140  		fmt.Fprint(wr, logger.Space)
   141  		fmt.Fprint(wr, e.Peer)
   142  	}
   143  	if e.Authority != "" {
   144  		fmt.Fprint(wr, logger.Space)
   145  		fmt.Fprint(wr, e.Authority)
   146  	}
   147  	if e.UserAgent != "" {
   148  		fmt.Fprint(wr, logger.Space)
   149  		fmt.Fprint(wr, e.UserAgent)
   150  	}
   151  	if e.ContentType != "" {
   152  		fmt.Fprint(wr, logger.Space)
   153  		fmt.Fprint(wr, e.ContentType)
   154  	}
   155  
   156  	fmt.Fprint(wr, logger.Space)
   157  	fmt.Fprint(wr, e.Elapsed.String())
   158  
   159  	if e.Err != nil {
   160  		fmt.Fprint(wr, logger.Space)
   161  
   162  		if s, ok := status.FromError(e.Err); ok {
   163  			fmt.Fprint(wr, tf.Colorize(fmt.Sprintf("failed (%[1]d - %[1]v)", s.Code()), ansi.ColorRed))
   164  		} else {
   165  			fmt.Fprint(wr, tf.Colorize("failed", ansi.ColorRed))
   166  		}
   167  	}
   168  }
   169  
   170  // Decompose implements JSONWritable.
   171  func (e RPCEvent) Decompose() map[string]interface{} {
   172  	var code codes.Code
   173  	if s, ok := status.FromError(e.Err); ok {
   174  		code = s.Code()
   175  	}
   176  	return map[string]interface{}{
   177  		"engine":      e.Engine,
   178  		"peer":        e.Peer,
   179  		"method":      e.Method,
   180  		"userAgent":   e.UserAgent,
   181  		"authority":   e.Authority,
   182  		"contentType": e.ContentType,
   183  		"elapsed":     timeutil.Milliseconds(e.Elapsed),
   184  		"err":         e.Err,
   185  		"code":        code,
   186  	}
   187  }