Modify NLog configurations specified with Configuration API through NLog config file xml - c#

I have a project which uses the below code to create a NLog instance.
public FileTarget CreateNLogFileTarget(string layout, FileArchivePeriod archiveMode, int maxArchiveFiles,
bool keepFileOpen, bool enableConcurrentWrites, ArchiveNumberingMode archiveNumberingMode, string fileName)
{
FileTarget fileTarget = new FileTarget();
fileTarget.Layout = layout;
fileTarget.ArchiveEvery = archiveMode;
fileTarget.MaxArchiveFiles = maxArchiveFiles;
fileTarget.KeepFileOpen = keepFileOpen;
fileTarget.ConcurrentWrites = enableConcurrentWrites;
fileTarget.ArchiveNumbering = archiveNumberingMode;
fileTarget.FileName = fileName;
return fileTarget;
}
FileTarget infoLogFileTarget = CreateNLogFileTarget(#"${longdate} ${message}",
FileArchivePeriod.Hour, 70, false, true, ArchiveNumberingMode.Rolling, "${basedir}/Logs/" + infoLogName + "/${shortdate}{#}.log");
I am using this project in another project and I need to use this NLog utility class to create my loggers. But I need to override these configurations. How can I override these configurations through the xml file? Any help would be much appreciated.

To use the FileTarget from CreateNLogFileTarget in your XML config, you should first find out the target name of the FileTarget it's probably in other parts of the code. Then you could use the target in your config:
<logger name='*' minlevel="Trace" writeTo='theTarget' />

Maybe by using NLog-variables. Change your CreateNLogFileTarget to setup the parameters to get their value from NLog-variables.
Then on startup check if these NLog variables already exists in the loaded NLog-configuration. If not then they are set by the runtime, before calling CreateNLogFileTarget.
https://github.com/NLog/NLog/wiki/Configuration-file#variables

Related

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 to get the machine configuration filename in .NET Core

I am porting an application from .NET Framework to .NET Core (Standard).
Within the application, we have the following code
public LogMessageListenerFromConfig()
: this(AppDomain.CurrentDomain.BaseDirectory.ConfigurationFile, LoggingSection.DefaultName)
{ }
public LogMessageListenerFromConfig(string section)
: this(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, section)
{ }
public LogMessageListenerFromConfig(string filename, string section)
{
// todo: set up a file watcher and refresh the listeners (etc.) when it changes.
var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = filename };
var configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
_section = LoggingSection.Section(configuration, section);
Refresh();
}
This appears to be compatible with .NET Core apart from the following statement
AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
There is no SetupInformation on an AppDomain anymore. Fair enough I read that it would cause issues etc. However, how else can I get the application configuration file name in a utility class?
Please be aware that this class will be used in both console and asp.net (core) applications.
Any help appreciated
Stephen
Same problem if you are porting a library to .Net Standard. Solution is this;
install this nuget package system.configuration.configurationmanager then you can use:
var conf = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var configFilePath = conf.FilePath;

ExeConfigurationFileMap: InvalidArgumentException, The string parameter 'fileMap.ExeConfigFilename' cannot be null or empty

