/// <summary>
        /// Gets the request ID for logging.
        /// </summary>
        /// <param name="responseHeaders">The response headers.</param>
        /// <param name="response">The response.</param>
        /// <param name="exception">The exception, if available.</param>
        /// <returns>The request ID.</returns>
        public string GetRequestId(Metadata responseHeaders, object response,
                                   RpcException exception)
        {
            string requestId = new AdsResponseMetadata(responseHeaders).RequestId;

            // For streaming calls, the trailing response headers are returned only after
            // the entire stream is read, whereas we write summary logs each time we retrieve
            // a response object from the stream. As a result, the requestId in summary logs
            // appear blank in all except the last entry. As a fix, we read the request Id
            // from the stream response object as a fallback.
            if (string.IsNullOrEmpty(requestId))
            {
                IResponseMetadata responseMetadata = response as IResponseMetadata;
                if (responseMetadata != null)
                {
                    requestId = responseMetadata.RequestId;
                }
            }
            if (string.IsNullOrEmpty(requestId))
            {
                AdsBaseException adsException = exception as AdsBaseException;
                if (adsException != null)
                {
                    requestId = adsException.RequestId;
                }
            }
            return(requestId);
        }
        /// <summary>
        /// Parses the task exception.
        /// </summary>
        /// <param name="e">The <see cref="AggregateException"/> to parse.</param>
        /// <returns>The parsed <see cref="AdsBaseException"/> if parsing is successful;
        /// The underlying <see cref="RpcException" /> if the exception cannot be parsed as a
        /// AdsBaseException.</returns>
        /// <remarks><code>AggregateException</code> is very close to a catch-all exception for
        /// Tasks. While all the situations that we know of involves this exception being thrown
        /// by an underlying <code>RpcException</code>, theoretically this method may return a
        /// null object, if the <code>AggregateException</code> is not caused by an
        /// <code>RpcException</code>. That would typically indicate an underlying issue with
        /// the code.</remarks>
        internal static RpcException ParseTaskException <TResponse>(AggregateException e)
        {
            RpcException     rpcException = ExtractRpcException(e);
            AdsBaseException adsException = ParseRpcException <TResponse>(rpcException);

            return((adsException == null) ? rpcException : adsException);
        }