NLog not finding any extension assembly without project reference - c#

I am trying to use a custom NLog target with a third party application (UiPath) that uses NLog for logging purposes. As I cannot add my target assembly as a dependency here, I copied my assembly dll into the same folder where UiPath stores NLog.dll and NLog.config.
I have updated NLog.config as per the docs at https://github.com/NLog/NLog/wiki/Register-your-custom-component and expected the custom target to be registered from the assembly dll.
Now the config looks like this (MyTarget is a minimal target that replaces the actual target, so that I could rule out dependency or runtime issues from the target itself):
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" internalLogFile="D:\nlog-internal.txt" internalLogLevel="Trace">
<variable name="WorkflowLoggingDirectory" value="${specialfolder:folder=LocalApplicationData}/UiPath/Logs" />
<rules>
<logger name="*" writeTo="WorkflowLogFiles" final="true" />
<logger name="*" writeTo="MyFirst" final="true" />
</rules>
<targets>
<target type="File" name="WorkflowLogFiles" fileName="${WorkflowLoggingDirectory}/${shortdate}_Execution.log" layout="${basedir} ==> ${message}," keepFileOpen="true" openFileCacheTimeout="5" concurrentWrites="true" encoding="utf-8" writeBom="true" />
<target type="MyFirst, MyTarget" name="MyFirst" Host="na" layout="${message}" />
</targets>
</nlog>
Note that this example should register the assembly from the <target> tag using its FQN. I have also tried with:
<extensions>
<assembly>MyTarget</assembly>
</extensions>
and:
<target type="MyFirst" name="MyFirst" layout="${message}" />
Which yields the same result in nlog-internal.txt:
Warn Error has been raised. Exception: NLog.NLogConfigurationException: Error loading extensions: MyTarget
---> System.IO.FileNotFoundException: Could not load file or assembly 'MyTarget, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.
File name: 'MyTarget, Culture=neutral, PublicKeyToken=null'
I am at a loss as to why it can't load the assembly like this. Probably overlooking something very basic.
Created a minimal reproduction: (Program.cs below)
using NLog;
namespace Example
{
static class Program
{
static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public static void Main()
{
Logger.Info("Hello!");
Console.ReadKey();
}
}
}
Which behaves identically when trying to configure the extension and target from NLog.config. If I add the MyTarget.dll as a dependency to this project before compilation, the target is found and behaves as expected.
edit: changed assembly to dependency in last sentence (see comment below)

I found that loading an assembly by name from the config file does not work as expected. Even when the dll is in the same folder.
Not working when you want to use a target from a dll in the same folder as NLog.dll and NLog.config:
<extensions>
<add assembly="MyTarget"/>
</extensions>
Also not working:
<targets>
<target name="mytarget" type="CustomTarget, MyAssemblyName" />
</targets>
What does work, is using the (rather undocumented) assemblyFile attribute:
<extensions>
<add assemblyFile="MyTarget.dll"/>
</extensions>

Related

NLog config inside web.config or app.config ignored in version 4.5.11

Recently, we've updated some web and console application, to use a newer version of NLog, the latest 4.5.11.
Before, we've used a 4.4 version.
With this 4.4 version, we were able to set the NLog config inside the web.config or app.config file.
According to the documentation, this is still possible. However, with the newer version this does not work.
I only get some logging, if I copy/paste the same rules, into an nlog.config file.
Of course, I could do this for all my applications, but there is a chance we miss something.
What is the cause it isn't read from the web/app.config file anymore?
I also tried to set the throwExceptions="true" attribute, but it gave me no exceptions.
Also if I set the internal log file, on the attribute inside my web.config, it's still not working. The internal log, is also empty.
Tested with internalLogLevel="Info" and internalLogLevel="Trace"
If I set it in the Application_Start, I only have
2019-02-27 10:23:33.1899 Info Shutting down logging...
2019-02-27 10:23:33.1899 Info Logger has been shut down.
This is my Web.Config (partly)
<configSections>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
</configSections>
...
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
internalLogFile="c:\temp\internal-nlog.txt" internalLogLevel="Info">
<targets>
<target name="logfile" xsi:type="File" fileName="${basedir}/Log/${shortdate}.log" layout="${date:format=dd-MM-yyyy HH\:mm\:ss.fff} [${level}] ${callsite} ${message}${onexception:${newline}EXCEPTION OCCURRED\:${exception:format=tostring}}" />
<target name="warnfile" xsi:type="File" fileName="${basedir}/Log/warn/${shortdate}.log" layout="${date:format=dd-MM-yyyy HH\:mm\:ss.fff} [${level}] ${callsite} ${message}${onexception:${newline}EXCEPTION OCCURRED\:${exception:format=tostring}}" />
</targets>
<rules>
<logger name="*" minlevel="Error" writeTo="logfile">
<filters>
<when condition="contains('${message}', 'ThreadAbortException')" action="Ignore" />
</filters>
</logger>
<logger name="*" level="Debug" writeTo="warnfile">
<filters>
<when condition="contains('${message}', 'ThreadAbortException')" action="Ignore" />
</filters>
</logger>
</rules>
</nlog>

