grpc/rpclog/grpc.go
func StreamServerInterceptor(log ttnlog.Interface) grpc.StreamServerInterceptor {
	return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) {
		log := getLog(log).WithField("method", info.FullMethod)
		log = log.WithFields(FieldsFromIncomingContext(ss.Context()))
		start := time.Now()
		log.Debug("rpc-server: stream starting")
		err = handler(srv, ss)
		log = log.WithField("duration", time.Since(start))
		if err != nil {
			if err == context.Canceled || grpc.Code(err) == codes.Canceled {
				log.Debug("rpc-server: stream canceled")
				return
			}
			log.WithError(err).Debug("rpc-server: stream failed")
			return
		}
		log.Debug("rpc-server: stream done")
		return
	}
}
func StreamServerInterceptor(log ttnlog.Interface) grpc.StreamServerInterceptor {
	return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) {
		log := getLog(log).WithField("method", info.FullMethod)
		log = log.WithFields(FieldsFromIncomingContext(ss.Context()))
		start := time.Now()
		log.Debug("rpc-server: stream starting")
		err = handler(srv, ss)
		log = log.WithField("duration", time.Since(start))
		if err != nil {
			if err == context.Canceled || grpc.Code(err) == codes.Canceled {
				log.Debug("rpc-server: stream canceled")
				return
			}
			log.WithError(err).Debug("rpc-server: stream failed")
			return
		}
		log.Debug("rpc-server: stream done")
		return
	}
}
// StreamServerInterceptor logs streaming RPCs on the server side