How to include NLog config files in code behind - c#

I have a nlog.config file with the following entry:
<include file="${basedir}/ActiveConfig/NLog/*.config"/>
But I want to solve this via code behind and have not found a method to do so.
LogManager.LoadConfiguration()
is overwriting my existing configuration.
Did I miss something?

You could set it like this:
var config = XmlLoggingConfiguration
.CreateFromXmlString("<include file='${basedir}/ActiveConfig/NLog/*.config' />");
LogManager.Configuration = config; // apply

Think the easy solution is just to load all config-files into an in-memorystream, and then load that into a single XmlLoggingConfiguration:
var xmlReader = System.Xml.XmlReader.Create(memorystream);
NLog.LogManager.Configuration = new XmlLoggingConfiguration(xmlReader, null);
Something like this, where you put the contents of all config-files within the same <nlog>-root:
<nlog>
<!-- XML File 1 -->
<targets>
</targets>
<rules>
</rules>
<! -- XML File 2 -->
<targets>
</targets>
<rules>
</rules>
</nlog>
If you want the AutoReload-feature working also, then this might work:
class MySpecialLoggingConfiguration : XmlLoggingConfiguration
{
private string[] _fileNames;
public MySpecialLoggingConfiguration(string[] fileNames)
{
_fileNames = fileNames;
// Your special concat-logic in memory
}
public override LoggingConfiguration Reload()
{
return new MySpecialLoggingConfiguration(_fileNames);
}
public override IEnumerable<string> FileNamesToWatch => _fileNames;
}

Related

Create sub-folder that contains log-files for each request

I have an application that listens for a hash input, processes that input, and serves it back. I would like to create a separate log directory for each input with the following structure:
- hashcode1
- input1.txt
- output1.txt
- log1.txt
- hashcode2
- input2.txt
- output2.txt
- log2.txt
I've looked into the NLog library but it seems the config requires a hardcoded log location, whereas my implementation requires the hashcode as an input. How can I achieve this with NLog, or any other logging library?
You can create the NLog configuration from code. This allows you to dynamically create targets in your processing loop for the appropriate log outputs:
using NLog;
using NLog.Targets;
// Init the config if we don't have a nlog.config
LogManager.Configuration = new();
// Do the work
var work = new List<string> { "5dd2ec2f", "24e1e841", "05137e70" };
work.ForEach(ProcessHash);
void ProcessHash(string hash)
{
// Create dynamic configuration and logger
var fileTarget = new FileTarget(hash) { FileName = $"{hash}/log.txt" };
LogManager.Configuration.AddRuleForAllLevels(fileTarget, hash);
LogManager.ReconfigExistingLoggers();
var logger = LogManager.GetLogger(hash);
// Process...
logger.Info($"Processing hash {hash}");
/* ... your code here ... */
// Remove the logging target
LogManager.Configuration.RemoveTarget(fileTarget.Name);
}
You can create a scope-property like this:
void ProcessHashCode(string hashInput)
{
using (NLog.MappedDiagnosticsLogicalContext.SetScoped("HashCode", hashInput))
{
NLog.LogManager.GetLogger("HashInput").Info(hashInput);
NLog.LogManager.GetCurrentClassLogger().Info("Received Hashcode");
var hashOutput = hashInput.GetHashCode();
NLog.LogManager.GetLogger("HashOuput").Info(hashOutput);
}
}
Then you can do this in NLog.config
<nlog>
<variable name="logdir" value="${mdlc:HashCode:whenEmpty=${basedir}}" />
<targets>
<target name="hashinput" type="file" filename="${logdir}/input.txt" />
<target name="hashouput" type="file" filename="${logdir}/output.txt" />
<target name="hashlog" type="file" filename="${logdir}/log.txt" />
</target>
<rules>
<logger name="HashInput" writeTo="hashinput" final="true" />
<logger name="HashOuput" writeTo="hashouput" final="true" />
<logger name="*" writeTo="hashlog" />
</rules>
</nlog>
See also: https://github.com/NLog/NLog/wiki/MDLC-Layout-Renderer
Alternative you can use Logger.WithProperty("HashCode", hashInput) like this:
void ProcessHashCode(string hashInput)
{
var inputLogger = NLog.LogManager.GetLogger("HashInput").WithProperty("HashCode", hashInput);
var outputLogger =
NLog.LogManager.GetLogger("HashOutput").WithProperty("HashCode", hashInput);
var hashLogger =
NLog.LogManager.GetCurrentClassLogger.WithProperty("HashCode", hashInput);
inputLogger.Info(hashCode);
hashLogger.Info("Received Hashcode");
var hashOutput = hashInput.GetHashCode();
outputLogger.Info(hashOutput);
}
And instead use variable like this:
<variable name="logdir" value="${event-properties:HashCode:whenEmpty=${basedir}}" />
See also: https://github.com/NLog/NLog/wiki/Context#logevent-properties

Xamarin MvvmCross how to use NLog for IMvxLogProvider?

