gogrpcgrpc-go

How to combine outputs of different events in golang for func HandleRPC from "google.golang.org/grpc/stats" package


// HandleRPC implements per-RPC tracing and stats instrumentation.
func (c *statsHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
 switch rs := rs.(type) {

  case *stats.InHeader:

     if rs.Client {
        fmt.Printf(rs.Header)
    }

  case *stats.End:
    ign := false
    c.lock.RLock()
    ign = c.ignore
    c.lock.RUnlock()

    if !ign {
        duration := rs.EndTime.Sub(rs.BeginTime)

        var st string
        s, ok := status.FromError(rs.Error)
        if ok {
            st = s.Code().String()
        }

        c.results <- &callResult{rs.Error, st, duration, rs.EndTime}

        if c.hasLog {
            c.log.Debugw("Received RPC Stats",
                "statsID", c.id, "code", st, "error", rs.Error,
                "duration", duration, "stats", rs)
        }
    }
}

}

I have the response metadata coming on event *stats.InHeader and the rest of grpc stats on event *stats.End, I want to combine metadata and grpc stats into one struct object callResult at the end of the grpc call. I am quite not sure how to realize this, I am really new to go and grpc. Could someone please help me with this, Thank you!


Solution

  • type MutableObject struct {
        InMetadata metadata.MD // Example mutable field
    }
    
    // TagRPC implements per-RPC context management.
    func (c *statsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
    
        ctx = context.WithValue(ctx, "InHeader", &MutableObject{})
    
        return ctx
    }
    
    // HandleRPC implements per-RPC tracing and stats instrumentation.
    func (c *statsHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
        switch rs := rs.(type) {
    
        case *stats.InHeader:
            var headerValue metadata.MD
            
            if rs.Client {
                ign := false
                c.lock.RLock()
                ign = c.ignore
                c.lock.RUnlock()
                if !ign {
                    headerValue = rs.Header
    
                    if header, ok := ctx.Value("InHeader").(*MutableObject); ok {
                        header.InMetadata = headerValue
                    }
                }
            }
    
        case *stats.End:
    
            ign := false
            c.lock.RLock()
            ign = c.ignore
            c.lock.RUnlock()
    
            if !ign {
                duration := rs.EndTime.Sub(rs.BeginTime)
    
                var st string
                s, ok := status.FromError(rs.Error)
                if ok {
                    st = s.Code().String()
                }
    
                // Retrieve the header value from the context
    
                
                var ts time.Time
                if header, ok := ctx.Value("InHeader").(*MutableObject); ok {
                    databroker_timestamp, err := strconv.ParseInt(header.InMetadata["ts"][0], 10, 64)
                    if err == nil {
                        ts = time.Unix(int64(math.Abs(float64(databroker_timestamp)/1000000000)), databroker_timestamp%1000000000)
                    }
    
                }
    
                c.results <- &callResult{rs.Error, st, duration, rs.EndTime, ts, 10, 10}
    
                if c.hasLog {
                    c.log.Debugw("Received RPC Stats",
                        "statsID", c.id, "code", st, "error", rs.Error,
                        "duration", duration, "stats", rs)
                }
            }
        }
    
    }
    
    1. Add a mutable object to the context in TagRPC
    2. Modify the mutable object on *stats.InHeader event
    3. Finally access the object on *stats.End