Varthorne
Varthorne

Reputation: 23

How can I integrate a logger (e.g. NLog, log4net, etc.) with the Dynamics CRM 2011 ITracingService?

Context

I spend a lot of time writing custom action (codeactivity class) and occasionally plugins for a Dynamics CRM 2011 on-prem application. All logging in Dynamics is done via the ITracingService interface, which only has has a single method called Trace. Trace outputs all messages into something (buffer, stringbuilder, whatever) that dumps the contents into a PluginTraceLog record once the action or plugin is done executing.

I don't know whether anything can be done with calls to Console and Trace/Debug, but suffice to say that these are not viable options for logging.

Lately, I've found myself logging so much information in some of my classes that I've started writing special "Print" functions that only log a message if a boolean is set to true. Needless to say, this is bad design, tedious, and a good sign that I should integrate a logger to have different levels of logging.

The Problem

One of the issues is that I have no idea how ITracingService is implemented, and so I have no idea if there's a way I could hook a logger (let's stick with NLog for now) into the TracingService.

I know that NLog provides the means to create custom Targets, but after reading a lot of its documentation, I don't see a way to inject the TracingService into the logger in order to use it as the basis for the Write() function. The documentation seems to assume that the only arguments you'd want to pass in are simple primitives (e.g. the URL to a web service that you want to target).

  1. Does anyone know how I could build a custom Target whose Write function wraps around the ITracingService.Trace function?

  2. As an aside, assuming such an implementation is possible, would anyone know how to instantiate the logger in a thread-safe way? Most posts I've read strongly recommend declaring the logger as a static variable. The issue is I'm not 100% sure whether that would play nice within the lifecycle of an IPlugin or CodeActivity, given that Microsoft heavily recommends making plugins and actions stateless.

Upvotes: 0

Views: 194

Answers (1)

Varthorne
Varthorne

Reputation: 23

After a bunch more research I eventually came across this line of code on this page:

NLog.LogManager.Setup().SetupExtensions(ext => RegisterTarget<MyTarget>(() => new MyTarget(wantedDependency))

So essentially, there is in fact a way (as of NLog 5.2) to pass in a custom factory function to initialize a Target with an object.

Alternatively it also proposes an approach that was deprecated in 5.2 that uses the CreateInstance function:

using NLog.Config;

var defaultConstructor = ConfigurationItemFactory.Default.CreateInstance;

ConfigurationItemFactory.Default.CreateInstance.CreateInstance = type =>
{
   if (type == typeof(MyCustomTarget))
       return new MyCustomTarget(myCustomParameter);
   
   return defaultConstructor(type);
};

Ultimately, it doesn't matter because I wasn't able to merge the NLog assembly with my own successfully. About half the time my packages would get messed up, and so the MSBuild.ILMerge.Task would not output anything to the \bin\Debug folder.

After restoring all my NuGet packages, it would work again, but it would still fail to sign the merged assembly. I suspect it's because our project uses a .pfx file for signing rather than .snk, but why this is I don't know. Another one of our projects uses a .snk and is able to use ILMerge just fine.

In any case, the Plugin Registration Tool would not accept the merged assembly as it was not strongly named, and so I eventually gave up.

Upvotes: 1

Related Questions