destructurama/attributed

"CreatePropertyValue" returns null when attribute inherits from ITypeDestructuringAttribute

aholeton opened this issue · 3 comments

I was attempting to create a custom attribute that needs to inspect multiple properties on an object to determine whether or not a specific property should be masked. When using propertyValueFactory.CreatePropertyValue(value, true); it looks like it is returning null as opposed to an object.

If this is not the right way to go about this, then any help would be appreciated on what I should be doing instead.

Describe the bug
When creating a custom attribute inheriting from ITypeDestructuringAttribute, the method propertyValueFactory.CreatePropertyValue(value, true), always returns null.

Expected behavior
Something other than null.

Screenshots and any additional context

[AttributeUsage(AttributeTargets.Class)]
public class LogSensitiveDataAttribute : Attribute, ITypeDestructuringAttribute
{
    public LogEventPropertyValue CreateLogEventPropertyValue(object? value, ILogEventPropertyValueFactory propertyValueFactory)
    {
        // this always returns null
        var result = propertyValueFactory.CreatePropertyValue(value, true);

        // other code
    }
}

You get into recursive trap with aforementioned code. Look:

изображение

Eventually you end in that method in Serilog core project:
изображение

You may enable SelfLog to verify that.

Code to reproduce:

using Destructurama.Attributed.Tests.Support;
using NUnit.Framework;
using Serilog.Core;
using Serilog.Events;
using Shouldly;

namespace Destructurama.Attributed.Tests;

// https://github.com/destructurama/attributed/issues/111
[TestFixture]
public class Issue111
{
    [Test]
   public void Issue_111()
    {
        var customized = new MyClass();
        var evt = DelegatingSink.Execute(customized);
        evt.Properties["Customized"].ShouldBe(ScalarValue.Null);
    }

    [LogSensitiveData]
    public class MyClass
    {
        public string Foo { get; set; } = "Foo123";

        public string Bar { get; set; } = "Bar123";
    }

    [AttributeUsage(AttributeTargets.Class)]
    public class LogSensitiveDataAttribute : Attribute, ITypeDestructuringAttribute
    {
        public LogEventPropertyValue CreateLogEventPropertyValue(object? value, ILogEventPropertyValueFactory propertyValueFactory)
        {
            // this always returns null
            var result = propertyValueFactory.CreatePropertyValue(value, true);

            return result;
        }
    }
}

Thanks for taking the time to look at this. I need to inspect multiple properties to determine if a specific property should be masked. Given your example, is it possible to inspect property Foo to determine whether property Bar should be masked?

You can do this via reflection or by any other mean but not by calling CreatePropertyValue passing the value itself which in turn calls into LogSensitiveDataAttribute again.