You could create an aspect by making your class derive from ContextBoundObject and your attribute from ContextAttribute. By reading this article I made the following sample that meet your requirements:
First, our Foo class derived from ContextBoundObject, decorated with our custom class attribute:
[Inpectable]
internal class Foo : ContextBoundObject
{
[InspectableProperty]
public int DoSomething(int a)
{
Console.WriteLine("I am doing something");
a += 1;
return a;
}
public void IamNotLogged()
{
Console.WriteLine("Lol");
}
[InspectableProperty]
public string IamLoggedToo()
{
var msg = "Lol too";
Console.WriteLine(msg);
return msg;
}
}
Now the Inspectable attribute
[AttributeUsage(AttributeTargets.Class)]
public class Inpectable : ContextAttribute
{
public Inpectable() : base("Inspectable")
{
}
public override void GetPropertiesForNewContext(IConstructionCallMessage ccm)
{
ccm.ContextProperties.Add(new InspectorProperty());
}
}
The InspectablePropertyAttribute which will only be used to identify those methods that should be logged:
[AttributeUsage(AttributeTargets.Method)]
public class InspectableProperty : Attribute
{
}
The InspectorProperty, which will capture the context of the instance and intercept the messages passing them to our aspect
public class InspectorProperty : IContextProperty,
IContributeObjectSink
{
public bool IsNewContextOK(Context newCtx) => true;
public void Freeze(Context newContext)
{
}
public string Name { get; } = "LOL";
public IMessageSink GetObjectSink(MarshalByRefObject o,IMessageSink next) => new InspectorAspect(next);
}
And where the magic works, the implementation of our InspectorAspect:
internal class InspectorAspect : IMessageSink
{
internal InspectorAspect(IMessageSink next)
{
NextSink = next;
}
public IMessageSink NextSink { get; }
public IMessage SyncProcessMessage(IMessage msg)
{
if (!(msg is IMethodMessage)) return NextSink.SyncProcessMessage(msg);
var call = (IMethodMessage) msg;
var type = Type.GetType(call.TypeName);
if (type == null) return NextSink.SyncProcessMessage(msg);
var methodInfo = type.GetMethod(call.MethodName);
if (!Attribute.IsDefined(methodInfo, typeof (InspectableProperty)))
return NextSink.SyncProcessMessage(msg);
Console.WriteLine($"Entering method: {call.MethodName}. Args being:");
foreach (var arg in call.Args)
Console.WriteLine(arg);
var returnMethod = NextSink.SyncProcessMessage(msg) as IMethodReturnMessage;
Console.WriteLine($"Method {call.MethodName} returned: {returnMethod?.ReturnValue}");
Console.WriteLine();
return returnMethod;
}
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
throw new InvalidOperationException();
}
}
Inside the SyncProcessMessage theInspectorAspect receives all the messages regarding the context (Foo), so we just filter those that are instances of IMethodMessage and then only those belonging to methods decorated with InspectableProperty attribute.
This may not be a ready-to-production solution, but I think that puts you in the right path to research for more info.
Finally testing:
private static void Main(string[] args)
{
var foo = new Foo();
foo.DoSomething(1);
foo.IamNotLogged();
foo.IamLoggedToo();
Console.ReadLine();
}
The output:

EDIT>>>
These are the namespaces needed:
- using System.Runtime.Remoting.Activation;
- using System.Runtime.Remoting.Contexts;
- using System.Runtime.Remoting.Messaging;