I followed Diagnostic & Logging and install NLog.Config nuget package to Android & iOS project.
on Android project, Setup.cs
public override MvxLogProviderType GetDefaultLogProviderType() => MvxLogProviderType.NLog;
On Xamarin.Forms project,
private static readonly IMvxLog _logger = Mvx.IoCProvider.Resolve<IMvxLogProvider>().GetLogFor<CanvasContainer>();
_logger.Debug($"startScale: {startScale}, currentScale: {currentScale}");
I checked _logger is not null.
Also, I changed NLog.config's Build Action to Embedded Resource.
Here is Android Project's NLog.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"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
throwExceptions="false"
internalLogFile="c:\temp\nlog-internal.log">
<targets>
<target xsi:type="File"
name="FileTarget"
fileName="${specialfolder:folder=MyDocuments}/logs/${shortdate}.log"
layout="${longdate}|${level:uppercase=true}|${logger}|${message}" />
<target xsi:type="ColoredConsole"
name="ConsoleTarget"
encoding="utf-8"
layout="${longdate}|${level:uppercase=true}|${logger}|${message}"
useDefaultRowHighlightingRules="true" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="FileTarget" />
<logger name="*" minlevel="Debug" writeTo="ConsoleTarget" />
</rules>
</nlog>
Is there anything wrong with this set up? I can not find sample or tutorial for this...
Think you need to help NLog when having NLog.config as Embedded Resource:
public override MvxLogProviderType GetDefaultLogProviderType() => MvxLogProviderType.NLog;
protected override IMvxLogProvider CreateLogProvider()
{
var nlogConfigFile = GetEmbeddedResourceStream(myAssembly, "NLog.config");
if (nlogConfigFile != null)
{
var xmlReader = System.Xml.XmlReader.Create(nlogConfigFile);
NLog.LogManager.Configuration = new XmlLoggingConfiguration(xmlReader, null);
}
return base.CreateLogProvider();
}
private static Stream GetEmbeddedResourceStream(Assembly assembly, string resourceFileName)
{
var resourcePaths = assembly.GetManifestResourceNames()
.Where(x => x.EndsWith(resourceFileName, StringComparison.OrdinalIgnoreCase))
.ToList();
if (resourcePaths.Count == 1)
{
return assembly.GetManifestResourceStream(resourcePaths.Single());
}
return null;
}
See also: https://github.com/NLog/NLog/wiki/Explicit-NLog-configuration-loading#loading-nlog-configuration-from-xamarin-resource

How to inject values into my NLog.config?

I use a NLog.config, but some values I want to change from outside. E.g. target:adress could change so I want to set it each time the software starts.
I imagine some thing like
var logger = new LoggerFactory().AddNLog().CreateLogger<Program>();
logger.target.adress = "myNewAdress";
How can I set values to my NLog.config?
You could edit the config in C# like this:
var configuration = LogManager.Configuration;
var fileTarget = configuration.FindTargetByName<FileTarget>("myTargetName");
fileTarget.FileName = "${basedir}/file.log";
LogManager.Configuration = configuration; //apply
Please note that combining the config file (nlog.config) and changing it in code, the reload of nlog.config could undo your changes. If you combine both, then reapply the changes on the reload event. E.g.
public void UpdateConfig()
{
var configuration = LogManager.Configuration;
var fileTarget = configuration.FindTargetByName<FileTarget>("myTargetName");
fileTarget.FileName = "${basedir}/file.log";
LogManager.Configuration = configuration; //apply
}
// On start of your program
UpdateConfig();
LogManager.ConfigurationReloaded += (sender, e) =>
{
//Re apply if config reloaded
UpdateConfig();
};
See also: https://github.com/NLog/NLog/wiki/Configure-from-code
I recommend that one makes use of the NLog context layoutrenderers instead of modifying target properties at runtime. It ofcourse requires that the target-property supports NLog Layout.
Example of NLog.config:
<nlog>
<targets>
<target type="file" name="file" fileName="${gdc:item=LogDir}\LogFile.txt}" />
</targets>
<rules>
<logger minLevel="Trace" writeTo="file" />
</rules>
</nlog>
Then at runtime you can change the GDC-variables:
NLog.GlobalDiagnosticsContext.Set("LogDir", "C:\\Temp");
${gdc} Can also be combined with WhenEmpty, so one can provide a fallback default value, when nothing has been asigned from code.
See also: https://github.com/NLog/NLog/wiki/Context

How can I automatically verify that all web.config transforms have the same elements?

