Modify app.config <system.diagnostics> section at runtime - c#

I need to modify the <configuration><system.diagnostics> section of an app.config at runtime so that I can:
add a CustomTraceListener under the <sharedListeners> element, which requires special initializeData that can only be ascertained at runtime.
add the CustomTraceListener shared listener to an existing source under the <source><listeners> element.
persist the CustomTraceListener to other assemblies which load their trace source and listener configurations from the config file.
The relevant sections in app.config looks something like this presently:
<system.diagnostics>
<sources>
<source name="mysource" switchName="..." switchType="...">
<listeners>
<add name="console" />
<add name="customtracelistener" /> /// need to add new listener here
</listeners>
</source>
</sources>
<sharedListeners>
<add name="console" type="..." initializeData="..." />
<add name="customtracelistener" type="..." initializeData="..."> /// need to add custom trace listener here
<filter type="..." initializeData="Warning"/> /// with a filter
</add>
</sharedListeners>
<switches>
<add name="..." value="..." />
</switches>
</system.diagnostics>
Using ConfigurationManager I can easily do:
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSection diagnostics = config.GetSection("system.diagnostics");
And when I do this, diagnostics is a System.Diagnostics.SystemDiagnosticsSection type. Interestingly I can't cast diagnostics to a SystemDiagnosticsSection type because I can't find it within any namespace. Regardless, ConfigurationSection doesn't seem to have any methods that I can use to write data into the section.
I also can't cast it to a NameValueConfigurationCollection because diagnostics base type is ConfigurationSection. I heard about this technique but it seems I can't use it.
Do I have to revert to using plain-old XML to accomplish this? I really don't like reinventing the wheel.

You can locate the path to the app.exe.config file through the ConfigurationManager, then load the config file as an XDocument.
string configPath = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath;
XDocument config = XDocument.Load(configPath);
XElement diagnostics = config.Descendants().FirstOrDefault(e => e.Name == "system.diagnostics");
if (diagnostics == default(XElement))
{
/// <system.diagnostics> element was not found in config
}
else
{
/// make changes within <system.diagnostics> here...
}
config.Save(configPath);
Trace.Refresh(); /// reload the trace configuration
Once the required changes are made, save the XDocument back to disk, and call Trace.Refresh() to reload the trace configuration.
See MSDN regarding the Trace.Refresh method here.

For experience i would warn you to make app.config changes from application if the app is deployed with a good install procedure under protected directories, eg. Program files in MS OS with UAC activated.
To update config files sometimes you need some admin privileges.
The Bad thing is than all run correctly under visual studio / debug or some test procedure, after deploy, in production,you may have some issue...

