The accepted answer is kind of weird : It means that you have to log each and every piece of Linq code you made.
(Using AspNetCore DI).
Knowing that the Microsoft.Azure.Cosmos.CosmosClient contains an HttpClient, the first thing we can do is search if we can pass our own HttpClient.
And we can. For example, via CosmosClientBuilder and using the injected IHttpClientFactory :
.WithHttpClientFactory(() => httpClientFactory.CreateClient("Client"))
If your log filters are well configured, you'll see the Request / Response in the console.
Now, we could either add a DelegatingHandler to our HttpClient this way in ConfigureServices or builder.Services in .NET 6 :
services.AddTransient<NoSqlLoggingDelegatingHandler>();
services.AddHttpClient("Client")
.AddHttpMessageHandler<NoSqlLoggingDelegatingHandler>();
or find out if CosmosClient has the option to add our own, and of course it does (Example via CosmosClientBuilder).
.AddCustomHandlers(
new NoSqlLoggingDelegatingHandler(loggerFactory.CreateLogger<NoSqlLoggingDelegatingHandler>()))
With this, we can intercept the Request and Response sent :
public override async Task<ResponseMessage> SendAsync(RequestMessage request, CancellationToken cancellationToken)
{
_logger.LogInformation("Requesting {uri}.\n{query}",
request.RequestUri, await GetQueryAsync(request.Content));
ResponseMessage response = await base.SendAsync(request, cancellationToken);
_logger.LogInformation("Requested {uri} - {status}",
response.RequestMessage.RequestUri, response.StatusCode);
return response;
}
And GetQueryAsync reads the Request's body Stream using System.Text.Json :
private static async ValueTask<string?> GetQueryAsync(Stream content)
{
string? stringContents;
// Create a StreamReader with leaveOpen = true so it doesn't close the Stream when disposed
using (StreamReader sr = new StreamReader(content, Encoding.UTF8, true, 1024, true))
{
stringContents = await sr.ReadToEndAsync();
}
content.Position = 0;
if (string.IsNullOrEmpty(stringContents))
{
return null;
}
try
{
using JsonDocument parsedJObject = JsonDocument.Parse(stringContents);
return parsedJObject.RootElement.GetProperty("query").GetString();
}
catch (KeyNotFoundException)
{
return stringContents;
}
// Not a JSON.
catch (JsonException)
{
return stringContents;
}
}
And there you have it. I've tried to shortened the code, for example, I didn't add a condition to check if the logging level is enabled or not, to avoid doing useless work.