Logging Request/Response Messages When Using Httpclient

Logging request/response messages when using HttpClient

An example of how you could do this:

Some notes:

  • LoggingHandler intercepts the request before it handles it to HttpClientHandler which finally writes to the wire.

  • PostAsJsonAsync extension internally creates an ObjectContent and when ReadAsStringAsync() is called in the LoggingHandler, it causes the formatter
    inside ObjectContent to serialize the object and that's the reason you are seeing the content in json.

Logging handler:

public class LoggingHandler : DelegatingHandler
{
public LoggingHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
{
}

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Console.WriteLine("Request:");
Console.WriteLine(request.ToString());
if (request.Content != null)
{
Console.WriteLine(await request.Content.ReadAsStringAsync());
}
Console.WriteLine();

HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

Console.WriteLine("Response:");
Console.WriteLine(response.ToString());
if (response.Content != null)
{
Console.WriteLine(await response.Content.ReadAsStringAsync());
}
Console.WriteLine();

return response;
}
}

Chain the above LoggingHandler with HttpClient:

HttpClient client = new HttpClient(new LoggingHandler(new HttpClientHandler()));
HttpResponseMessage response = client.PostAsJsonAsync(baseAddress + "/api/values", "Hello, World!").Result;

Output:

Request:
Method: POST, RequestUri: 'http://kirandesktop:9095/api/values', Version: 1.1, Content: System.Net.Http.ObjectContent`1[
[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], Headers:
{
Content-Type: application/json; charset=utf-8
}
"Hello, World!"

Response:
StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Date: Fri, 20 Sep 2013 20:21:26 GMT
Server: Microsoft-HTTPAPI/2.0
Content-Length: 15
Content-Type: application/json; charset=utf-8
}
"Hello, World!"

Reducing HttpClient log verbosity

I managed to exclude these logs using Serilog, which I was already using in the application.

When setting up the LoggerConfiguration, you can apply "filters", like so:

Log.Logger = new LoggerConfiguration()
.Filter.ByExcluding("SourceContext like '%System.Net.Http.HttpClient%' and @l = 'Information'")
.CreateLogger();

Logging requests, responses and exceptions of WebClient

For calling web service and if you use .Net 4.5, it's easier if you use HttpClient.

You can create LoggingHandler like this:

public class LoggingHandler : DelegatingHandler
{
public LoggingHandler()
: this(new HttpClientHandler())
{ }

public LoggingHandler(HttpMessageHandler innerHandler)
: base(innerHandler)
{ }

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var activityId = Guid.NewGuid();
using (new Tracer("Service Call", activityId))
{
var entry = new LogEntry { Severity = TraceEventType.Verbose, Title = "Request" };
if (Logger.ShouldLog(entry))
{
entry.Message = request.ToString();
if (request.Content != null)
{
entry.Message += Environment.NewLine;
entry.Message += await request.Content
.ReadAsStringAsync()
.ConfigureAwait(false);
}
Logger.Write(entry);
}

var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);

entry = new LogEntry { Severity = TraceEventType.Verbose, Title = "Response" };
if (Logger.ShouldLog(entry))
{
entry.Message = response.ToString();
if (response.Content != null)
entry.Message += await response.Content
.ReadAsStringAsync()
.ConfigureAwait(false);

Logger.Write(entry);
}

return response;
}
}
}

And then create HttpClient:

//hook all http request and response
var hc = new HttpClient(new LoggingHandler());

Documentation for HttpClient class:
https://msdn.microsoft.com/en-us/library/system.net.http.httpclient%28v=vs.118%29.aspx

Comparing HttpClient and WebClient Need help deciding between HttpClient and WebClient

ASP.NET Core 3.1 HttpClient to log only warnings and errors

You can configure Logging in .NET Core through the Appsettings file. You should find a section in the appsettings.json file along the lines

{
"Logging": {
"Debug": {
"LogLevel": {
"Default": "Information"
}
}
}
}

You can add an additional Log Level filter to specify the minimum log level required to log.

{
"Logging": {
"Debug": {
"LogLevel": {
"Default": "Information",
"System.Net.Http.HttpClient": "Debug"
}
}
}
}

Documentation for Logging with filters in .NET Core can be found here.

Documemtation for Logging with filters in the IHttpClientFactory library can be found here. This documentation also includes examples of log filtering with a named HttpClient.

Get the raw request that is sent with HttpClient and HttpRequestMessage

If you cannot use any proxy solution (like Fiddler) then I can see 2 options. One is described in comments in your question to use DelegatingHandler. You can read more about this in documentation. What is interesting is that HttpClient supports logging out of the box which is described in this section https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-6.0#logging of the article which describes DelegatingHandlers

If you are worried that something will manipulate the outgoing request then you can implement option 2. This is to create temporary asp.net core application with .UseHttpLogging() middleware plugged in into pipeline as described here https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-logging/?view=aspnetcore-6.0 That way you will know exactly how your request looks like from application which is being requested point of view. Now if you will point your azure function to you temporary app - you should see what gets send

Hope it helps



Related Topics



Leave a reply



Submit