I am getting an InvalidArgumentException when I run my application. I am attempting to create a new ExeConfigurationFileMap, and then load it with ConfigurationManager.
public static ExeConfigurationFileMap configFile = new ExeConfigurationFileMap(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\QuikSnap\\QuikSnap.config");
public static Configuration config = ConfigurationManager.OpenMappedExeConfiguration(Settings.configFile, ConfigurationUserLevel.None);
I have also attempted to set the configuration file after declaring it, but still didn't have any luck.
If I attempt to continue after this exception, I next receive a TypeInitalizationException upon trying to set a variable to one of the values in the configuration file.
Ran into this same problem. For some ridiculous reason, initializing ExeConfigurationFileMap even with the filepath does not set the property ExeConfigFilename which is required by the Configuration objects constructor. I fixed it by immediately setting that property after instantiating the ExeConfigurationFileMap object like below:
public static ExeConfigurationFileMap configFile = new ExeConfigurationFileMap(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\QuikSnap\\QuikSnap.config");
configFile.ExeConfigFilename = "QuikSnap.config";
public static Configuration config = ConfigurationManager.OpenMappedExeConfiguration(Settings.configFile, ConfigurationUserLevel.None);
You didn't set the right property with the value of the config file path.
Also, you have a static variable dependency on another static variable in the same class. There could be potentially an issue of order of execution here (though i'm not sure)
Try this instead:
public static Configuration config = ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap()
{
ExeConfigFilename = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\QuikSnap\\QuikSnap.config"
}, ConfigurationUserLevel.None);

Tricking / Forcing Enterprise Library and EF to use a different config file

I am building a plug-in for another system, which causes it not to use app/exe.config. I am using Enterprise Library Logging 5.0 and EF5. How do I let these use a different config file ? I solved the problem for these two tool sets individually, but is there a generic way, should I use another tool which also reads the config files ?
My solution for EF5 is :
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = "MyConfig.config";
Configurationn config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
string connString = config.ConnectionStrings.ConnectionStrings["MyConnectionName"].ConnectionString;
MyDbContext c = new MyDbContext(connString );
which uses the following custom constructor :
public MyDbContext(string nameOrconnectionString)
: base(nameOrconnectionString)
{
}
For Enterprise Library 5 :
FileConfigurationSource fcs = new FileConfigurationSource("MyConfig.config");
EnterpriseLibraryContainer.Current = EnterpriseLibraryContainer.CreateDefaultContainer(fcs);
This is what I was looking for :
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", #"SomeDirectory\blabla.config");

Modifying app.config via custom msi installer

I need to create an address string in app.config as:
<client>
<endpoint address="http://ServerName/xxx/yyy.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IClientIInfoService"
contract="DocuHealthLinkSvcRef.IClientIInfoService" name="BasicHttpBinding_IClientIInfoService" />
</client>
The ServerName need to be entered by the user during installation.
For that i have created a new UI dialog in the Installer. I have also written an Installer.cs class and overrided the install () as:
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
string targetDirectory = Context.Parameters["targetdir"];
string ServerName = Context.Parameters["ServerName"];
System.Diagnostics.Debugger.Break();
string exePath = string.Format("{0}myapp.exe", targetDirectory);
Configuration config = ConfigurationManager.OpenExeConfiguration(exePath);
config.AppSettings.Settings["ServerName"].Value = ServerName;
config.Save();
}
}
But how do i use this ServerName in my app.config to create the specified string.
I'm working on VS2010.
You could use WiX (Windows Installer XML toolset) to build your MSI, in which case you can use the XmlFile utility tag to update the server name:
<util:XmlFile Id="UpdateServerName" File="[INSTALLLOCATION]AppName.exe.config" Action="setValue" ElementPath="/client/endpoint" Name="address" Value="http://[SERVERNAME]/xxx/yyy.svc" />
You can capture the server name during installation using a WixUI extension form.
Advantages of WiX: WiX is msbuild compliant (unlike .vdproj files), and gives you much finer-grained control over your installer, among other things
Assuming you are using the full ServiceModel section group in the app.config
Essentially you follow these steps:
Load ServiceModel config section
Get Client Section
Get ChannelEndpoint Element
Change Address value by replacing string "ServerName" with entered value
Set Address attribute to new value
Save config
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
string targetDirectory = Context.Parameters["targetdir"];
string ServerName = Context.Parameters["ServerName"];
System.Diagnostics.Debugger.Break();
string exePath = string.Format("{0}myapp.exe", targetDirectory);
Configuration config = ConfigurationManager.OpenExeConfiguration(exePath);
config.AppSettings.Settings["ServerName"].Value = ServerName;
//Get ServiceModelSectionGroup from config
ServiceModelSectionGroup group = ServiceModelSectionGroup.GetSectionGroup (config);
//get the client section
ClientSection clientSection = group.Client;
//get the first endpoint
ChannelEndpointElement channelEndpointElement = clientSection.Endpoints[0];
//get the address attribute and replace servername in the string.
string address = channelEndpointElement.Address.ToString().Replace("ServerName", ServerName);
//set the Address attribute to the new value
channelEndpointElement.Address = new Uri(address);
config.Save();
}
At the end of the day, app.config is xml file. You can use Linq To XML or XPathNavigator to replace the address attribute of endpoint element.
Below code uses Linq to Xml
using System.Xml.Linq;
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
string targetDirectory = Context.Parameters["targetdir"];
string ServerName = Context.Parameters["ServerName"];
System.Diagnostics.Debugger.Break();
string configPath = string.Format("{0myapp.exe.config", targetDirectory);
XElement root = XElement.Load(configPath);
var endPointElements = root.Descendants("endpoint");
foreach(var element in endPointElements)
{
element.Attribute("address").Value = ServerName;
}
root.Save(configPath);
}
}
Since you have a windows-installer tag, I assume you either have an MSI package, or can create one...
Then:
You can create a public MSI property like ENDPOINTSERVER you require
during installation.
Add a custom action that modifies app.config to
run after "InstallFinalize" with the value of ENDPOINTSERVER
A silent installation will be possible using:
msiexec /i app.msi ENDPOINTSERVER=www.MyServer.com /qb-
try to use below two lines before saving the config file changes:
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("Section Name");
root.Save(configPath);
P.S: it doesn't update the solution item 'app.config', but the '.exe.config' one in the bin/ folder if you run it with F5.

Categories

Resources