How to use Orchard dependencies in a custom log4net appender?

Topics: Core, Customizing Orchard, General
Jun 30, 2015 at 5:08 PM
I am writing a custom log4net appender for my project (a Sentry appender using the Raven C# client) and would like to store the sentry DSN in a site setting (manageable in the admin), and access it during the appender initialization to create the sentry client.

So basically I need a at least a valid reference to IOrchardServices (or WorkContext) from my custom SentryAppender.

The problem is that appenders must derive log4net.Appenders.AppenderSkeleton and support only parameterless constructors, so no dependency injection from the constructor... How would you solve this?
Jul 3, 2015 at 12:16 AM
Edited Jul 3, 2015 at 9:43 AM
I don't know if this is the best way to go, but implementing the IShim interface and registering it to the Orchard's host container in the appender's constructor could do the trick:
public class CustomAppender : AppenderSkeleton, IShim
{
    private IOrchardHostContainer _hostContainer = null;
    private readonly IOrchardServices _orchardServices;

    public CustomAppender()
    {
        OrchardHostContainerRegistry.RegisterShim(this);
        this._orchardServices = this._hostContainer.Resolve<IOrchardServices>();
    }

    protected override void Append(LoggingEvent logEvent)
    {
        // Do stuff
    }

    IOrchardHostContainer IShim.HostContainer
    {
        get { return this._hostContainer; }
        set { this._hostContainer = value; }
    }
}
Jul 3, 2015 at 9:51 AM
Edited Jul 3, 2015 at 4:47 PM
Well, no.

OrchardHostContainerRegistry.RegisterShim(this); can't be called in the CustomAppender constructor because at this moment it throws an exception (probably too early in the initialization process). I tried to initialize it lazily (in the first Append() call), but even though the host container could be initialized and was made available in the _hostContainer member of my instance, the this._orchardServices = this._hostContainer.Resolve<IOrchardServices>(); call always returns null... No idea why...
public class CustomAppender : AppenderSkeleton, IShim
{
    private IOrchardHostContainer _hostContainer = null;
    private readonly IOrchardServices _orchardServices;

    public CustomAppender()
    {
    }

    protected override void Append(LoggingEvent logEvent)
    {
        if (this._hostContainer == null)
        {
            // This sets a valid IOrchardHostContainer in this._hostContainer
            OrchardHostContainerRegistry.RegisterShim(this);

            // This returns null! Only IOrchardHost, IRunningShellTable and a few
            // others can be resolved from the container
            this._orchardServices = this._hostContainer.Resolve<IOrchardServices>();
        }

        // Do stuff
    }

    IOrchardHostContainer IShim.HostContainer
    {
        get { return this._hostContainer; }
        set { this._hostContainer = value; }
    }
}
Jul 3, 2015 at 4:50 PM
I can get something more useful if I use HttpContext.Current.Request.RequestContext.GetWorkContext() from the Append() method, but tying the code to HttpContext and absolutely needing one (not always the case I suppose!) is somehow embarrassing.