If you make direct changes to the <configuration><system.diagnostics>
section of the app.config file at run time, the app needs to be restarted or Trace.Refresh() must be called to have the changes take effect.
Another option is to programmatically add TraceListeners at application start-up, e.g.
Trace.Listeners.Add(new TextWriterTraceListener("output.log", "myListener"));
see https://msdn.microsoft.com/en-us/library/sk36c28t(v=vs.110).aspx.
To add a filter with the initializeData value as in your question you can use the TraceListener.Filter property
To share settings across applications you can use the configSource property on the <system.diagnostics> element and put that element in a separate config file. The downside of this is that the file needs to be in the same folder as the app.config. So a change to one file would either need to be copied and pasted to other locations or shared in some other way.
An alternative would be to save a custom config file containing the trace listener information at a location all apps can access and then in each app load the file at start-up and configure the trace listeners as above.
Update
To share logging throughout your application you could create a class that implements the singleton pattern to return your TraceSource instance or to wrap your logging activities. This way you don't have to pass round the same instance
public class Logger
{
private static Logger _logger;
private TraceSource _ts;
private Logger()
{
// Configure TraceSource here as required, e.g.
_ts = new TraceSource("StackOverflow", SourceLevels.All);
_ts.Listeners.Add(new TextWriterTraceListener(#"c:\temp\tracefile.log"));
}
public static Logger Get()
{
if (_logger == null)
{
_logger = new Logger();
}
return _logger;
}
public void TraceInfo(string message)
{
_ts.TraceInformation(message);
_ts.Flush();
}
}
// To use
Logger.Get().TraceInfo("This is a trace message");
You could extend this to then encapsulate the actual messages you want to log so that the code doing the logging doesn't know the specifics and you have just one place where your events are defined, e.g.
public void TraceApplicationStarting()
{
_ts.TraceEvent(TraceEventType.Verbose, 1, "Application Starting");
}

Related

Is there a way to dynamically change log name at runtime?

I currently am writing to a statically named log file as defined in my serilog config:
<add key="serilog:write-to:File.path" value="%LOCALAPPDATA%\App_Data\Logs\StaticallyNamedLog.log" />
I'd like to be able to change the logfile name at runtime i.e.
<add key="serilog:write-to:File.path" value="%LOCALAPPDATA%\App_Data\Logs\{appname}-Log.log" />
In log4net for example I'd set a log4net global property value for "appname" in code before the settings were read in.
log4net.globalContext.properties["appname"] = "App1"
log4net.config.xmlConfigurator.configure("log4net.config")
Then in the logfile I'd reference it like so
%property{appname}
This would then get substituted into this path.
How can I do the equivalent in Serilog? I've done a lot of searching but can't find anything on this specific problem.
Thanks

ConfigurationManager.AppSettings Returns Null In Unit Test Project

I have a C# unit test project with application settings in the app.config file. I am testing a class that exists in a different project. That class depends on both, ConfigurationManager.AppSettings and ConfigurationManager.ConnectionStrings.
The project that the class being tested resides in does not have an app.config file. I would have thought that because the class is being instantiated in the context of the unit test project that it would use the unit test project's app.config file. Indeed, that does seem to be the case for the connection string.
The class retrieves the connection string without any issues. However, when the class tries to retrieve any application settings the configuration manager always returns null. What is going on here?
Edit 1
I thought maybe it would be a good idea to try load some settings in the test project to see what happens. I tried to load the setting in the unit test immediately before calling the code that instantiates the class in the external project. Same result, nothing. I guess I can exclude the other project from the equation for the time being.
Here is an excerpt from my config file:
<configSections>
<sectionGroup name="applicationSettings"
type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="MyNamespace.Properties.Settings"
type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
requirePermission="false" />
</sectionGroup>
</configSections>
...
<applicationSettings>
<MyNamespace.Properties.Settings>
<setting name="Bing_Key"
serializeAs="String">
<value>...</value>
</setting>
</MyNamespace.Properties.Settings>
</applicationSettings>
and here is how I am attempting to load the setting:
string test = System.Configuration.ConfigurationManager.AppSettings["Bing_Key"];
Consider refactoring your code that accesses the config to use a wrapper. Then you can write mocks for the wrapper class and not have to deal with the importing of the configuration file for the test.
In a library that is common to both, have something like this:
public interface IConfigurationWrapper {
string GetValue(string key);
bool HasKey(string key);
}
Then, in your libraries that need to access config, inject an instance of this interface type into the class that needs to read config.
public class MyClassOne {
private IConfigurationWrapper _configWrapper;
public MyClassOne(IConfigurationWrapper wrapper) {
_configWrapper = wrapper;
} // end constructor
public void MethodThatDependsOnConfiguration() {
string configValue = "";
if(_configWrapper.HasKey("MySetting")) {
configValue = _configWrapper.GetValue("MySetting");
}
} // end method
} // end class MyClassOne
Then, in one of your libraries, create an implementation that depends on the config file.
public class AppConfigWrapper : IConfigurationWrapper {
public string GetValue(string key) {
return ConfigurationManager.AppSettings[key];
}
public bool HasKey(string key) {
return ConfigurationManager.AppSettings.AllKeys.Select((string x) => x.ToUpperInvariant()).Contains(key.ToUpperInvariant());
}
}
Then, in the code that calls your class.
//Some method container
MyClassOne dataClass = new MyClassOne(new AppConfigWrapper());
dataClass.MethodThatDependsOnConfiguration();
Then in your test, you are free from dependency bondage. :) You can either create a fake version that implements IConfigurationWrapper and pass it in for your test, where you hard-code the return values from the GetValue and HasKey functions, or if you're using a mocking library like Moq:
Mock<IConfigurationWrapper> fakeWrapper = new Mock<IConfigurationWrapper>();
fakeWrapper.Setup((x) => x.GetValue(It.IsAny<string>)).Returns("We just bypassed config.");
MyClassOne testObject = new MyClassOne(fakeWrapper.Object);
testObject.MethodThatDependsOnConfiguration();
Here is an article that covers the concept (albeit, for web forms, but the concepts are the same): http://www.schwammysays.net/how-to-unit-test-code-that-uses-appsettings-from-web-config/
You mentioned settings in the project properties. See if you can access the setting this way:
string test = Properties.Settings.Default.Bing_Key;
You may need to get the executing assembly of where the project settings file is defined, but try this first.
EDIT
When using Visual Studio's project settings file, it adds stuff to your app.config and creates the app.config if it is not present. ConfigurationManager CAN'T touch these settings! You can only get to these specific generated project.settings file from using the above static method. If you want to use ConfigurationManager, you will need to hand write your app.config. Add your settings to it like so:
<appSettings>
<add key="bing_api" value="whatever"/>
</appSettings>
If you're using .NET Core your problem could be a known issue caused by the fact that the test process runs as testhost.dll (or testhost.x86.dll), which means the runtime config file is expected to be named "testhost.dll.config" (or "testhost.x86.dll.config"), instead of the app.config output (ex: "MyLibrary.Tests.dll.config").
To fix it, add the code below to your project file (.csproj, etc) inside of root node <Project>. During build, two copies of app.config will be put in the output directory and named "testhost.dll.config" and "testhost.x86.dll.config", which will get your app settings working again. (You only need 1 of these files but it's safer to include both.)
<Target Name="CopyCustomContent" AfterTargets="AfterBuild">
<Copy SourceFiles="app.config" DestinationFiles="$(OutDir)\testhost.dll.config" />
<Copy SourceFiles="app.config" DestinationFiles="$(OutDir)\testhost.x86.dll.config" />
</Target>
I recommend app.config only as a temporary solution. If you're like me you might have run into the problem while upgrading a .NET Framework project to .NET Core and needed a quick fix. But don't forget to look into the new, more elegant solutions provided by .NET Core for storing app settings.
And then he screamed "NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO".
Cite: I have a C# unit test project with application settings in the app.config file. I am testing a class that exists in a different project. That class depends on both, ConfigurationManager.AppSettings and ConfigurationManager.ConnectionStrings.
You don't do this. EVER!!!! Why? because you have now created a dependency. Instead, use dependency injection so the class can do its work without having to peak into the configuration file that belongs to the application.

How to read web.config section as XML in C#?

This is copied example from:
How to read custom config section in app.config in c#
I want to read following custom section from app.config:
<StartupFolders>
<Folders name="a">
<add folderType="Inst" path="c:\foo" />
<add folderType="Prof" path="C:\foo1" />
</Folders>
<Folders name="b">
<add folderType="Inst" path="c:\foo" />
<add folderType="Prof" path="C:\foo1" />
</Folders>
</StartupFolders>
And this is my case too. However, I don't want to create custom class for handling values, defining this class in web.config, and then finally using it. It is heavy-weight for my needs.
Instead I would like to do something very simple -- retrieve a section as XML. Then I could use regular Linq.Xml to parse it. This way, I don't need to create new classes per each section, I don't need to declare them. For my purpose it is sufficient on one hand, and minimal at the other (I do it once, key-value mapper for nested sections). I.e. perfect.
The only missing piece is (my question) -- how to get a web.config section as XML?
Note about the section:
it cannot be encoded, because it has to be edited by hand
it cannot be serialized for the same reason
So I am not looking for a workaround how to squeeze entire section as value in appSettings, but I am really looking for a method to get proper section as XML.
I would like to get it from ConfigManager (!), because this way I don't have to deal with resolving which web.config should I read, etc. I.e. less chance to make mistake than mimicing web.config precedence manually.
Forgive me for reminding this, but please avoid "answers", you shouldn't do this, use custom class per each section, etc. I already considered this, and opted against it.
I think you either have to do it manually and load the Web config into memory:
XmlDocument doc = new XmlDocument();
doc.Load(Server.MapPath("~/Web.config"));
Or you will need to create the custom configuration sections you want to avoid.
You can define a re-usable custom config section that exposes the section XML as you desire. The key is, that you don't have to define a different class for each custom config section.
For clarity, my project namespace is "ConsoleApp1" as is the assembly name (this appears in type definitions).
First, create a custom config section that exposes the XML reader:
public class XmlConfigSection : ConfigurationSection
{
public XmlReader Xml { get; private set; }
override protected void DeserializeSection(XmlReader reader)
{
Xml = reader;
}
}
You can then define any of your custom sections to use this class in the app.config:
<configSections>
<section name="StartupFolders" type="ConsoleApp1.XmlConfigSection, ConsoleApp1" />
<section name="AnotherCustomSection" type="ConsoleApp1.XmlConfigSection, ConsoleApp1" />
</configSections>
Then in your code, you can access raw XmlReader of the config section like this:
var xmlReader = (ConfigurationManager.GetSection("StartupFolders") as XmlConfigSection).Xml;
If you then want an XML string instead of the reader you can do something like this (though I'd suggest sticking with the XmlReader):
StringBuilder sb = new StringBuilder();
while (xmlReader.Read())
sb.AppendLine(xmlReader.ReadOuterXml());
var xmlStr = sb.ToString();
Totally untested but could you use something like this? :
ConfigurationSection exampleSection =
(ConfigurationSection)ConfigurationManager
.GetSection("system.web/exampleSection");
Then possibly use exampleSection.ElementInformation to get more info?

Using ConfigurationManager's OpenExeConfiguration and GetSection,(Custom config file and section)

Is it possible to retrieve a custom configuration section from a config file other than the app.config or web.config.
I tried using the System.Configuration.ConfigurationManager's OpenExeConfiguration and GetSection method calls together but without luck. My intention is to define custom configuration sections for interchangeable process adapters and contain the custom config section in a separate config file other than app.config and web.config. I see plenty of examples for appsettings and connectionstrings.
static private DigiKeyReadTaskConfigSection digiKeyReadTaskConfigSection;
static DigiKeyReadTaskConfigSection DigiKeyReadTaskConfigSection {
get {
if (digiKeyReadTaskConfigSection == null) {
digiKeyReadTaskConfigSection = (DigiKeyReadTaskConfigSection)ConfigurationManager.OpenExeConfiguration("ReadTask.config").GetSection("DigiKeyReadTaskConfigSection");
}
return digiKeyReadTaskConfigSection;
}
}
The digiKeyReadTaskConfigSection = (DigiKeyReadTaskConfigSection)ConfigurationManager.OpenExeConfiguration call seems to be working however the (DigiKeyReadTaskConfigSection)ConfigurationManager.OpenExeConfiguration("ReadTask.config").GetSection("DigiKeyReadTaskConfigSection") returns null.
The ReadTask.config file lives in the bin file of the App:
<configuration> <configSections>
<section name="DigiKeyReadTaskConfigSection" type="DataReadInc.WebSiteRead.TaskConfigSection.DigiKeyReadTaskConfigSection, DataReadInc.WebSiteRead" />
<section name="ArrowReadTaskConfigSection" type="DataReadInc.WebSiteRead.TaskConfigSection.ArrowReadTaskConfigSection, DataReadInc.WebSiteRead" /> </configSections> <DigiKeyReadTaskConfigSection DigiKeySiteURL="http://search.digikey.com/scripts/DkSearch/dksus.dll?WT.z_header=search_go&lang=en&site=us&keywords="
SiteLogInURL="https://ordering.digikey.com/RegisteredUser/Login.aspx,formName=" SiteLoginId="X" SiteLoginPassword="X" /> <ArrowReadTaskConfigSection ArrowAmericaSiteURL="http://components.arrow.com/part/search/"
SiteLoginURL="http://components.arrow.com/login/processlogin#" SiteLoginId="X" SiteLoginPassword="X" /> </configuration>
I have seen this type of setup with Spring.Net and a J2EE implementation so I am sure it is possible. I can just put my custom config sections in the App.config or web.config file, however it would be significantly cleaner for them to exist in their own config file.
Use ConfigurationManager.OpenMappedExeConfiguration(). OpenExeConfiguration relates to a certain exe.

Sharing application configurations across a solution

I have a solution which has multiple output projects (a website, and admin tool, and a SOAP API layer).
They each share common projects in the solution (the service layer, data layer etc). In one of these common projects, I am looking to store a config layer.
Right now, we have three seperate appsettings config files for each output project -
development.AppSettings.config
testing.AppSettings.config
production.AppSettings.config
So altogether, there are nine config files. Only one is used in each project, as they are referenced by utilising the configSource attribute in the web.config appsettings node.
Anyhoo, it's getting to be a pain any time we want to add/remove values from our config files, because it means that we have to change all nine files to do this. And here's what I'd like to do:
In the common project, we have three config files as above. These would be set to copy to the output directory, so that each project has a copy of them. These would be the 'base' config.
Then in each project, I would like to have three files again, but they wouldn't necessarily have to contain the same values as the base configs. If they did however, then the base config value would be overridden by the value in the output project config. A form of configuration inheritance, I suppose.
On application start, I'd like to be able to get these two config files - the base config, and the project config file. And then set the app settings accordingly.
What I'm wondering though, is what's a nice way of determining which file to use? Also, I'm wondering if this is a good way of sharing application values across a large solution, and if there's another, perhaps more efficient way of doing it?
If I'm in development mode, then I don't want production.appsettings.config, and vice versa if I'm in production mode.
Is there a simple way to get the mode (development/testing/production) that I'm in before I go off and get the configurations?
You can have one set of files (3 configs) and link/share them in whatever projects you need.
http://www.devx.com/vb2themax/Tip/18855
Hope this helps.
You could use the ConfigurationManager.OpenExeConfiguration static method. This will allow you to work with as many config files as you want.
You may also try creating a custom class to store all of your settings. You could then serialize your object to save it as a file. You could extend your base custom config class to fit all your other projects.
After some careful thought, and a trip to the toilet at 03:30, I came across a solution which works.
Let's say that we have some appSettings in our base config file:
<add key="MyKey1" value="MyValue1" />
<add key="MyKey2" value="MyValue2" />
<!-- And so on... -->
<add key="MyKey5" value="MyValue5" />
And in my output project, I have three appSettings:
<!-- This is used to identify which config to use. -->
<add key="Config" value="Development" />
<!-- Different value to the one in the base -->
<add key="MyKey2" value="NewValue2" />
<!-- This key does not exist in the base config -->
<add key="MyKey6" value="MyValue6" />
In my Application_Start, I have a call to GetConfigs():
ConfigHelper.GetConfig(HostingEnvironment.MapPath("~/bin/BaseConfig"));
And the actual GetConfigs function:
public static void GetConfigs()
{
if (configMode == null)
{
configMode = ConfigurationManager.AppSettings.Get("Config").ToLowerInvariant();
}
//Now load the app settings file and retrieve all the config values.
var config = XElement.Load(#"{0}\AppSettings.{1}.config".FormatWith(directory, configMode))
.Elements("add")
.Select(x => new { Key = x.Attribute("key").Value, Value = x.Attribute("value").Value })
//If the current application instance does not contain this key in the config, then add it.
//This way, we create a form of configuration inheritance.
.Where(x => ConfigurationManager.AppSettings.Get(x.Key) == null);
foreach (var configSetting in config)
{
ConfigurationManager.AppSettings.Set(configSetting.Key, configSetting.Value);
}
}
Now, my output project effectively has the following configuration settings:
<add key="Config" value="Development" />
<add key="MyKey1" value="MyValue1" />
<add key="MyKey2" value="NewValue2" />
<!-- And so on... -->
<add key="MyKey5" value="MyValue5" />
<add key="MyKey6" value="MyValue6" />
Simples!

Categories

Resources