Is it possible to destructure serilog event data based on an open generic? For example, I am trying to do this:
.Destructure.ByTransforming<CosmosResponse<T>>(
transform =>
new
{
transform.StatusCode,
transform.RequestCharge,
transform.ActivityId,
})
This is so I can take any Cosmos response object and pull out only specific bits of information that should be logged. However, it doesn't seem to work - my callback is never invoked.
I also tried calling ByTransformingWhere
and always returning true
. I was surprised to see it was called for every logging call, not just those dealing with CosmosResponse<T>
. And although it did call my predicate for the Cosmos response object, it still didn't call the callback to convert the object.
This is the best I've been able to achieve so far:
.Destructure.ByTransformingWhere<dynamic>(
t => t.IsGenericType && t.GenericTypeArguments.Length == 1 && t.IsSubclassOf(typeof(CosmosResponse<>).MakeGenericType(t.GenericTypeArguments[0])),
r =>
new
{
r.StatusCode,
r.RequestCharge,
r.ActivityId
})
It's a little worrying because the predicate is called for every log invocation, but I can't find any other way around this.
A far better approach, in this case, was to avoid destructuring altogether and instead add a custom handler to the Cosmos client. This facilitates interception of all requests, and the various pieces of information I care about (status code, request charge etc) are all present in the response headers.
Of course, this is specific to Cosmos DB's client library and says nothing about the original problem. If you actually do need to destructure based on an open generic type, the above is still the best known option afaik.
I'm expecting that something like this would do the job :
.Destructure.ByTransformingWhere<dynamic>(
t => t.GetGenericTypeDefinition() == typeof(CosmosResponse<>),
o => new { o.Whatever })
As the docs say, though :
When destructuring objects, transform instances of the specified type with the provided function, if the predicate returns true. Be careful to avoid any intensive work in the predicate, as it can slow down the pipeline significantly.
So it's probably a good idea to see how you can optimize or avoid those Reflection calls.
UPDATE
To have proper intellisense / type-checking, you should be able to do :
.Destructure.ByTransformingWhere<CosmosResponse<object>>(
t => t.IsGenericType && t.GenericTypeArguments.Length == 1 && t.IsSubclassOf(typeof(CosmosResponse<>).MakeGenericType(t.GenericTypeArguments[0])),
r =>
new
{
r.StatusCode,
r.RequestCharge,
r.ActivityId
})
(i.e. provide CosmosResponse<object>
as a type parameter instead of dynamic
, so that the properties that exist on CosmosResponse
light up)