I'm working on an ASP.NET project that has several web.config transformations that are generated at build time (all at once, using MsBuild); one .config file for each deployment environment.
EX:
<Target Name="BeforeBuild">
<TransformXml
Source="Web.Base.config"
Transform="Web.DevServer1.config"
Destination="ConfigBuild\Web.DevServer1.config" />
<TransformXml
Source="Web.Base.config"
Transform="Web.QAServer1.config"
Destination="ConfigBuild\Web.QAServer1.config" />
<!-- ... -->
<!-- ... -->
</Target>
Each transformation has several elements whose values are substituted into the base web.config file. Management and I are concerned that a necessary element could potentially be overlooked by mistake in one of the transformation files.
Is there a way to automatically verify that none of our transformation files are missing any elements, using either MsBuild or some other VisualStudio tool?
Preferably, this check would be carried out at build time.
Is there a way to automatically verify that none of our transformation files are missing any elements, using either MsBuild or some other VisualStudio tool?
You can create a custom task, which compare the config file by using XmlDocument class, if they are different, use Log.LogMessage to output the node message. Like this:
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.Xml;
namespace Common
{
public class SimpleTask3 : Task
{
private string myProperty;
// The [Required] attribute indicates a required property.
// If a project file invokes this task without passing a value
// to this property, the build will fail immediately.
[Required]
public string MyProperty
{
get
{
return myProperty;
}
set
{
myProperty = value;
}
}
public override bool Execute()
{
// Log a high-importance comment
Log.LogMessage(MessageImportance.High, "The task was passed \"" + myProperty + "\"");
XmlDocument xDoc = new XmlDocument();
xDoc.Load(myProperty + "/web.base.config");
XmlDocument sDoc = new XmlDocument();
sDoc.Load(myProperty + "/ConfigBuild/Web.DevServer1.config");
//compare with them and check the different.
//if different
Log.LogMessage(MessageImportance.High, "different message");
return true;
}
}
}
Web.config:
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="BeforeBuild">
<!--<Message Text="BuildDependsOn: $(BuildDependsOn)" />-->
<Message Text="Inside of BeforeBuild, time: $([System.DateTime]::Now)" />
<TransformXml Source="D:\Project\Msbuild\App1\App2\Web.Base.config" Transform="D:\Project\Msbuild\App1\App2\Web.DevServer1.config" Destination="D:\Project\Msbuild\App1\App2\ConfigBuild\Web.DevServer1.config" />
<TransformXml Source="D:\Project\Msbuild\App1\App2\Web.Base.config" Transform="D:\Project\Msbuild\App1\App2\Web.QAServer1.config" Destination="D:\Project\Msbuild\App1\App2\ConfigBuild\Web.QAServer1.config" />
</Target>
<UsingTask TaskName="Common.SimpleTask3"
AssemblyFile="D:\Project\Msbuild\App1\Common\bin\Debug\Common.dll"/>
<Target Name="AfterBuild">
<SimpleTask3 MyProperty="D:\Project\Msbuild\App1\App2"/>
</Target>

Logging datetime with ms precision using NLog event context layout renderer

I am trying to log some audit information to a SQL Server 2008 table using NLog 2. In order to be able to pass parameters to the SQL insert query, I'm using LogEventInfo and the Event Context Layout Renderer.
The logging itself works but the datetime is stored with only second precision. I want to be able to store with millisecond precision but have not found anything that shows me how to do this.
This is the C# code where I log the event:
private void LogMessage(DateTime requestDateTime)
{
LogEventInfo theEvent = new LogEventInfo(LogLevel.Debug, "", "Pass my custom value");
theEvent.Properties["RequestDate"] = requestDateTime;
}
This is the target I have in my NLog.config configuration:
<target xsi:type="Database"
name="SqlLog"
dbProvider="sqlserver"
connectionString="server=localhost;database=Test;integrated security=sspi">
<commandText>
INSERT [dbo].[ApiLog]
(
[ServerName], [RequestDate]
)
VALUES (#ServerName, #RequestDate);
</commandText>
<parameter name="#ServerName" layout="${machinename}"/>
<parameter name="#RequestDate" layout="${event-context:item=RequestDate}"/>
</target>
There is a workaround I have found using theEvent.Properties["RequestDate"] = requestDateTime.ToString("yyyy-MM-dd HH:mm:ss.fff") but I would prefer not to have to do this because then you may run into problems with date time formatting and culture.
Does anyone know of a way that I can change the precision with config in NLog.config?
It looks like the EventContextLayoutRenderer uses Convert.ToString which will lose the precision you are looking for:
public class EventContextLayoutRenderer : LayoutRenderer
{
//....
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
object value;
if (logEvent.Properties.TryGetValue(this.Item, out value))
{
builder.Append(Convert.ToString(value, CultureInfo.InvariantCulture));
}
}
}
As you probably know (and I'm not sure if it's helpful in your situation), but there is a Date layout where you can specify the format and/or a LongDate one which will provide something like 2014-01-01 12:12:12.1234
EDIT
For what it's worth, you can add a customer layout renderer very easily
[LayoutRenderer("event-context-dateTime")]
public class DateTimeContextLayoutRenderer : EventContextLayoutRenderer
{
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
object value;
if (logEvent.Properties.TryGetValue(this.Item, out value))
{
//Your code here
}
}
}
And in your configuration
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<extensions>
<add assembly="YourAssembly"/>
</extensions>
<targets>
<target xsi:type="File" name="file" fileName="c:\temp\NlogLayout.txt"
layout="${longdate} ${event-context-dateTime:item=RequestDate}" />
...
</nlog>

Categories

Resources