I've run into a problem that I'm struggling to find a clean solution for, and Googling has not made me any wiser.
The situation
(1) We have our own assembly for setting up and adding a Serilog logger to any of our projects (consistent logging output, theme, etc) and this assembly has no references to any of the consuming projects (which are in different repos). Let's call this the CompanySerilog
assembly.
(2) One of the consuming projects is an externally accessible API, for which the 'contract' objects are defined in an ExternalContracts assembly. i.e. the request and response objects, and any enums used as part of those objects. This ExternalContracts assembly can be given to developers integrating against the API.
(3) We want to log all requests, and use an IActionFilter
to log out each request object using the Serilog structured logging approach. e.g. looping over each parameter in the context and ultimately doing a _logger.LogDebug("With {name} of {@requestObject}", name, value);
The problem
Some request objects have sensitive data which we want to mask but:
- We could define the method of destructure when creating the logger in
CompanySerilog
using the standard.Destructure
extension, but don't know, or want to know, the specifics of the request objects because those might be fromApi1
,Api2
etc and this would mean adding a reference to every consuming project. - We could add attributes to our request objects (
Destructurama.Attributed
) but that would mean ourExternalContracts
assembly would now need a reference to that NuGet package, which in turn requires references to all the necessary Serilog packages. Strictly speaking logging concerns should not be needed in the ExternalContracts assembly: that is our problem not the consumer of our API
As I say, I've been struggling to come up with ways to solve this and can't find much in the way of information on using, for example, IDestructuringPolicy and whether it is even appropriate, or whether transformations should come into play. So far I can only think of the following options but I'm hoping someone else has run into this problem and has a wickedly clever and clean way of supporting this use-case.
Solutions?
Stop doing structured logging and just define a
ToString()
for each request object that masks out values we don't want to log. This is simple, doesn't require nasty project cross-references or adding logging concerns into an external contract. But it does mean no structured logging is possible.Add all the needed logging references into the external contract. This would allow us to continue using in-built destruction, but means consumers of our API would have an ExternalContracts assembly that included logging assemblies
Set up
.Destructure
values when configuring logging inCompanySerilog
by referencing every project that will consume this assembly. Aint gonna happen!Something else? Please!