Nlog with a class Library

I have a class library and configuration file in a separate place I want to use it
so I do the following
LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(configurationFile , true);
LogManager.ReconfigExistingLoggers();
The configuration of NLog is part of a remote app.config looks like
<configuration>
<!-- ... -->
<configSections>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog" />
</configSections>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<!--
Log in a separate thread, possibly queueing up to
5000 messages. When the queue overflows, discard any
extra messages
-->
<target name="file" xsi:type="AsyncWrapper" queueLimit="5000" overflowAction="Discard"
layout="${machinename} - ${level} - ${longdate} - ${callsite} - ${threadid} - ${message} ${exception:format=tostring}">
<target xsi:type="File" fileName="C:\logs/${level}.txt" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="file" />
</rules>
</nlog>
</configuration>
the configuration object LogManager.Configuration always has the default values , any idea how to fix this.
When creating the first NLog Logger-object, then NLog will attempt to load the NLog.config (or app.config) automatically. Searching multiple locations:
https://github.com/nlog/NLog/wiki/Configuration-file#configuration-file-locations
You should try and activate the NLog internal logger and see why it doesn't find the "remote app.config" without doing manually loading (Can be enabled by code):
https://github.com/NLog/NLog/wiki/Internal-Logging
It is NOT recommended that the class library itself tries to modify the global NLog-configuration, as it might surprise the main-application. If wanting to have an isolated NLog configuration for your class-library, then you should consider creating an isolated NLog LogFactory:
https://github.com/NLog/NLog/wiki/Configure-component-logging
If you want to load an NLog.config from a non-standard path (not app.config). Then you can also do this:
https://github.com/NLog/NLog/wiki/Explicit-NLog-configuration-loading

How to programmatically configure nLog targets

