Repository: grpc/grpc-go
File: stats/opencensus/stats.go (Go)

Code before:
	recvMsgs int64
	// number of bytes received (within each message) received on side (client
	// || server)
	recvBytes int64
	// number of compressed bytes received (within each message) received on
	// side (client || server)
	recvCompressedBytes int64

	startTime time.Time
	method    string
}

// statsTagRPC creates a recording object to derive measurements from in the
// context, scoping the recordings to per RPC Attempt client side (scope of the
// context). It also populates the gRPC Metadata within the context with any
// opencensus specific tags set by the application in the context, binary
// encoded to send across the wire.
func (csh *clientStatsHandler) statsTagRPC(ctx context.Context, info *stats.RPCTagInfo) (context.Context, *metricsInfo) {
	mi := &metricsInfo{
		startTime: time.Now(),
		method:    info.FullMethodName,
	}

	// Populate gRPC Metadata with OpenCensus tag map if set by application.
	if tm := tag.FromContext(ctx); tm != nil {
		md, ok := metadata.FromOutgoingContext(ctx)
		if !ok {
			md = metadata.MD{}
		}
		md["grpc-tags-bin"] = []string{string(tag.Encode(tm))}
		ctx = metadata.NewOutgoingContext(ctx, md)
	}
	return ctx, mi
}

// statsTagRPC creates a recording object to derive measurements from in the
// context, scoping the recordings to per RPC server side (scope of the
// context). It also deserializes the opencensus tags set in the context's gRPC
// Metadata, and adds a server method tag to the opencensus tags. If multiple
// tags exist, it adds the last one.
func (ssh *serverStatsHandler) statsTagRPC(ctx context.Context, info *stats.RPCTagInfo) (context.Context, *metricsInfo) {
	mi := &metricsInfo{
		startTime: time.Now(),
		method:    info.FullMethodName,
	}

	if md, ok := metadata.FromIncomingContext(ctx); ok {
		if tgValues := md.Get("grpc-tags-bin"); len(tgValues) > 0 {
			tagsBin := []byte(tgValues[len(tgValues)-1])
			if tags, err := tag.Decode(tagsBin); err == nil {
				ctx = tag.NewContext(ctx, tags)

Review comment (performance):
`metadata.AppendToOutgoingContext` is more efficient.  And as long as we read the last incoming instance of the header, I believe the behavior is similar enough.  And if anyone is setting the header multiple times to different values, they were just lucky if it ever worked.

Code after:
	recvMsgs int64
	// number of bytes received (within each message) received on side (client
	// || server)
	recvBytes int64
	// number of compressed bytes received (within each message) received on
	// side (client || server)
	recvCompressedBytes int64

	startTime time.Time
	method    string
}

// statsTagRPC creates a recording object to derive measurements from in the
// context, scoping the recordings to per RPC Attempt client side (scope of the
// context). It also populates the gRPC Metadata within the context with any
// opencensus specific tags set by the application in the context, binary
// encoded to send across the wire.
func (csh *clientStatsHandler) statsTagRPC(ctx context.Context, info *stats.RPCTagInfo) (context.Context, *metricsInfo) {
	mi := &metricsInfo{
		startTime: time.Now(),
		method:    info.FullMethodName,
	}

	// Populate gRPC Metadata with OpenCensus tag map if set by application.
	if tm := tag.FromContext(ctx); tm != nil {
		ctx = metadata.AppendToOutgoingContext(ctx, "grpc-tags-bin", string(tag.Encode(tm)))
	}
	return ctx, mi
}

// statsTagRPC creates a recording object to derive measurements from in the
// context, scoping the recordings to per RPC server side (scope of the
// context). It also deserializes the opencensus tags set in the context's gRPC
// Metadata, and adds a server method tag to the opencensus tags. If multiple
// tags exist, it adds the last one.
func (ssh *serverStatsHandler) statsTagRPC(ctx context.Context, info *stats.RPCTagInfo) (context.Context, *metricsInfo) {
	mi := &metricsInfo{
		startTime: time.Now(),
		method:    info.FullMethodName,
	}

	if tgValues := metadata.ValueFromIncomingContext(ctx, "grpc-tags-bin"); len(tgValues) > 0 {
		tagsBin := []byte(tgValues[len(tgValues)-1])
		if tags, err := tag.Decode(tagsBin); err == nil {
			ctx = tag.NewContext(ctx, tags)
		}
	}

	// We can ignore the error here because in the error case, the context
	// passed in is returned. If the call errors, the server side application
	// layer won't get this key server method information in the tag map, but