We currently have an internal library that we're using to make a lot of our HTTP calls that uses RestSharp rather that the HttpClient to make all of our requests to downstream services. Is it possible to enable x-ray tracing in AWS without re-writing that library to instead use HttpClient? The goal is to get the same functionality that would be outlined here (when using HttpClient): https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-dotnet-httpclients.html
-
What did you end up doing to resolve your problem? – Judy007 Dec 03 '20 at 23:41
2 Answers
The RestSharp library is a separate HTTP client which the X-Ray SDK for .NET does not support at this time. I'm not familiar with RestSharp, but I believe it uses HttpClient under the hood as that is the native library for HTTP calls in .NET. So if you could somehow replace the underlying HttpClient used by the RestSharp library with an instrumented version of HttpClient, that would enable X-Ray tracing, but I'm not sure if such a replacement is possible.
The X-Ray SDK for .NET is open sourced, and we're happy to take pull requests if you'd like to write a module for X-Ray RestSharp support https://github.com/aws/aws-xray-sdk-dotnet

- 267
- 1
- 10
-
That's correct, RestSharp uses `HttpWebRequest`, which, in turn, uses `HttpClient`. It is actually quite easy to instrument `HttpClient` in an unobtrusive manner by using diagnostic listeners like it is done in OpenTracing https://github.com/opentracing-contrib/csharp-netcore/blob/master/src/OpenTracing.Contrib.NetCore/CoreFx/HttpHandlerDiagnostics.cs – Alexey Zimarev Sep 09 '20 at 06:26
-
Can you elaborate a little on what it would take technically speaking in order to add support for RestSharp? You mentioned writing a module? Do you have a sample module that someone can reference when considering building that? Can you give some pointers? – Judy007 Oct 05 '20 at 03:34
-
What are the necessary and sufficient steps to update the xray sdk dotnet so that it supports RestSharp? Can you please provide some pointers? I believe you mentioned something about a "module". What is a module in this respect? Is this a xray sdk specific concept I can learn more about somewhere? – Judy007 Dec 03 '20 at 23:41
-
@AlexeyZimarev, I dont believe customers of the RestSharp library are newing up an instance of HttpClient directly in their caller code. I believe your reference is for anyone newing up an instance of HttpClient. When someone takes advantage of RestSharp, they are likely going to be newing up only instances of RestSharp. – Judy007 Dec 05 '20 at 16:35
-
@jbooker it is true, but any instrumentation with `System.Diagnostics` that uses activities can be easily extended from the outside. As soon as you create an activity before making a request with RestSharp, it will be propagated by `HttpClient`. Also, it has little to do with the `HttpClient` instance as the tracing happens per-request basis. – Alexey Zimarev Dec 07 '20 at 08:43
-
Ok, so if it’s that simple, can you please add code snippet to showcase what your trying to describe is a solution please? – Judy007 Dec 07 '20 at 14:21
-
The X-Ray auto-instrumentation agent for .NET uses the approach described by Alexey to instrument, among other things, the base HttpClient library. It should satisfy this requirement: https://github.com/aws/aws-xray-dotnet-agent – William Armiros Dec 07 '20 at 20:49
This is how I ended up implementing it - near impossible to intercept anything in RestSharp, so I took the reverse approach of breaking down and modifying the "GetResponseTraced" extension:
public static class RestClientXrayExtensions
{
public static IRestResponse ExecuteTraced(this IRestClient client, IRestRequest request)
{
return client.ExecuteTraced(request, () => client.Execute(request));
}
public static IRestResponse<T> ExecuteTraced<T>(this IRestClient client, IRestRequest request)
where T : new()
{
return client.ExecuteTraced(request, () => client.Execute<T>(request));
}
public static IRestResponse ExecuteAsGetTraced(this IRestClient client, IRestRequest request, string httpMethod)
{
return client.ExecuteTraced(request, () => client.ExecuteAsGet(request, httpMethod));
}
private static TResponse ExecuteTraced<TRequest,TResponse>(this IRestClient client, TRequest request, Func<TResponse> execute)
where TRequest : IRestRequest
where TResponse : IRestResponse
{
ProcessRequest(client.BuildUri(request), request.Method.ToString(), header => request.AddHeader("X-Amzn-Trace-Id", header));
try
{
var response = execute();
ProcessResponse(response.StatusCode, response.ContentLength);
return response;
}
catch (Exception ex)
{
AWSXRayRecorder.Instance.AddException(ex);
throw;
}
finally
{
AWSXRayRecorder.Instance.EndSubsegment(new DateTime?());
}
}
private static void ProcessRequest(Uri uri, string method, Action<string> addHeaderAction)
{
if (!AWSXRayRecorder.Instance.IsTracingDisabled())
{
AWSXRayRecorder instance = AWSXRayRecorder.Instance;
instance.BeginSubsegment(uri.Host, new DateTime?());
instance.SetNamespace("remote");
Dictionary<string, object> dictionary = new Dictionary<string, object>()
{
["url"] = (object)uri.AbsoluteUri,
[nameof(method)] = (object)method
};
instance.AddHttpInformation("request", (object)dictionary);
try
{
TraceHeader header;
if (!TraceHeader.TryParse(instance.GetEntity(), out header))
return;
addHeaderAction(header.ToString());
}
catch (EntityNotAvailableException ex)
{
instance.TraceContext.HandleEntityMissing((IAWSXRayRecorder)instance, (Exception)ex, "Failed to get entity since it is not available in trace context while processing http request.");
}
}
}
private static void ProcessResponse(HttpStatusCode httpStatusCode, long? contentLength)
{
if (!AWSXRayRecorder.Instance.IsTracingDisabled())
{
int num = (int)httpStatusCode;
Dictionary<string, object> dictionary = new Dictionary<string, object>()
{
["status"] = (object)num
};
if (num >= 400 && num <= 499)
{
AWSXRayRecorder.Instance.MarkError();
if (num == 429)
AWSXRayRecorder.Instance.MarkThrottle();
}
else if (num >= 500 && num <= 599)
AWSXRayRecorder.Instance.MarkFault();
dictionary["content_length"] = (object)contentLength;
AWSXRayRecorder.Instance.AddHttpInformation("response", (object)dictionary);
}
}
}

- 1
- 1