I am trying to figure out how to programatically configure a NLog FallbackGroup target that has two email type sub-targets. Based on some things that happen during the start of the application, I would like to override the "to" part of the two sub-targets of the FallbackGroup target.
Currently, the app has a NLog.config file that has these targets in it. But, the values are hard-coded in it. Any change will require a redeploy of the app... That's not what we want... What we really need is the ability to modify the "to" setting in the two targets in some logic called on startup.
Here's the NLog.config file:
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
throwExceptions="false"
internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
<targets>
<target name="logfile" xsi:type="File" fileName="BAM_logfile.txt" />
<target xsi:type="FallbackGroup"
name="email-error"
returnToFirstOnSuccess="true">
<target xsi:type="Mail"
name="mailserver1"
to="kevin.orcutt#acme.com"
from="noreply#acme.com"
subject="Exception Message from: ${processname} v:${assembly-version} on ${machinename}"
smtpServer="smtp.acme.com"
smtpPort="25"
layout="${longdate}${newline}${windows-identity} running ${processname} v:${assembly-version} on ${machinename}${newline}At: ${callsite}${newline}Message: ${message}${newline}Exception:${newline}${exception:format=toString,Data:maxInnerExceptionLevel=10}${newline}" />
<target xsi:type="Mail"
name="mailserver2"
to="kevin.orcutt#acme.com"
from="noreply#acme.com"
subject="Exception Message from: ${processname} v:${assembly-version} on ${machinename}"
smtpServer="mail.acme.com"
smtpPort="25"
layout="${longdate}${newline}${windows-identity} running ${processname} v:${assembly-version} on ${machinename}${newline}At: ${callsite}${newline}Message: ${message}${newline}Exception:${newline}${exception:format=toString,Data:maxInnerExceptionLevel=10}${newline}" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Info" maxlevel="Warn" writeTo="logfile" />
<logger name="*" level="Error" writeTo="email-error" />
</rules>
<extensions>
<add assembly="NLog.MailKit"/>
</extensions>
</nlog>
So my question is... Is there a way of overriding the "to" part of the sub-targets in the NLog.config file programatically, or is it best to configure the entire FallbackGroup target on application start? A second and less obvious question is I'm looking for some examples of either solution... I haven't quite figured out the code to do either... :-(
It works something like this:
Load the configuration from the appropriate XML file. This can be your app.config or a separate XML file. Then find the target by name, and cast it to the appropriate type. From there you can modify its properties however you see fit.
var xmlConfig = new XmlLoggingConfiguration("nlog.config");
var target = xmlConfig.FindTargetByName("mailserver1") as MailTarget;
target.To = "...";
You can do this with any target: load it by name, then cast it to the appropriate type. When you're done making changes, apply the configuration:
LogManager.Configuration = xmlConfig;
This should be done at startup, before you get any loggers. I think any loggers you get before applying these changes will not be affected.

Same logger to log in multiple destinations?

How can I have one logger, which I get with GetLogger() method, logging to file AND rich text box at the same time?
One approach is to use an IoC container. Simply define an interface and then define your logger classes based on that interface. They can then be created or injected into your other classes as needed.
your nlog configuration defines rules and targets independent from each other. Your requirement is to have 2 targets, a file logger and some other logger that displays your log entries in a text box. You would just add two rules to your nlog configuration to direct your log entries to those two targets.
this would be an example nlog.config that logs all logging entries to two different targets. You can set different minLevels for the individual logging rules. The first target is a simple file logger, the other target is a MethodCall target that calls a static method with the log parameters. See the nlog documentation for all available targets and further documentation.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
</configSections>
<nlog>
<targets>
<target name="toFile" type="File"
layout="${longdate} ${level} [${threadid}] - ${callsite}: ${message} ${onexception:${newline}Ausnahme\:${exception:format=tostring}}"
fileName="${basedir}/log.txt"
archiveFileName="${basedir}/log.{#####}.txt"
archiveAboveSize="1024000"
archiveNumbering="Sequence"
concurrentWrites="true"
keepFileOpen="false"
maxArchiveFiles="3"
/>
<target xsi:type="MethodCall"
name="toRichTextBox"
methodName="String"
className="String">
<parameter layout="Layout" name="String" type="System.Type"/><!-- repeated -->
</target>
</targets>
<rules>
<logger name="*" minlevel="Warn" writeTo="toFile" />
<logger name="*" minlevel="Warn" writeTo="toRichTextBox" />
</rules>
</nlog>
</configuration>

Ignoring NLog Configuration referenced in other assembly

I have a unique problem with the NLog. I have a third-party assembly we use into our project. They have used the NLog into their project and referenced NLog configuration into their configuration which I don't have access to.
I initially had a problem even adding the NLog to a project since both version of dlls were different. I had to download the source code of NLog and change the assembly name since alias was not working for me (OR I didn't know how to use it.)
After giving my own assembly name, NLog started wotrking but.. It started logging third-party log information too with it. Looks like they were not careful enough when defining the logger and they just defined (*). Now my question is, how do I avoid their stuff from logging in to my file? I have also tried setting it to final = true.
Here is how my very simple configuration file looks like.
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!--
See https://github.com/nlog/nlog/wiki/Configuration-file
for information on customizing logging rules and outputs.
-->
<targets>
<!-- add your targets here -->
<!--
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}" />
-->
<target name="errorLog" xsi:type="File"
fileName="${basedir}/logs/error/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}${onexception:${newline}EXCEPTION\: ${exception:format=ToString}}"
/>
<target name="generalLog" xsi:type="File"
fileName="C:/logs/${date:format=yyyy-MM-dd}.log"
layout="${longdate} ${uppercase:${level}} ${message}"/>
</targets>
<rules>
<!-- add your logging rules here -->
<logger name="Errors" writeTo="errorLog" minlevel="Warn"/>
<logger name="EBusiness.Model.*" writeTo="generalLog" levels="Trace,Debug,Info" final="true"/>
<!--
<logger name="*" minlevel="Trace" writeTo="f" />
-->
</rules>
</nlog>
If they are writing to the root logger, you should configure the root logger to not log to anywhere you care (maybe point it at a console logger) and your application can log to a specific logger which does write to your files.
Ugly, but should work.
For example:
Add this target:
<target name="console" xsi:type="Console" />
And Change the * rule to be:
<logger name="*" minlevel="Fatal " writeTo="console" />
Then, Add your own logger rule, say something like:
<logger name="MyNameSpace.*" minlevel="Trace" writeTo="logfile" final="true" />
Assuming you have a logfile target defined pointing at your file.

Categories