Reputation: 9680
I wrote a custom assembly with a custom TargetWithLayout, let's call it MyTargets.NLog.dll, and MyTestTarget for the target.
When I load it and use it using
<extensions>
<add assembly="MyTargets.NLog"/>
</extensions>
And in section.
<target name="my" type="MyTest" />
It works perfectly. I can use it from my program. BTW : .Net 4.6.2, and NLog 4.5.3
But my assembly has a little inconvenient : it depends of a few other assemblies, and I would like to ship it in one single file.
So I have used ILMerge to generate one single assembly, but it does not work as expected. I don't use specific options, just "copy attributes".
If I use the "merged" assembly instead of the original one, at runtime I got :
NLog.config failed. Exception: NLog.NLogConfigurationException: Exception when parsing ...NLog.config. ---> System.ArgumentException: Target cannot be found: 'MyTarget'
à NLog.Config.Factory`2.CreateInstance(String name)
à NLog.Config.XmlLoggingConfiguration.ParseTargetsElement(NLogXmlElement targetsElement)
à NLog.Config.XmlLoggingConfiguration.ParseNLogElement(NLogXmlElement nlogElement, String filePath, Boolean autoReloadDefault)
à NLog.Config.XmlLoggingConfiguration.ParseTopLevel(NLogXmlElement content, String filePath, Boolean autoReloadDefault)
à NLog.Config.XmlLoggingConfiguration.Initialize(XmlReader reader, String fileName, Boolean ignoreErrors)
--- Fin de la trace de la pile d'exception interne ---
I know the assembly itself is loaded well, as the error is related to the target. (If the assembly itself was missing, I should have something like : Error loading extensions: MyTargets.NLog ---> Could not load file or assembly 'MyTargets.NLog' or one of its dependencies. The system cannot find the file specified.)
I have checked with a decompiler (dotPeek) : the target exists in both assemblies, the "original" one and the merged one , with exactly the same definition :
namespace MyTargets.NLog.Targets
{
[Target("MyTest")]
public class MyTestTarget : TargetWithLayout
{
...
}
}
With the decompiler I also see all other assemblies embedded inside the merged assemblies : all my dependencies (as expected), but also NLog assembly itself (as it's referenced as a dependency of the project).
Do you have any idea of what can prevent NLog to load the Target from the merged dll, even if the class is existing ? Can it be related to the fact ILMerged have also reintegrate inside it the whole NLog package (I don't find a way to prevent it in ILMerge).
Thank you
You mention the existence of parameters "type" and "assemblyFile". I didn't find it in documentation ( https://github.com/nlog/nlog/wiki/Register-your-custom-component ). But you are right, they exist : https://github.com/NLog/NLog/blob/master/src/NLog/Config/XmlLoggingConfiguration.cs Unfortunately, "type" argument is used against current (NLog assembly), not the custom assembly specified in "assembly" attribute. See line 1039. Even if my assembly was not merged, it could not help in my case.
So I have attempted to modify and rebuild NLog, by adding a method which takes both "type" and "assembly" in argument, and does this call inspired by existing cases in the logger configuration loading :
Assembly asm = AssemblyHelpers.LoadFromName(assemblyName);
var loadedType = asm.GetType(type, true, false);
ConfigurationItemFactory.RegisterType(loadedType, prefix);
The method does not throw any error, the type is succesfully loaded, but my target is still not available.
I have dig a bit deeper, and found the IFactory for TargetAttribute DOES NOT allow to register my class because it does not find required Target attribute. Apparently,
loadedType.GetCustomAttributes<TargetAttribute>(false)
Does not found it (despite there is a corresponding attribute on the class). And
loadedType.GetCustomAttributes<Attribute>(true)
Finds it, with a TargetAttribute.
So my guess is the duplicate definition of TargetAttribute in "runtime" nlog assembly and "merged" assembly prevents reflection to work correctly while searching for specific attributes, because they are defined in the two places.
Maybe one of the solutions here : Is it possible to only merge a subset of dependencies using ILMerge? could help (by excluding NLog from output assembly) but I don't have much time to test.
So I have gone for a completely different solution : as assembly merging is not an option, I removed all dependencies from my NLog custom library, and directly imported the required external files as "links" in Visual Studio (in Add existing file window, using the little arrow on left of Open button for those who never did it). So I don't need anymore the related assemblies. I will just have to take care to don't delete/modify them too deeply in the original DLLs (but if that happens, I count on my continuous integration server to warn me I broke something).
Upvotes: 1
Views: 1978
Reputation: 36740
<add assembly="MyTargets.NLog"/>
Will load the assembly "MyTargets.NLog", searches for targets with the TargetAttribute
and register them.
That won't work if the assembly is merged with others, so I recommend to use another way to register the target to NLog.
Recommend to register it manually. Do this a soon as possible, e.g. main()
, or app_start
.
Target.Register("MyTarget", typeof(MyNamespace.MyTarget));
See also: https://github.com/NLog/NLog/wiki/Register-your-custom-component
Upvotes: 1