Reputation: 311
I've created two NLog custom targets on .NET Standard 2.0, and imported them into an existing ASP.NET 4.7.2 website.
nlog.config looks like this:
<?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"
autoReload="true"
internalLogLevel="Info"
internalLogFile="${basedir}/internal-nlog.txt"
throwExceptions="true"
throwConfigExceptions="true">
<extensions>
<add assembly="MyAssembly"/>
</extensions>
<targets async="false">
<target name="logconsole" xsi:type="Console" />
<target xsi:type="AzureTableTarget"
name="azureTable"
// some configs
/>
<target xsi:type="PostmarkLogTarget"
name="postmark"
// some configs
/>
</targets>
<rules>
<logger name="*" minlevel="Warn" writeTo="postmark" />
<logger name="*" minlevel="Info" writeTo="azureTable" />
<logger name="*" minlevel="Debug" writeTo="logconsole" />
</rules>
</nlog>
When the app starts locally, everything works fine. When it starts on the Azure App Service, I get this in the nlog internal log (and a big, fat error page):
2019-06-21 15:08:53.5719 Info Message Template Auto Format enabled
2019-06-21 15:08:53.6015 Info Loading assembly: MyAssembly
2019-06-21 15:08:53.6926 Info Adding target ConsoleTarget(Name=logconsole)
2019-06-21 15:08:53.7595 Error Parsing configuration from D:\home\site\wwwroot\NLog.config failed. Exception: NLog.NLogConfigurationException: Exception when parsing D:\home\site\wwwroot\NLog.config. ---> System.ArgumentException: Target cannot be found: 'AzureTableTarget'
at NLog.Config.Factory`2.CreateInstance(String itemName)
at NLog.Config.LoggingConfigurationParser.ParseTargetsElement(ILoggingConfigurationElement targetsElement)
at NLog.Config.LoggingConfigurationParser.ParseNLogSection(ILoggingConfigurationElement configSection)
at NLog.Config.XmlLoggingConfiguration.ParseNLogSection(ILoggingConfigurationElement configSection)
at NLog.Config.LoggingConfigurationParser.LoadConfig(ILoggingConfigurationElement nlogConfig, String basePath)
at NLog.Config.XmlLoggingConfiguration.ParseNLogElement(ILoggingConfigurationElement nlogElement, String filePath, Boolean autoReloadDefault)
at NLog.Config.XmlLoggingConfiguration.ParseTopLevel(NLogXmlElement content, String filePath, Boolean autoReloadDefault)
at NLog.Config.XmlLoggingConfiguration.Initialize(XmlReader reader, String fileName, Boolean ignoreErrors)
--- End of inner exception stack trace ---
2019-06-21 15:08:53.8489 Error Failed loading from config file location: D:\home\site\wwwroot\NLog.config Exception: NLog.NLogConfigurationException: Exception when parsing D:\home\site\wwwroot\NLog.config. ---> System.ArgumentException: Target cannot be found: 'AzureTableTarget'
at NLog.Config.Factory`2.CreateInstance(String itemName)
at NLog.Config.LoggingConfigurationParser.ParseTargetsElement(ILoggingConfigurationElement targetsElement)
at NLog.Config.LoggingConfigurationParser.ParseNLogSection(ILoggingConfigurationElement configSection)
at NLog.Config.XmlLoggingConfiguration.ParseNLogSection(ILoggingConfigurationElement configSection)
at NLog.Config.LoggingConfigurationParser.LoadConfig(ILoggingConfigurationElement nlogConfig, String basePath)
at NLog.Config.XmlLoggingConfiguration.ParseNLogElement(ILoggingConfigurationElement nlogElement, String filePath, Boolean autoReloadDefault)
at NLog.Config.XmlLoggingConfiguration.ParseTopLevel(NLogXmlElement content, String filePath, Boolean autoReloadDefault)
at NLog.Config.XmlLoggingConfiguration.Initialize(XmlReader reader, String fileName, Boolean ignoreErrors)
--- End of inner exception stack trace ---
at NLog.Config.XmlLoggingConfiguration.Initialize(XmlReader reader, String fileName, Boolean ignoreErrors)
at NLog.Config.XmlLoggingConfiguration..ctor(XmlReader reader, String fileName, Boolean ignoreErrors, LogFactory logFactory)
at NLog.Config.LoggingConfigurationFileLoader.LoadXmlLoggingConfiguration(XmlReader xmlReader, String configFile, LogFactory logFactory)
at NLog.Config.LoggingConfigurationFileLoader.LoadXmlLoggingConfigurationFile(LogFactory logFactory, String configFile)
at NLog.Config.LoggingConfigurationFileLoader.TryLoadLoggingConfiguration(LogFactory logFactory, String configFile, LoggingConfiguration& config)
2019-06-21 15:08:54.1153 Info Configuring from an XML element in D:\home\site\wwwroot\NLog.config...
2019-06-21 15:08:54.1457 Info Message Template Auto Format enabled
2019-06-21 15:08:54.1457 Info Loading assembly: MyAssembly
2019-06-21 15:08:54.1457 Info Adding target ConsoleTarget(Name=logconsole)
2019-06-21 15:08:54.3332 Info Adding target AzureTableTarget(Name=azureTable)
2019-06-21 15:08:54.3525 Info Adding target PostmarkLogTarget(Name=postmark)
2019-06-21 15:08:54.4120 Info Found 38 configuration items
2019-06-21 15:08:54.4738 Info Configuration initialized.
The second load comes about because I have code in global.asax.cs to register and configure the targets specifically. This code fires immediately after setting up AutoFac, and before anything tries to log to anywhere.
Running locally, the code proceeds through these steps in order, even in Release mode. It looks like it tries to log a message before configuration has completed when running on Azure.
Even if that were the case, both custom targets have default public constructors, so NLog should be able to instantiate them automagically. (Which is why I reload configs after setting up the targets.)
Two questions:
What is different about the Azure App Service that causes (or allows) NLog to jump the gun like that?
Short of removing nlog.config and setting up logging in code, how can I prevent this behavior from happening?
Upvotes: 0
Views: 1192
Reputation: 311
Found it. Wow.
I had this code hanging out in a .cs file:
public static readonly Logger Logger = LogManager.GetCurrentClassLogger();
On the App Service deployment, the static constructor of the class containing that line ran before App_Start had finished. On my local box, it didn't.
So I changed it to this:
public static Logger Logger => _logger ?? (_logger = LogManager.GetCurrentClassLogger());
private static Logger _logger;
...and everything works now. The Logger is only created when used, not just because ASP.NET wanted to instantiate static classes ahead of time.
Upvotes: 1
Reputation: 36740
Target cannot be found: 'AzureTableTarget'
This mean that the target class 'AzureTableTarget' cannot be found in one of the assemblies and thus an instance cannot be create.
You need to tell NLog in which assembly the AzureTableTarget type could be found.
Something like this:
<extensions>
<add assembly="AssemblyNameWhereAzureTableTargetIsDefined"/>
</extensions>
What is different about the Azure App Service that causes (or allows) NLog to jump the gun like that?
Are the same assemblies available? So is the assembly with the AzureTableTarget published?
Short of removing nlog.config and setting up logging in code
For this case it doesn't matter if NLog is configured from file or from code.
how can I prevent this behavior from happening?
Always add all external NLog extensions to <extensions>
Last but not least, throwExceptions="true"
isn't recommend for production! (If your logging breaks, do you really like that your application breaks?)
Upvotes: 1