Overview
When using IHttpClientFactory, you can output communication details such as HTTP request starts, ends, headers, and status codes simply by changing the log settings. This is a very effective debugging method for tracking what headers were sent or which URLs were accessed during API communication troubleshooting.
Specifications (Input/Output)
- Input: Log level settings in
appsettings.json. - Output: HTTP communication trace information displayed in the console or other log destinations.
- Prerequisites: Uses packages such as
Microsoft.Extensions.HttpandMicrosoft.Extensions.Logging.Console.
Basic Usage
In the Logging section of appsettings.json, set the level for the System.Net.Http.HttpClient namespace to Trace or Information.
{
"Logging": {
"LogLevel": {
"Default": "Warning",
// Setting to output detailed HttpClient logs
"System.Net.Http.HttpClient": "Trace"
}
}
}
Full Code Example
Below is a complete implementation example of a console application with the configuration file and logging enabled.
1. Required Packages
dotnet add package Microsoft.Extensions.Http
dotnet add package Microsoft.Extensions.Logging.Console
dotnet add package Microsoft.Extensions.Configuration.Json
2. appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"System": "Warning",
// Setting this to Trace will show detailed header information.
// Information will only show request start and end.
"System.Net.Http.HttpClient": "Trace"
}
}
}
3. Program.cs & Worker
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
class Program
{
static async Task Main()
{
// 1. Load configuration file
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.Build();
// 2. Setup DI container and logging
var services = new ServiceCollection();
services.AddLogging(builder =>
{
builder.AddConfiguration(configuration.GetSection("Logging"));
builder.AddConsole(); // Output to console
});
// Register HttpClient
services.AddHttpClient();
// Register the execution class
services.AddTransient<MyWorker>();
var provider = services.BuildServiceProvider();
// 3. Execute
var worker = provider.GetRequiredService<MyWorker>();
await worker.RunAsync();
}
}
public class MyWorker
{
private readonly IHttpClientFactory _clientFactory;
private readonly ILogger<MyWorker> _logger;
public MyWorker(IHttpClientFactory clientFactory, ILogger<MyWorker> logger)
{
_clientFactory = clientFactory;
_logger = logger;
}
public async Task RunAsync()
{
_logger.LogInformation("Starting process.");
// Create client
var client = _clientFactory.CreateClient();
// Test URL
var url = "http://httpbin.org/get";
try
{
// Communication logs will automatically flow to the console here
var result = await client.GetStringAsync(url);
Console.WriteLine("\n--- Response Result ---");
Console.WriteLine(result.Substring(0, Math.Min(result.Length, 100)) + "...");
}
catch (Exception ex)
{
_logger.LogError(ex, "A communication error occurred");
}
}
}
Log Output Example during Execution
trce: System.Net.Http.HttpClient.Default.LogicalHandler[100]
Start processing HTTP request GET http://httpbin.org/get
trce: System.Net.Http.HttpClient.Default.ClientHandler[100]
Sending HTTP request GET http://httpbin.org/get
trce: System.Net.Http.HttpClient.Default.ClientHandler[101]
Received HTTP response headers after 150.5ms - 200
trce: System.Net.Http.HttpClient.Default.LogicalHandler[101]
End processing HTTP request after 160.2ms - 200
Customization Points
- Control per Named Client: The category name becomes
System.Net.Http.HttpClient.[ClientName].LogicalHandler, allowing you to enable logs only for specific API clients. - Adjusting Output Detail: Setting the level to
Informationoutputs only “Start” and “End (Duration),” omitting header information. This is suitable for production environments where you want to reduce log volume.
Important Notes
- Bodies are not included: Standard logging does not output request bodies (such as JSON content) for security and performance reasons. To see the body, you must implement a custom
DelegatingHandler. - Sensitive Information: At the
Tracelevel, headers likeAuthorizationmay be recorded in the logs. Manage log files carefully and use theRedactLoggedHeadersoption to mask specific headers if necessary.
Advanced Usage
Logging Request/Response Bodies
If the standard features are insufficient, you can insert a custom handler.
public class LoggingHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// Output request log (read and display Content here, etc.)
Console.WriteLine($"Request: {request.RequestUri}");
if (request.Content != null)
{
string body = await request.Content.ReadAsStringAsync();
Console.WriteLine($"Body: {body}");
}
var response = await base.SendAsync(request, cancellationToken);
// Output response log
return response;
}
}
// Add handler during registration
services.AddTransient<LoggingHandler>();
services.AddHttpClient("MyClient")
.AddHttpMessageHandler<LoggingHandler>();
Conclusion
The easiest way to check HttpClient communication is to set the log level for System.Net.Http.HttpClient to Trace in appsettings.json. This automatically records the request URL, method, header information, status code, and duration, making it easy to investigate the causes of communication errors. However, since it does not output the request body, you should consider implementing a custom handler if more detailed debugging is required.
