#region Apache License
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to you under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion
using System;
using System.Xml;
using System.Collections;
using System.IO;
#if !NETCF && !NETSTANDARD1_3
using System.Reflection;
#endif
using System.Threading;
using System.Net;
using log4net.Util;
using log4net.Repository;
namespace log4net.Config
{
///
/// Use this class to initialize the log4net environment using an Xml tree.
///
///
///
/// Configures a using an Xml tree.
///
///
/// Nicko Cadell
/// Gert Driesen
public sealed class XmlConfigurator
{
#region Private Instance Constructors
///
/// Private constructor
///
private XmlConfigurator()
{
}
#endregion Protected Instance Constructors
#region Configure static methods
#if !NETCF
///
/// Automatically configures the using settings
/// stored in the application's configuration file.
///
///
///
/// Each application has a configuration file. This has the
/// same name as the application with '.config' appended.
/// This file is XML and calling this function prompts the
/// configurator to look in that file for a section called
/// log4net that contains the configuration data.
///
///
/// To use this method to configure log4net you must specify
/// the section
/// handler for the log4net configuration section. See the
/// for an example.
///
///
/// The repository to configure.
#else
///
/// Automatically configures the using settings
/// stored in the application's configuration file.
///
///
///
/// Each application has a configuration file. This has the
/// same name as the application with '.config' appended.
/// This file is XML and calling this function prompts the
/// configurator to look in that file for a section called
/// log4net that contains the configuration data.
///
///
/// The repository to configure.
#endif
public static ICollection Configure(ILoggerRepository repository)
{
ArrayList configurationMessages = new ArrayList();
using (new LogLog.LogReceivedAdapter(configurationMessages))
{
InternalConfigure(repository);
}
repository.ConfigurationMessages = configurationMessages;
return configurationMessages;
}
private static void InternalConfigure(ILoggerRepository repository)
{
LogLog.Debug(declaringType, "configuring repository [" + repository.Name + "] using .config file section");
try
{
LogLog.Debug(declaringType, "Application config file is [" + SystemInfo.ConfigurationFileLocation + "]");
}
catch
{
// ignore error
LogLog.Debug(declaringType, "Application config file location unknown");
}
#if NETCF || NETSTANDARD1_3
// No config file reading stuff. Just go straight for the file
Configure(repository, new FileInfo(SystemInfo.ConfigurationFileLocation));
#else
try
{
XmlElement configElement = System.Configuration.ConfigurationManager.GetSection("log4net") as XmlElement;
if (configElement == null)
{
// Failed to load the xml config using configuration settings handler
LogLog.Error(declaringType, "Failed to find configuration section 'log4net' in the application's .config file. Check your .config file for the and elements. The configuration section should look like: ");
}
else
{
// Configure using the xml loaded from the config file
InternalConfigureFromXml(repository, configElement);
}
}
catch(System.Configuration.ConfigurationException confEx)
{
if (confEx.BareMessage.IndexOf("Unrecognized element") >= 0)
{
// Looks like the XML file is not valid
LogLog.Error(declaringType, "Failed to parse config file. Check your .config file is well formed XML.", confEx);
}
else
{
// This exception is typically due to the assembly name not being correctly specified in the section type.
string configSectionStr = "";
LogLog.Error(declaringType, "Failed to parse config file. Is the specified as: " + configSectionStr, confEx);
}
}
#endif
}
#if !NETSTANDARD1_3 // Excluded because GetCallingAssembly() is not available in CoreFX (https://github.com/dotnet/corefx/issues/2221).
#if !NETCF
///
/// Automatically configures the log4net system based on the
/// application's configuration settings.
///
///
///
/// Each application has a configuration file. This has the
/// same name as the application with '.config' appended.
/// This file is XML and calling this function prompts the
/// configurator to look in that file for a section called
/// log4net that contains the configuration data.
///
///
/// To use this method to configure log4net you must specify
/// the section
/// handler for the log4net configuration section. See the
/// for an example.
///
///
///
#else
///
/// Automatically configures the log4net system based on the
/// application's configuration settings.
///
///
///
/// Each application has a configuration file. This has the
/// same name as the application with '.config' appended.
/// This file is XML and calling this function prompts the
/// configurator to look in that file for a section called
/// log4net that contains the configuration data.
///
///
#endif
public static ICollection Configure()
{
return Configure(LogManager.GetRepository(Assembly.GetCallingAssembly()));
}
///
/// Configures log4net using a log4net element
///
///
///
/// Loads the log4net configuration from the XML element
/// supplied as .
///
///
/// The element to parse.
public static ICollection Configure(XmlElement element)
{
ArrayList configurationMessages = new ArrayList();
ILoggerRepository repository = LogManager.GetRepository(Assembly.GetCallingAssembly());
using (new LogLog.LogReceivedAdapter(configurationMessages))
{
InternalConfigureFromXml(repository, element);
}
repository.ConfigurationMessages = configurationMessages;
return configurationMessages;
}
#if !NETCF
///
/// Configures log4net using the specified configuration file.
///
/// The XML file to load the configuration from.
///
///
/// The configuration file must be valid XML. It must contain
/// at least one element called log4net that holds
/// the log4net configuration data.
///
///
/// The log4net configuration file can possible be specified in the application's
/// configuration file (either MyAppName.exe.config for a
/// normal application on Web.config for an ASP.NET application).
///
///
/// The first element matching <configuration> will be read as the
/// configuration. If this file is also a .NET .config file then you must specify
/// a configuration section for the log4net element otherwise .NET will
/// complain. Set the type for the section handler to , for example:
///
///
///
///
///
///
///
/// The following example configures log4net using a configuration file, of which the
/// location is stored in the application's configuration file :
///
///
/// using log4net.Config;
/// using System.IO;
/// using System.Configuration;
///
/// ...
///
/// XmlConfigurator.Configure(new FileInfo(ConfigurationSettings.AppSettings["log4net-config-file"]));
///
///
/// In the .config file, the path to the log4net can be specified like this :
///
///
///
///
///
///
///
///
///
#else
///
/// Configures log4net using the specified configuration file.
///
/// The XML file to load the configuration from.
///
///
/// The configuration file must be valid XML. It must contain
/// at least one element called log4net that holds
/// the log4net configuration data.
///
///
/// The following example configures log4net using a configuration file, of which the
/// location is stored in the application's configuration file :
///
///
/// using log4net.Config;
/// using System.IO;
/// using System.Configuration;
///
/// ...
///
/// XmlConfigurator.Configure(new FileInfo(ConfigurationSettings.AppSettings["log4net-config-file"]));
///
///
/// In the .config file, the path to the log4net can be specified like this :
///
///
///
///
///
///
///
///
///
#endif
public static ICollection Configure(FileInfo configFile)
{
ArrayList configurationMessages = new ArrayList();
using (new LogLog.LogReceivedAdapter(configurationMessages))
{
InternalConfigure(LogManager.GetRepository(Assembly.GetCallingAssembly()), configFile);
}
return configurationMessages;
}
///
/// Configures log4net using the specified configuration URI.
///
/// A URI to load the XML configuration from.
///
///
/// The configuration data must be valid XML. It must contain
/// at least one element called log4net that holds
/// the log4net configuration data.
///
///
/// The must support the URI scheme specified.
///
///
public static ICollection Configure(Uri configUri)
{
ArrayList configurationMessages = new ArrayList();
ILoggerRepository repository = LogManager.GetRepository(Assembly.GetCallingAssembly());
using (new LogLog.LogReceivedAdapter(configurationMessages))
{
InternalConfigure(repository, configUri);
}
repository.ConfigurationMessages = configurationMessages;
return configurationMessages;
}
///
/// Configures log4net using the specified configuration data stream.
///
/// A stream to load the XML configuration from.
///
///
/// The configuration data must be valid XML. It must contain
/// at least one element called log4net that holds
/// the log4net configuration data.
///
///
/// Note that this method will NOT close the stream parameter.
///
///
public static ICollection Configure(Stream configStream)
{
ArrayList configurationMessages = new ArrayList();
ILoggerRepository repository = LogManager.GetRepository(Assembly.GetCallingAssembly());
using (new LogLog.LogReceivedAdapter(configurationMessages))
{
InternalConfigure(repository, configStream);
}
repository.ConfigurationMessages = configurationMessages;
return configurationMessages;
}
#endif // !NETSTANDARD1_3
///
/// Configures the using the specified XML
/// element.
///
///
/// Loads the log4net configuration from the XML element
/// supplied as .
///
/// The repository to configure.
/// The element to parse.
public static ICollection Configure(ILoggerRepository repository, XmlElement element)
{
ArrayList configurationMessages = new ArrayList();
using (new LogLog.LogReceivedAdapter(configurationMessages))
{
LogLog.Debug(declaringType, "configuring repository [" + repository.Name + "] using XML element");
InternalConfigureFromXml(repository, element);
}
repository.ConfigurationMessages = configurationMessages;
return configurationMessages;
}
#if !NETCF
///
/// Configures the using the specified configuration
/// file.
///
/// The repository to configure.
/// The XML file to load the configuration from.
///
///
/// The configuration file must be valid XML. It must contain
/// at least one element called log4net that holds
/// the configuration data.
///
///
/// The log4net configuration file can possible be specified in the application's
/// configuration file (either MyAppName.exe.config for a
/// normal application on Web.config for an ASP.NET application).
///
///
/// The first element matching <configuration> will be read as the
/// configuration. If this file is also a .NET .config file then you must specify
/// a configuration section for the log4net element otherwise .NET will
/// complain. Set the type for the section handler to , for example:
///
///
///
///
///
///
///
/// The following example configures log4net using a configuration file, of which the
/// location is stored in the application's configuration file :
///
///
/// using log4net.Config;
/// using System.IO;
/// using System.Configuration;
///
/// ...
///
/// XmlConfigurator.Configure(new FileInfo(ConfigurationSettings.AppSettings["log4net-config-file"]));
///
///
/// In the .config file, the path to the log4net can be specified like this :
///
///
///
///
///
///
///
///
///
#else
///
/// Configures the using the specified configuration
/// file.
///
/// The repository to configure.
/// The XML file to load the configuration from.
///
///
/// The configuration file must be valid XML. It must contain
/// at least one element called log4net that holds
/// the configuration data.
///
///
/// The following example configures log4net using a configuration file, of which the
/// location is stored in the application's configuration file :
///
///
/// using log4net.Config;
/// using System.IO;
/// using System.Configuration;
///
/// ...
///
/// XmlConfigurator.Configure(new FileInfo(ConfigurationSettings.AppSettings["log4net-config-file"]));
///
///
/// In the .config file, the path to the log4net can be specified like this :
///
///
///
///
///
///
///
///
///
#endif
public static ICollection Configure(ILoggerRepository repository, FileInfo configFile)
{
ArrayList configurationMessages = new ArrayList();
using (new LogLog.LogReceivedAdapter(configurationMessages))
{
InternalConfigure(repository, configFile);
}
repository.ConfigurationMessages = configurationMessages;
return configurationMessages;
}
private static void InternalConfigure(ILoggerRepository repository, FileInfo configFile)
{
LogLog.Debug(declaringType, "configuring repository [" + repository.Name + "] using file [" + configFile + "]");
if (configFile == null)
{
LogLog.Error(declaringType, "Configure called with null 'configFile' parameter");
}
else
{
// Have to use File.Exists() rather than configFile.Exists()
// because configFile.Exists() caches the value, not what we want.
if (File.Exists(configFile.FullName))
{
// Open the file for reading
FileStream fs = null;
// Try hard to open the file
for(int retry = 5; --retry >= 0; )
{
try
{
fs = configFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
break;
}
catch(IOException ex)
{
if (retry == 0)
{
LogLog.Error(declaringType, "Failed to open XML config file [" + configFile.Name + "]", ex);
// The stream cannot be valid
fs = null;
}
System.Threading.Thread.Sleep(250);
}
}
if (fs != null)
{
try
{
// Load the configuration from the stream
InternalConfigure(repository, fs);
}
finally
{
// Force the file closed whatever happens
fs.Dispose();
}
}
}
else
{
LogLog.Debug(declaringType, "config file [" + configFile.FullName + "] not found. Configuration unchanged.");
}
}
}
///
/// Configures the using the specified configuration
/// URI.
///
/// The repository to configure.
/// A URI to load the XML configuration from.
///
///
/// The configuration data must be valid XML. It must contain
/// at least one element called log4net that holds
/// the configuration data.
///
///
/// The must support the URI scheme specified.
///
///
public static ICollection Configure(ILoggerRepository repository, Uri configUri)
{
ArrayList configurationMessages = new ArrayList();
using (new LogLog.LogReceivedAdapter(configurationMessages))
{
InternalConfigure(repository, configUri);
}
repository.ConfigurationMessages = configurationMessages;
return configurationMessages;
}
private static void InternalConfigure(ILoggerRepository repository, Uri configUri)
{
LogLog.Debug(declaringType, "configuring repository [" + repository.Name + "] using URI ["+configUri+"]");
if (configUri == null)
{
LogLog.Error(declaringType, "Configure called with null 'configUri' parameter");
}
else
{
if (configUri.IsFile)
{
// If URI is local file then call Configure with FileInfo
InternalConfigure(repository, new FileInfo(configUri.LocalPath));
}
else
{
// NETCF dose not support WebClient
WebRequest configRequest = null;
try
{
configRequest = WebRequest.Create(configUri);
}
catch(Exception ex)
{
LogLog.Error(declaringType, "Failed to create WebRequest for URI ["+configUri+"]", ex);
}
if (configRequest != null)
{
#if !NETCF_1_0
// authentication may be required, set client to use default credentials
try
{
configRequest.Credentials = CredentialCache.DefaultCredentials;
}
catch
{
// ignore security exception
}
#endif
try
{
#if NETSTANDARD
using WebResponse response = configRequest.GetResponseAsync().GetAwaiter().GetResult();
#else
using WebResponse response = configRequest.GetResponse();
#endif
if (response != null)
{
using var configStream = response.GetResponseStream();
InternalConfigure(repository, configStream);
}
}
catch(Exception ex)
{
LogLog.Error(declaringType, "Failed to request config from URI ["+configUri+"]", ex);
}
}
}
}
}
///
/// Configures the using the specified configuration
/// file.
///
/// The repository to configure.
/// The stream to load the XML configuration from.
///
///
/// The configuration data must be valid XML. It must contain
/// at least one element called log4net that holds
/// the configuration data.
///
///
/// Note that this method will NOT close the stream parameter.
///
///
public static ICollection Configure(ILoggerRepository repository, Stream configStream)
{
ArrayList configurationMessages = new ArrayList();
using (new LogLog.LogReceivedAdapter(configurationMessages))
{
InternalConfigure(repository, configStream);
}
repository.ConfigurationMessages = configurationMessages;
return configurationMessages;
}
private static void InternalConfigure(ILoggerRepository repository, Stream configStream)
{
LogLog.Debug(declaringType, "configuring repository [" + repository.Name + "] using stream");
if (configStream == null)
{
LogLog.Error(declaringType, "Configure called with null 'configStream' parameter");
}
else
{
// Load the config file into a document
#if NETSTANDARD1_3
XmlDocument doc = new XmlDocument();
#else
XmlDocument doc = new XmlDocument { XmlResolver = null };
#endif
try
{
#if (NETCF)
// Create a text reader for the file stream
XmlTextReader xmlReader = new XmlTextReader(configStream);
#elif NET_2_0 || NETSTANDARD
// Allow the DTD to specify entity includes
XmlReaderSettings settings = new XmlReaderSettings();
// .NET 4.0 warning CS0618: 'System.Xml.XmlReaderSettings.ProhibitDtd'
// is obsolete: 'Use XmlReaderSettings.DtdProcessing property instead.'
#if NETSTANDARD1_3 // TODO DtdProcessing.Parse not yet available (https://github.com/dotnet/corefx/issues/4376)
settings.DtdProcessing = DtdProcessing.Ignore;
#elif !NET_4_0 && !MONO_4_0 && !NETSTANDARD2_0
settings.ProhibitDtd = true;
#else
settings.DtdProcessing = DtdProcessing.Ignore;
#endif
// Create a reader over the input stream
using XmlReader xmlReader = XmlReader.Create(configStream, settings);
#else
// Create a validating reader around a text reader for the file stream
using XmlValidatingReader xmlReader = new XmlValidatingReader(new XmlTextReader(configStream));
// Specify that the reader should not perform validation, but that it should
// expand entity refs.
xmlReader.ValidationType = ValidationType.None;
xmlReader.EntityHandling = EntityHandling.ExpandEntities;
#endif
// load the data into the document
doc.Load(xmlReader);
}
catch(Exception ex)
{
LogLog.Error(declaringType, "Error while loading XML configuration", ex);
// The document is invalid
doc = null;
}
if (doc != null)
{
LogLog.Debug(declaringType, "loading XML configuration");
// Configure using the 'log4net' element
XmlNodeList configNodeList = doc.GetElementsByTagName("log4net");
if (configNodeList.Count == 0)
{
LogLog.Debug(declaringType, "XML configuration does not contain a element. Configuration Aborted.");
}
else if (configNodeList.Count > 1)
{
LogLog.Error(declaringType, "XML configuration contains [" + configNodeList.Count + "] elements. Only one is allowed. Configuration Aborted.");
}
else
{
InternalConfigureFromXml(repository, configNodeList[0] as XmlElement);
}
}
}
}
#endregion Configure static methods
#region ConfigureAndWatch static methods
#if (!NETCF && !SSCLI)
#if !NETSTANDARD1_3 // Excluded because GetCallingAssembly() is not available in CoreFX (https://github.com/dotnet/corefx/issues/2221).
///
/// Configures log4net using the file specified, monitors the file for changes
/// and reloads the configuration if a change is detected.
///
/// The XML file to load the configuration from.
///
///
/// The configuration file must be valid XML. It must contain
/// at least one element called log4net that holds
/// the configuration data.
///
///
/// The configuration file will be monitored using a
/// and depends on the behavior of that class.
///
///
/// For more information on how to configure log4net using
/// a separate configuration file, see .
///
///
///
public static ICollection ConfigureAndWatch(FileInfo configFile)
{
ArrayList configurationMessages = new ArrayList();
ILoggerRepository repository = LogManager.GetRepository(Assembly.GetCallingAssembly());
using (new LogLog.LogReceivedAdapter(configurationMessages))
{
InternalConfigureAndWatch(repository, configFile);
}
repository.ConfigurationMessages = configurationMessages;
return configurationMessages;
}
#endif // !NETSTANDARD1_3
///
/// Configures the using the file specified,
/// monitors the file for changes and reloads the configuration if a change
/// is detected.
///
/// The repository to configure.
/// The XML file to load the configuration from.
///
///
/// The configuration file must be valid XML. It must contain
/// at least one element called log4net that holds
/// the configuration data.
///
///
/// The configuration file will be monitored using a
/// and depends on the behavior of that class.
///
///
/// For more information on how to configure log4net using
/// a separate configuration file, see .
///
///
///
public static ICollection ConfigureAndWatch(ILoggerRepository repository, FileInfo configFile)
{
ArrayList configurationMessages = new ArrayList();
using (new LogLog.LogReceivedAdapter(configurationMessages))
{
InternalConfigureAndWatch(repository, configFile);
}
repository.ConfigurationMessages = configurationMessages;
return configurationMessages;
}
private static void InternalConfigureAndWatch(ILoggerRepository repository, FileInfo configFile)
{
LogLog.Debug(declaringType, "configuring repository [" + repository.Name + "] using file [" + configFile + "] watching for file updates");
if (configFile == null)
{
LogLog.Error(declaringType, "ConfigureAndWatch called with null 'configFile' parameter");
}
else
{
// Configure log4net now
InternalConfigure(repository, configFile);
try
{
lock (m_repositoryName2ConfigAndWatchHandler)
{
// support multiple repositories each having their own watcher
ConfigureAndWatchHandler handler =
(ConfigureAndWatchHandler)m_repositoryName2ConfigAndWatchHandler[configFile.FullName];
if (handler != null)
{
m_repositoryName2ConfigAndWatchHandler.Remove(configFile.FullName);
handler.Dispose();
}
// Create and start a watch handler that will reload the
// configuration whenever the config file is modified.
handler = new ConfigureAndWatchHandler(repository, configFile);
m_repositoryName2ConfigAndWatchHandler[configFile.FullName] = handler;
}
}
catch(Exception ex)
{
LogLog.Error(declaringType, "Failed to initialize configuration file watcher for file ["+configFile.FullName+"]", ex);
}
}
}
#endif
#endregion ConfigureAndWatch static methods
#region ConfigureAndWatchHandler
#if (!NETCF && !SSCLI)
///
/// Class used to watch config files.
///
///
///
/// Uses the to monitor
/// changes to a specified file. Because multiple change notifications
/// may be raised when the file is modified, a timer is used to
/// compress the notifications into a single event. The timer
/// waits for time before delivering
/// the event notification. If any further
/// change notifications arrive while the timer is waiting it
/// is reset and waits again for to
/// elapse.
///
///
private sealed class ConfigureAndWatchHandler : IDisposable
{
///
/// Holds the FileInfo used to configure the XmlConfigurator
///
private FileInfo m_configFile;
///
/// Holds the repository being configured.
///
private ILoggerRepository m_repository;
///
/// The timer used to compress the notification events.
///
private Timer m_timer;
///
/// The default amount of time to wait after receiving notification
/// before reloading the config file.
///
private const int TimeoutMillis = 500;
///
/// Watches file for changes. This object should be disposed when no longer
/// needed to free system handles on the watched resources.
///
private FileSystemWatcher m_watcher;
///
/// Initializes a new instance of the class to
/// watch a specified config file used to configure a repository.
///
/// The repository to configure.
/// The configuration file to watch.
///
///
/// Initializes a new instance of the class.
///
///
#if NET_4_0 || MONO_4_0 || NETSTANDARD
[System.Security.SecuritySafeCritical]
#endif
public ConfigureAndWatchHandler(ILoggerRepository repository, FileInfo configFile)
{
m_repository = repository;
m_configFile = configFile;
// Create a new FileSystemWatcher and set its properties.
m_watcher = new FileSystemWatcher();
m_watcher.Path = m_configFile.DirectoryName;
m_watcher.Filter = m_configFile.Name;
// Set the notification filters
m_watcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.FileName;
// Add event handlers. OnChanged will do for all event handlers that fire a FileSystemEventArgs
m_watcher.Changed += new FileSystemEventHandler(ConfigureAndWatchHandler_OnChanged);
m_watcher.Created += new FileSystemEventHandler(ConfigureAndWatchHandler_OnChanged);
m_watcher.Deleted += new FileSystemEventHandler(ConfigureAndWatchHandler_OnChanged);
m_watcher.Renamed += new RenamedEventHandler(ConfigureAndWatchHandler_OnRenamed);
// Begin watching.
m_watcher.EnableRaisingEvents = true;
// Create the timer that will be used to deliver events. Set as disabled
m_timer = new Timer(new TimerCallback(OnWatchedFileChange), null, Timeout.Infinite, Timeout.Infinite);
}
///
/// Event handler used by .
///
/// The firing the event.
/// The argument indicates the file that caused the event to be fired.
///
///
/// This handler reloads the configuration from the file when the event is fired.
///
///
private void ConfigureAndWatchHandler_OnChanged(object source, FileSystemEventArgs e)
{
LogLog.Debug(declaringType, "ConfigureAndWatchHandler: "+e.ChangeType+" [" + m_configFile.FullName + "]");
// Deliver the event in TimeoutMillis time
// timer will fire only once
m_timer.Change(TimeoutMillis, Timeout.Infinite);
}
///
/// Event handler used by .
///
/// The firing the event.
/// The argument indicates the file that caused the event to be fired.
///
///
/// This handler reloads the configuration from the file when the event is fired.
///
///
private void ConfigureAndWatchHandler_OnRenamed(object source, RenamedEventArgs e)
{
LogLog.Debug(declaringType, "ConfigureAndWatchHandler: " + e.ChangeType + " [" + m_configFile.FullName + "]");
// Deliver the event in TimeoutMillis time
// timer will fire only once
m_timer.Change(TimeoutMillis, Timeout.Infinite);
}
///
/// Called by the timer when the configuration has been updated.
///
/// null
private void OnWatchedFileChange(object state)
{
XmlConfigurator.InternalConfigure(m_repository, m_configFile);
}
///
/// Release the handles held by the watcher and timer.
///
#if NET_4_0 || MONO_4_0 || NETSTANDARD
[System.Security.SecuritySafeCritical]
#endif
public void Dispose()
{
m_watcher.EnableRaisingEvents = false;
m_watcher.Dispose();
m_timer.Dispose();
}
}
#endif
#endregion ConfigureAndWatchHandler
#region Private Static Methods
///
/// Configures the specified repository using a log4net element.
///
/// The hierarchy to configure.
/// The element to parse.
///
///
/// Loads the log4net configuration from the XML element
/// supplied as .
///
///
/// This method is ultimately called by one of the Configure methods
/// to load the configuration from an .
///
///
private static void InternalConfigureFromXml(ILoggerRepository repository, XmlElement element)
{
if (element == null)
{
LogLog.Error(declaringType, "ConfigureFromXml called with null 'element' parameter");
}
else if (repository == null)
{
LogLog.Error(declaringType, "ConfigureFromXml called with null 'repository' parameter");
}
else
{
LogLog.Debug(declaringType, "Configuring Repository [" + repository.Name + "]");
IXmlRepositoryConfigurator configurableRepository = repository as IXmlRepositoryConfigurator;
if (configurableRepository == null)
{
LogLog.Warn(declaringType, "Repository [" + repository + "] does not support the XmlConfigurator");
}
else
{
// Copy the xml data into the root of a new document
// this isolates the xml config data from the rest of
// the document
#if NETSTANDARD1_3
XmlDocument newDoc = new XmlDocument();
#else
XmlDocument newDoc = new XmlDocument { XmlResolver = null };
#endif
XmlElement newElement = (XmlElement)newDoc.AppendChild(newDoc.ImportNode(element, true));
// Pass the configurator the config element
configurableRepository.Configure(newElement);
}
}
}
#endregion Private Static Methods
#region Private Static Fields
///
/// Maps repository names to ConfigAndWatchHandler instances to allow a particular
/// ConfigAndWatchHandler to dispose of its FileSystemWatcher when a repository is
/// reconfigured.
///
private static readonly Hashtable m_repositoryName2ConfigAndWatchHandler = new Hashtable();
///
/// The fully qualified type of the XmlConfigurator class.
///
///
/// Used by the internal logger to record the Type of the
/// log message.
///
private static readonly Type declaringType = typeof(XmlConfigurator);
#endregion Private Static Fields
}
}