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.