I am trying to log some information to database, but being kind of new to nlog config ( until now the default config from Tutorials worked fine) I am not sure what I am missing from my config to work.
I get this error when I app starts
2022-02-24 17:06:46.1375 Error Failed loading from config file location: C:\Repos\App\bin\Debug\netcoreapp3.1\NLog.config Exception: NLog.NLogConfigurationException: Exception when parsing C:\Repos\App\bin\Debug\netcoreapp3.1\NLog.config.
---> System.FormatException: Input string was not in a correct format.
at System.Text.StringBuilder.FormatError()
at System.Text.StringBuilder.AppendFormatHelper(IFormatProvider provider, String format, ParamsArray args)
at System.String.FormatHelper(IFormatProvider provider, String format, ParamsArray args)
at System.String.Format(String format, Object[] args)
at NLog.NLogConfigurationException..ctor(Exception innerException, String message, Object[] messageParameters)
at NLog.Config.LoggingConfigurationParser.ConfigureObjectFromAttributes(Object targetObject, ILoggingConfigurationElement element, Boolean ignoreType)
at NLog.Config.LoggingConfigurationParser.ParseTargetElement(Target target, ILoggingConfigurationElement targetElement, Dictionary`2 typeNameToDefaultTargetParameters)
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, 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)
My Config files 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"
throwConfigExceptions="true"
internalLogLevel="trace"
internalLogFile="c:\temp\internal-nlog-AspNetCore3.txt">
<!-- enable asp.net core layout renderers -->
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<!-- the targets to write to -->
<targets>
<!-- File Target for all log messages with basic details -->
<target xsi:type="File" name="allfile" fileName="c:\temp\nlog-AspNetCore3-all-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}" />
<!-- File Target for own log messages with extra web details using some ASP.NET core renderers -->
<target xsi:type="File" name="ownFile-web" fileName="c:\temp\nlog-AspNetCore3-own-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}|" />
<!--Console Target for hosting lifetime messages to improve Docker / Visual Studio startup detection -->
<target xsi:type="Console" name="lifetimeConsole" layout="${MicrosoftConsoleLayout}" />
<target xsi:type="database" name="database"
layout="${longdate}|${level:uppercase=true}|${logger}|${message}|identity:${identity}|windows-identity:${windows-identity}">
<connectionString>
Data Source=MYCOMPUTER;Initial Catalog=DatabaseName;Integrated Security=True
</connectionString>
<commandText>
INSERT INTO SystemLogging(LogDate,LogLevel,LogLogger,LogMessage,LogMachineName, LogUserName, LogCallSite, LogThread, LogException, LogStackTrace)
VALUES(#time_stamp, #level, #logger, #message,#machinename, #user_name, #call_site, #threadid, #log_exception, #stacktrace);
</commandText>
<parameter name="#time_stamp" layout="${longdate}"/>
<parameter name="#level" layout="${level}"/>
<parameter name="#logger" layout="${logger}"/>
<parameter name="#message" layout="${message}"/>
<parameter name="#machinename" layout="${machinename}"/>
<parameter name="#user_name" layout="${windows-identity:domain=true}"/>
<parameter name="#call_site" layout="${callsite:filename=true}"/>
<parameter name="#threadid" layout="${threadid}"/>
<parameter name="#log_exception" layout="${exception}"/>
<parameter name="#stacktrace" layout="${stacktrace}"/>
</target>
</targets>
<!-- rules to map from logger name to target -->
<rules>
<!--All logs, including from Microsoft-->
<logger name="*" minlevel="Trace" writeTo="allfile" />
<!--Output hosting lifetime messages to console target for faster startup detection -->
<logger name="Microsoft.Hosting.Lifetime" minlevel="Info" writeTo="lifetimeConsole, ownFile-web" final="true" />
<!--Skip non-critical Microsoft logs and so log only own logs-->
<logger name="Microsoft.*" maxlevel="Info" final="true" />
<!-- BlackHole -->
<logger name="*" minlevel="Trace" writeTo="ownFile-web" />
</rules>
</nlog>
I know for a fact the problem is somewhere in line 28, layout of db connection.
layout="${longdate}|${level:uppercase=true}|${logger}|${message}|identity:${identity}|windows-identity:${windows-identity}"
If I remove this line however I get an other error
"LayoutRenderer cannot be found: 'windows-identity'"
It feels like an obvious problem, however search the internet I could not find a solution. Anyone with more experience in NLog, can you share an example of DB Logging that uses Windows-Auth for db logging and not a username/password in connection string.
I've also tried ( added '' to windows-identity, but that didn't work either )
layout="${longdate}|${level:uppercase=true}|${logger}|${message}|identity: '${identity}' | windows-identity: '${windows-identity}'"
Edit:
Updated config
<?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"
throwConfigExceptions="true"
internalLogLevel="trace"
internalLogFile="c:\temp\internal-nlog-AspNetCore3.txt">
<!-- enable asp.net core layout renderers -->
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
<add assembly="NLog.WindowsIdentity"/>
</extensions>
<!-- the targets to write to -->
<targets>
<!-- File Target for all log messages with basic details -->
<target xsi:type="File" name="allfile" fileName="c:\temp\nlog-AspNetCore3-all-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}" />
<!-- File Target for own log messages with extra web details using some ASP.NET core renderers -->
<target xsi:type="File" name="ownFile-web" fileName="c:\temp\nlog-AspNetCore3-own-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}|" />
<!--Console Target for hosting lifetime messages to improve Docker / Visual Studio startup detection -->
<target xsi:type="Console" name="lifetimeConsole" layout="${MicrosoftConsoleLayout}" />
<target xsi:type="database" name="database"
layout="${longdate}|${level:uppercase=true}|${logger}|${message}|identity:${identity}|windows-identity:${environment-user}">
<connectionString>
Data Source=MYCOMPUTER;Initial Catalog=DatabaseName;Integrated Security=True
</connectionString>
<commandText>
INSERT INTO SystemLogging(LogDate,LogLevel,LogLogger,LogMessage,LogMachineName, LogUserName, LogCallSite, LogThread, LogException, LogStackTrace)
VALUES(#time_stamp, #level, #logger, #message,#machinename, #user_name, #call_site, #threadid, #log_exception, #stacktrace);
</commandText>
<parameter name="#time_stamp" layout="${longdate}"/>
<parameter name="#level" layout="${level}"/>
<parameter name="#logger" layout="${logger}"/>
<parameter name="#message" layout="${message}"/>
<parameter name="#machinename" layout="${machinename}"/>
<parameter name="#user_name" layout="${windows-identity:domain=true}"/>
<parameter name="#call_site" layout="${callsite:filename=true}"/>
<parameter name="#threadid" layout="${threadid}"/>
<parameter name="#log_exception" layout="${exception}"/>
<parameter name="#stacktrace" layout="${stacktrace}"/>
</target>
</targets>
<!-- rules to map from logger name to target -->
<rules>
<!--All logs, including from Microsoft-->
<logger name="*" minlevel="Trace" writeTo="allfile" />
<!--Output hosting lifetime messages to console target for faster startup detection -->
<logger name="Microsoft.Hosting.Lifetime" minlevel="Info" writeTo="lifetimeConsole, ownFile-web" final="true" />
<!--Skip non-critical Microsoft logs and so log only own logs-->
<logger name="Microsoft.*" maxlevel="Info" final="true" />
<!-- BlackHole -->
<logger name="*" minlevel="Trace" writeTo="ownFile-web" />
</rules>
</nlog>
So it turns out the layout is not working. I removed it and app would start with no erros. However I still had to add a rule in order to make logs in db. Here is the working version of the nlog 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"
autoReload="true"
throwConfigExceptions="true"
internalLogLevel="trace"
internalLogFile="c:\temp\internal-nlog-AspNetCore3.txt">
<!-- enable asp.net core layout renderers -->
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
<add assembly="NLog.WindowsIdentity"/>
</extensions>
<!-- the targets to write to -->
<targets>
<!-- File Target for all log messages with basic details -->
<target xsi:type="File" name="allfile" fileName="c:\temp\nlog-AspNetCore3-all-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}" />
<!-- File Target for own log messages with extra web details using some ASP.NET core renderers -->
<target xsi:type="File" name="ownFile-web" fileName="c:\temp\nlog-AspNetCore3-own-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}|" />
<!--Console Target for hosting lifetime messages to improve Docker / Visual Studio startup detection -->
<target xsi:type="Console" name="lifetimeConsole" layout="${MicrosoftConsoleLayout}" />
<target xsi:type="database" name="database">
<connectionString>
Data Source=MYCOMPUTER;Initial Catalog=DbName;Integrated Security=True
</connectionString>
<commandText>
INSERT INTO SystemLogging(LogDate,LogLevel,LogLogger,LogMessage,LogMachineName, LogUserName, LogCallSite, LogThread, LogException, LogStackTrace)
VALUES(#time_stamp, #level, #logger, #message,#machinename, #user_name, #call_site, #threadid, #log_exception, #stacktrace);
</commandText>
<parameter name="#time_stamp" layout="${longdate}"/>
<parameter name="#level" layout="${level}"/>
<parameter name="#logger" layout="${logger}"/>
<parameter name="#message" layout="${message}"/>
<parameter name="#machinename" layout="${machinename}"/>
<parameter name="#user_name" layout="${windows-identity:domain=true}"/>
<parameter name="#call_site" layout="${callsite:filename=true}"/>
<parameter name="#threadid" layout="${threadid}"/>
<parameter name="#log_exception" layout="${exception}"/>
<parameter name="#stacktrace" layout="${stacktrace}"/>
</target>
</targets>
<!-- rules to map from logger name to target -->
<rules>
<!--All logs, including from Microsoft-->
<logger name="*" minlevel="Trace" writeTo="allfile" />
<logger name="*" minLevel="Trace" writeTo="database"/>
<!--Output hosting lifetime messages to console target for faster startup detection -->
<logger name="Microsoft.Hosting.Lifetime" minlevel="Info" writeTo="lifetimeConsole, ownFile-web" final="true" />
<!--Skip non-critical Microsoft logs and so log only own logs-->
<logger name="Microsoft.*" maxlevel="Info" final="true" />
<!-- BlackHole -->
<logger name="*" minlevel="Trace" writeTo="ownFile-web" />
</rules>
</nlog>
The NLog Database-Target doesn't have a Layout-property, that can be assigned. Instead use input parameter-collection.
WindowsIdentity-nuget-package is required for ${windows-identity} on NetCore, and must be included:
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
<add assembly="NLog.WindowsIdentity"/>
</extensions>
Maybe you are looking for ${environment-user} ?
See also: https://github.com/NLog/NLog/wiki/Windows-Identity-Layout-Renderer
I have an NLog database target that looks like this:
<target xsi:type="Database" name="database"
connectionString="Server=.\SQLEXPRESS;Database=ApplicationOne;Trusted_Connection=True;MultipleActiveResultSets=true;User Id=User0101;Password=PW0101"
commandText="INSERT INTO [SchemaOne].[EventLogs](Id, Message, Level, Logger )VALUES(NewID(), #Message, #Level, #Logger)">
<parameter name="#Message" layout="${message}" />
<parameter name="#Level" layout="${level}" />
<parameter name="#Logger" layout="${logger}" />
</target>
Is it possible to change the connectionString to use connectionStringName from my appsettings instead?
My appsettings is called dssettings.json and it contains the connection details here:
"DatabaseConfiguration": {
"DatabaseName": "ApplicationOne",
"ConnectionName": "DefaultConnection",
"ConnectionString": "Server=.\\SQLEXPRESS;Database=ApplicationOne;Trusted_Connection=True;MultipleActiveResultSets=true;User Id=User0101;Password=PW0101"
},
Update NLog.Extension.Logging ver. 1.4.0
With NLog.Extension.Logging ver. 1.4.0 then you can now use ${configsetting}
See also: https://github.com/NLog/NLog/wiki/ConfigSetting-Layout-Renderer
Original Answer
With help from nuget-package NLog.Appsettings.Standard then you can normally do this:
<extensions>
<add assembly="NLog.Appsettings.Standard" />
</extensions>
<targets>
<target xsi:type="Database" name="database"
connectionString="${appsettings:name=DatabaseConfiguration.ConnectionString}"
commandText="INSERT INTO [SchemaOne].[EventLogs](Id, Message, Level, Logger )VALUES(NewID(), #Message, #Level, #Logger)">
<parameter name="#Message" layout="${message}" />
<parameter name="#Level" layout="${level}" />
<parameter name="#Logger" layout="${logger}" />
</target>
</targets>
But because you are using a special dssettings.json (instead of appsettings.json), then you probably have to implement your own custom NLog layout renderer:
https://github.com/NLog/NLog/wiki/How-to-write-a-custom-layout-renderer
Maybe you can use the source-code from the above nuget-package as inspiration for loading dssettings.json. Or maybe create PullRequest that adds support for specifying non-default config-filename.
I am using SlowCheetah to transform my Log4Net files when I publish. However, it can't seem to distinguish between the attributes in different appender sections.
My Log4Net.config looks basically like this:
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<appender name="SmtpAppender" type="log4net.Appender.SmtpAppender">
<to value="DevEmail" />
<from value="DevEmail" />
<subject value="Dev Warning" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="Time: %date%newlineHost: %property{log4net:HostName}%newlineClass: %logger%newlineUser: %property{user}%newlineMessage: %message%newline%newline%newline" />
</layout>
<threshold value="WARN" />
</appender>
<appender name="FatalSmtpAppender" type="log4net.Appender.SmtpAppender">
<to value="DevEmail" />
<from value="DevEmail" />
<subject value="Dev Fatal" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="Time: %date%newlineHost: %property{log4net:HostName}%newlineClass: %logger%newlineUser: %property{user}%newlineMessage: %message%newline%newline%newline" />
</layout>
<threshold value="FATAL" />
</appender>
</log4net>
And my transform file looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<log4net xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appender name="SmtpAppender" type="log4net.Appender.SmtpAppender">
<to value="ProductionEmail" xdt:Transform="SetAttributes" />
<from value="ProductionEmail" xdt:Transform="SetAttributes" />
<subject value="Production Warning" xdt:Transform="SetAttributes" />
</appender>
<appender name="FatalSmtpAppender" type="log4net.Appender.SmtpAppender">
<to value="ProductionEmail" xdt:Transform="SetAttributes" />
<from value="ProductionEmail" xdt:Transform="SetAttributes" />
<subject value="Production Fatal" xdt:Transform="SetAttributes" />
</appender>
</log4net>
The problem is that the transformed config has the same subject attribute value for both appenders; I guess when it hits the SetAttributes it can't tell which tag it's looking for, so it transforms all of them. What it the correct syntax to tell it to only find the elements within the same appender? I assume I need to use the xdt:Locator attribute, but I can't do Match(name) like I do for web.config because these elements don't have a name attribute. The appender element has a name attribute, but I don't know how to tell it to match based on the parent element's name.
I know that I could use replace on the appender node, with the match(Name), but then I would be replacing the entire node, including a bunch of elements such as the layout which I don't want to be transformed (and thus have multiple copy-pastes of the same code, which I would like to avoid).
I found the answer in this MSDN article: http://msdn.microsoft.com/en-us/library/dd465326.aspx.
I needed to use xdt:Locator="Match(name)" on the parent <appender> node, and then xdt:Transform on the child nodes. I had tried this previously but had used xdt:locator="Match(name)" instead of xdt:Locator="Match(name)"... The attribute is case sensitive.
I use log4net to log the errors in my web application and it works fine. However if I place the same code in website I get error "Unrecognized configuration section log4net"
here is my web.config section
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,Log4net" requirePermission="false"/>
<root>
<level value="RELEASE" />
<appender-ref ref="LogFileAppender" />
</root>
<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender" >
<param name="File" value="D:\ESSReport\Logs\ESSlog.log" />
<param name="AppendToFile" value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="5" />
<maximumFileSize value="4MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%newline%-5p%d{yyyy-MM-dd hh:mm:ss} [%thread] [%logger] [%line] %newline - %message" />
</layout>
</appender>
I have added dll to my website
You are probably missing configuration section registration.
Here is sample code how you can register custom section:
<configuration>
<configSections>
<sectionGroup name="LoggerConfiguration">
<section name="GPWFLogger" type="GP.Solutions.WF.Entities.LoggerConfiguration,GPWFLogger" allowDefinition="Everywhere" allowLocation="true"/>
</sectionGroup>
</configSections>
<LoggerConfiguration>
<GPWFLogger
ConnectionStringName="ASPNETDB"
LogLevel="Full"
LogPrimaryTarget="SqlServer"
LogFilePath="GPWFwebClient.log" />
</LoggerConfiguration>
Take notice that LoggerConfiguration is registred inside sectionGroup.
You can use this principle in your case.
I am trying to add Failed Request Tracing to my IIS 7/ASP.NET server.
First, I create failed request tracing for "all content, error codes 400-999" because want to save all errors.
Then, I try to create a trace for "all content, time: 5 seconds" because I want to trace all "long" requests. However, IIS 7 gives me an error: "A failed request trace for this content already exists".
How can I add this second trace for all content that takes > 5 seconds?
In your web.config the Failed Request Tracing config looks something like:
<tracing>
<traceFailedRequests>
<add path="*">
<traceAreas>
<add provider="ASP" verbosity="Verbose" />
<add provider="ASPNET" areas="Infrastructure, etc" verbosity="Verbose" />
<add provider="ISAPI Extension" verbosity="Verbose" />
<add provider="WWW Server" areas="Authentication, etc" verbosity="Verbose" />
</traceAreas>
<failureDefinitions statusCodes="400-999" />
</add>
</traceFailedRequests>
</tracing>
The attribute path defines the content type i.e. the options in the first page of the Add FRT wizard (*, *.aspx, *.asp, Custom).
If you examine the schema for the system.webServer/tracing/traceFailedRequests section in applicationHost.config (located in %systemroot%\System32\inetsrv\
config\schema\IIS_schema.xml you'll find the following constraints:
The failed request path must be unique:
<attribute name="path" type="string" isUniqueKey ="true" />
Within a path each provider (ASP, ASPNET, ISAPI Extension etc) must be unique:
<attribute name="provider" type="string" required="true" isUniqueKey="true" />
If you added another trace rule to trace the same content (*) but specifying timeTaken then you'd be trying to add:
<add path="*">
<traceAreas>
<add provider="ASP" verbosity="Verbose" />
<add provider="ASPNET" areas="Infrastructure, etc" verbosity="Verbose" />
<add provider="ISAPI Extension" verbosity="Verbose" />
<add provider="WWW Server" areas="Authentication, etc" verbosity="Verbose" />
</traceAreas>
<failureDefinitions statusCodes="400-999" />
</add>
This of course conflicts with the rules in the schema which say that the path must be unique.
However what you can do is specify specific content that you want to trace when the timeTaken is >= to 5 seconds.
For example:
<add path="*.aspx">
<traceAreas>
<add provider="ASP" verbosity="Verbose" />
<add provider="ASPNET" areas="Infrastructure, etc" verbosity="Verbose" />
<add provider="ISAPI Extension" verbosity="Verbose" />
<add provider="WWW Server" areas="Authentication, etc" verbosity="Verbose" />
</traceAreas>
<failureDefinitions timeTaken="00:00:05" statusCodes="400-999" />
</add>
<add path="*.asp">
<traceAreas>
<add provider="ASP" verbosity="Verbose" />
<add provider="ASPNET" areas="Infrastructure, etc" verbosity="Verbose" />
<add provider="ISAPI Extension" verbosity="Verbose" />
<add provider="WWW Server" areas="Authentication, etc" verbosity="Verbose" />
</traceAreas>
<failureDefinitions timeTaken="00:00:05" statusCodes="400-999" />
</add>
<add path="*.asmx">
<traceAreas>
<add provider="ASP" verbosity="Verbose" />
<add provider="ASPNET" areas="Infrastructure, etc" verbosity="Verbose" />
<add provider="ISAPI Extension" verbosity="Verbose" />
<add provider="WWW Server" areas="Authentication, etc" verbosity="Verbose" />
</traceAreas>
<failureDefinitions timeTaken="00:00:05" statusCodes="400-999" />
</add>
Not as convenient as just being able to do a wildcard but it is a workaround.