12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139 |
- #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;
- using System.Reflection;
- using System.Threading;
- using System.Net;
- using log4net.Appender;
- using log4net.Util;
- using log4net.Repository;
- namespace log4net.Config
- {
- /// <summary>
- /// Use this class to initialize the log4net environment using an Xml tree.
- /// </summary>
- /// <remarks>
- /// <para>
- /// Configures a <see cref="ILoggerRepository"/> using an Xml tree.
- /// </para>
- /// </remarks>
- /// <author>Nicko Cadell</author>
- /// <author>Gert Driesen</author>
- public sealed class XmlConfigurator
- {
- #region Private Instance Constructors
- /// <summary>
- /// Private constructor
- /// </summary>
- private XmlConfigurator()
- {
- }
- #endregion Protected Instance Constructors
- #region Configure static methods
- #if !NETCF
- /// <summary>
- /// Automatically configures the <see cref="ILoggerRepository"/> using settings
- /// stored in the application's configuration file.
- /// </summary>
- /// <remarks>
- /// <para>
- /// 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
- /// <c>log4net</c> that contains the configuration data.
- /// </para>
- /// <para>
- /// To use this method to configure log4net you must specify
- /// the <see cref="Log4NetConfigurationSectionHandler"/> section
- /// handler for the <c>log4net</c> configuration section. See the
- /// <see cref="Log4NetConfigurationSectionHandler"/> for an example.
- /// </para>
- /// </remarks>
- /// <param name="repository">The repository to configure.</param>
- #else
- /// <summary>
- /// Automatically configures the <see cref="ILoggerRepository"/> using settings
- /// stored in the application's configuration file.
- /// </summary>
- /// <remarks>
- /// <para>
- /// 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
- /// <c>log4net</c> that contains the configuration data.
- /// </para>
- /// </remarks>
- /// <param name="repository">The repository to configure.</param>
- #endif
- static public ICollection Configure(ILoggerRepository repository)
- {
- ArrayList configurationMessages = new ArrayList();
- using (new LogLog.LogReceivedAdapter(configurationMessages))
- {
- InternalConfigure(repository);
- }
- repository.ConfigurationMessages = configurationMessages;
- return configurationMessages;
- }
- static private 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 = null;
- #if NET_2_0
- configElement = System.Configuration.ConfigurationManager.GetSection("log4net") as XmlElement;
- #else
- configElement = System.Configuration.ConfigurationSettings.GetConfig("log4net") as XmlElement;
- #endif
- 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 <log4net> and <configSections> elements. The configuration section should look like: <section name=\"log4net\" type=\"log4net.Config.Log4NetConfigurationSectionHandler,log4net\" />");
- }
- 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 = "<section name=\"log4net\" type=\"log4net.Config.Log4NetConfigurationSectionHandler," + Assembly.GetExecutingAssembly().FullName + "\" />";
- LogLog.Error(declaringType, "Failed to parse config file. Is the <configSections> 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
- /// <summary>
- /// Automatically configures the log4net system based on the
- /// application's configuration settings.
- /// </summary>
- /// <remarks>
- /// <para>
- /// 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
- /// <c>log4net</c> that contains the configuration data.
- /// </para>
- /// <para>
- /// To use this method to configure log4net you must specify
- /// the <see cref="Log4NetConfigurationSectionHandler"/> section
- /// handler for the <c>log4net</c> configuration section. See the
- /// <see cref="Log4NetConfigurationSectionHandler"/> for an example.
- /// </para>
- /// </remarks>
- /// <seealso cref="Log4NetConfigurationSectionHandler"/>
- #else
- /// <summary>
- /// Automatically configures the log4net system based on the
- /// application's configuration settings.
- /// </summary>
- /// <remarks>
- /// <para>
- /// 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
- /// <c>log4net</c> that contains the configuration data.
- /// </para>
- /// </remarks>
- #endif
- static public ICollection Configure()
- {
- return Configure(LogManager.GetRepository(Assembly.GetCallingAssembly()));
- }
- /// <summary>
- /// Configures log4net using a <c>log4net</c> element
- /// </summary>
- /// <remarks>
- /// <para>
- /// Loads the log4net configuration from the XML element
- /// supplied as <paramref name="element"/>.
- /// </para>
- /// </remarks>
- /// <param name="element">The element to parse.</param>
- static public 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
- /// <summary>
- /// Configures log4net using the specified configuration file.
- /// </summary>
- /// <param name="configFile">The XML file to load the configuration from.</param>
- /// <remarks>
- /// <para>
- /// The configuration file must be valid XML. It must contain
- /// at least one element called <c>log4net</c> that holds
- /// the log4net configuration data.
- /// </para>
- /// <para>
- /// The log4net configuration file can possible be specified in the application's
- /// configuration file (either <c>MyAppName.exe.config</c> for a
- /// normal application on <c>Web.config</c> for an ASP.NET application).
- /// </para>
- /// <para>
- /// The first element matching <c><configuration></c> will be read as the
- /// configuration. If this file is also a .NET .config file then you must specify
- /// a configuration section for the <c>log4net</c> element otherwise .NET will
- /// complain. Set the type for the section handler to <see cref="System.Configuration.IgnoreSectionHandler"/>, for example:
- /// <code lang="XML" escaped="true">
- /// <configSections>
- /// <section name="log4net" type="System.Configuration.IgnoreSectionHandler" />
- /// </configSections>
- /// </code>
- /// </para>
- /// <example>
- /// The following example configures log4net using a configuration file, of which the
- /// location is stored in the application's configuration file :
- /// </example>
- /// <code lang="C#">
- /// using log4net.Config;
- /// using System.IO;
- /// using System.Configuration;
- ///
- /// ...
- ///
- /// XmlConfigurator.Configure(new FileInfo(ConfigurationSettings.AppSettings["log4net-config-file"]));
- /// </code>
- /// <para>
- /// In the <c>.config</c> file, the path to the log4net can be specified like this :
- /// </para>
- /// <code lang="XML" escaped="true">
- /// <configuration>
- /// <appSettings>
- /// <add key="log4net-config-file" value="log.config"/>
- /// </appSettings>
- /// </configuration>
- /// </code>
- /// </remarks>
- #else
- /// <summary>
- /// Configures log4net using the specified configuration file.
- /// </summary>
- /// <param name="configFile">The XML file to load the configuration from.</param>
- /// <remarks>
- /// <para>
- /// The configuration file must be valid XML. It must contain
- /// at least one element called <c>log4net</c> that holds
- /// the log4net configuration data.
- /// </para>
- /// <example>
- /// The following example configures log4net using a configuration file, of which the
- /// location is stored in the application's configuration file :
- /// </example>
- /// <code lang="C#">
- /// using log4net.Config;
- /// using System.IO;
- /// using System.Configuration;
- ///
- /// ...
- ///
- /// XmlConfigurator.Configure(new FileInfo(ConfigurationSettings.AppSettings["log4net-config-file"]));
- /// </code>
- /// <para>
- /// In the <c>.config</c> file, the path to the log4net can be specified like this :
- /// </para>
- /// <code lang="XML" escaped="true">
- /// <configuration>
- /// <appSettings>
- /// <add key="log4net-config-file" value="log.config"/>
- /// </appSettings>
- /// </configuration>
- /// </code>
- /// </remarks>
- #endif
- static public ICollection Configure(FileInfo configFile)
- {
- ArrayList configurationMessages = new ArrayList();
- using (new LogLog.LogReceivedAdapter(configurationMessages))
- {
- InternalConfigure(LogManager.GetRepository(Assembly.GetCallingAssembly()), configFile);
- }
- return configurationMessages;
- }
- /// <summary>
- /// Configures log4net using the specified configuration URI.
- /// </summary>
- /// <param name="configUri">A URI to load the XML configuration from.</param>
- /// <remarks>
- /// <para>
- /// The configuration data must be valid XML. It must contain
- /// at least one element called <c>log4net</c> that holds
- /// the log4net configuration data.
- /// </para>
- /// <para>
- /// The <see cref="System.Net.WebRequest"/> must support the URI scheme specified.
- /// </para>
- /// </remarks>
- static public 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;
- }
- /// <summary>
- /// Configures log4net using the specified configuration data stream.
- /// </summary>
- /// <param name="configStream">A stream to load the XML configuration from.</param>
- /// <remarks>
- /// <para>
- /// The configuration data must be valid XML. It must contain
- /// at least one element called <c>log4net</c> that holds
- /// the log4net configuration data.
- /// </para>
- /// <para>
- /// Note that this method will NOT close the stream parameter.
- /// </para>
- /// </remarks>
- static public 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
- /// <summary>
- /// Configures the <see cref="ILoggerRepository"/> using the specified XML
- /// element.
- /// </summary>
- /// <remarks>
- /// Loads the log4net configuration from the XML element
- /// supplied as <paramref name="element"/>.
- /// </remarks>
- /// <param name="repository">The repository to configure.</param>
- /// <param name="element">The element to parse.</param>
- static public 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
- /// <summary>
- /// Configures the <see cref="ILoggerRepository"/> using the specified configuration
- /// file.
- /// </summary>
- /// <param name="repository">The repository to configure.</param>
- /// <param name="configFile">The XML file to load the configuration from.</param>
- /// <remarks>
- /// <para>
- /// The configuration file must be valid XML. It must contain
- /// at least one element called <c>log4net</c> that holds
- /// the configuration data.
- /// </para>
- /// <para>
- /// The log4net configuration file can possible be specified in the application's
- /// configuration file (either <c>MyAppName.exe.config</c> for a
- /// normal application on <c>Web.config</c> for an ASP.NET application).
- /// </para>
- /// <para>
- /// The first element matching <c><configuration></c> will be read as the
- /// configuration. If this file is also a .NET .config file then you must specify
- /// a configuration section for the <c>log4net</c> element otherwise .NET will
- /// complain. Set the type for the section handler to <see cref="System.Configuration.IgnoreSectionHandler"/>, for example:
- /// <code lang="XML" escaped="true">
- /// <configSections>
- /// <section name="log4net" type="System.Configuration.IgnoreSectionHandler" />
- /// </configSections>
- /// </code>
- /// </para>
- /// <example>
- /// The following example configures log4net using a configuration file, of which the
- /// location is stored in the application's configuration file :
- /// </example>
- /// <code lang="C#">
- /// using log4net.Config;
- /// using System.IO;
- /// using System.Configuration;
- ///
- /// ...
- ///
- /// XmlConfigurator.Configure(new FileInfo(ConfigurationSettings.AppSettings["log4net-config-file"]));
- /// </code>
- /// <para>
- /// In the <c>.config</c> file, the path to the log4net can be specified like this :
- /// </para>
- /// <code lang="XML" escaped="true">
- /// <configuration>
- /// <appSettings>
- /// <add key="log4net-config-file" value="log.config"/>
- /// </appSettings>
- /// </configuration>
- /// </code>
- /// </remarks>
- #else
- /// <summary>
- /// Configures the <see cref="ILoggerRepository"/> using the specified configuration
- /// file.
- /// </summary>
- /// <param name="repository">The repository to configure.</param>
- /// <param name="configFile">The XML file to load the configuration from.</param>
- /// <remarks>
- /// <para>
- /// The configuration file must be valid XML. It must contain
- /// at least one element called <c>log4net</c> that holds
- /// the configuration data.
- /// </para>
- /// <example>
- /// The following example configures log4net using a configuration file, of which the
- /// location is stored in the application's configuration file :
- /// </example>
- /// <code lang="C#">
- /// using log4net.Config;
- /// using System.IO;
- /// using System.Configuration;
- ///
- /// ...
- ///
- /// XmlConfigurator.Configure(new FileInfo(ConfigurationSettings.AppSettings["log4net-config-file"]));
- /// </code>
- /// <para>
- /// In the <c>.config</c> file, the path to the log4net can be specified like this :
- /// </para>
- /// <code lang="XML" escaped="true">
- /// <configuration>
- /// <appSettings>
- /// <add key="log4net-config-file" value="log.config"/>
- /// </appSettings>
- /// </configuration>
- /// </code>
- /// </remarks>
- #endif
- static public ICollection Configure(ILoggerRepository repository, FileInfo configFile)
- {
- ArrayList configurationMessages = new ArrayList();
- using (new LogLog.LogReceivedAdapter(configurationMessages))
- {
- InternalConfigure(repository, configFile);
- }
- repository.ConfigurationMessages = configurationMessages;
- return configurationMessages;
- }
-
- static private 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.Close();
- }
- }
- }
- else
- {
- LogLog.Debug(declaringType, "config file [" + configFile.FullName + "] not found. Configuration unchanged.");
- }
- }
- }
- /// <summary>
- /// Configures the <see cref="ILoggerRepository"/> using the specified configuration
- /// URI.
- /// </summary>
- /// <param name="repository">The repository to configure.</param>
- /// <param name="configUri">A URI to load the XML configuration from.</param>
- /// <remarks>
- /// <para>
- /// The configuration data must be valid XML. It must contain
- /// at least one element called <c>log4net</c> that holds
- /// the configuration data.
- /// </para>
- /// <para>
- /// The <see cref="System.Net.WebRequest"/> must support the URI scheme specified.
- /// </para>
- /// </remarks>
- static public ICollection Configure(ILoggerRepository repository, Uri configUri)
- {
- ArrayList configurationMessages = new ArrayList();
- using (new LogLog.LogReceivedAdapter(configurationMessages))
- {
- InternalConfigure(repository, configUri);
- }
- repository.ConfigurationMessages = configurationMessages;
- return configurationMessages;
- }
-
- static private 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 NETSTANDARD1_3
- WebResponse response = configRequest.GetResponseAsync().GetAwaiter().GetResult();
- #else
- WebResponse response = configRequest.GetResponse();
- #endif
- if (response != null)
- {
- try
- {
- // Open stream on config URI
- using(Stream configStream = response.GetResponseStream())
- {
- InternalConfigure(repository, configStream);
- }
- }
- finally
- {
- response.Close();
- }
- }
- }
- catch(Exception ex)
- {
- LogLog.Error(declaringType, "Failed to request config from URI ["+configUri+"]", ex);
- }
- }
- }
- }
- }
- /// <summary>
- /// Configures the <see cref="ILoggerRepository"/> using the specified configuration
- /// file.
- /// </summary>
- /// <param name="repository">The repository to configure.</param>
- /// <param name="configStream">The stream to load the XML configuration from.</param>
- /// <remarks>
- /// <para>
- /// The configuration data must be valid XML. It must contain
- /// at least one element called <c>log4net</c> that holds
- /// the configuration data.
- /// </para>
- /// <para>
- /// Note that this method will NOT close the stream parameter.
- /// </para>
- /// </remarks>
- static public ICollection Configure(ILoggerRepository repository, Stream configStream)
- {
- ArrayList configurationMessages = new ArrayList();
- using (new LogLog.LogReceivedAdapter(configurationMessages))
- {
- InternalConfigure(repository, configStream);
- }
- repository.ConfigurationMessages = configurationMessages;
- return configurationMessages;
- }
-
- static private 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
- XmlDocument doc = new XmlDocument();
- try
- {
- #if (NETCF)
- // Create a text reader for the file stream
- XmlTextReader xmlReader = new XmlTextReader(configStream);
- #elif NET_2_0 || NETSTANDARD1_3
- // 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
- settings.ProhibitDtd = false;
- #else
- settings.DtdProcessing = DtdProcessing.Parse;
- #endif
- // Create a reader over the input stream
- XmlReader xmlReader = XmlReader.Create(configStream, settings);
- #else
- // Create a validating reader around a text reader for the file stream
- 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 <log4net> element. Configuration Aborted.");
- }
- else if (configNodeList.Count > 1)
- {
- LogLog.Error(declaringType, "XML configuration contains [" + configNodeList.Count + "] <log4net> 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).
- /// <summary>
- /// Configures log4net using the file specified, monitors the file for changes
- /// and reloads the configuration if a change is detected.
- /// </summary>
- /// <param name="configFile">The XML file to load the configuration from.</param>
- /// <remarks>
- /// <para>
- /// The configuration file must be valid XML. It must contain
- /// at least one element called <c>log4net</c> that holds
- /// the configuration data.
- /// </para>
- /// <para>
- /// The configuration file will be monitored using a <see cref="FileSystemWatcher"/>
- /// and depends on the behavior of that class.
- /// </para>
- /// <para>
- /// For more information on how to configure log4net using
- /// a separate configuration file, see <see cref="M:Configure(FileInfo)"/>.
- /// </para>
- /// </remarks>
- /// <seealso cref="M:Configure(FileInfo)"/>
- static public 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
- /// <summary>
- /// Configures the <see cref="ILoggerRepository"/> using the file specified,
- /// monitors the file for changes and reloads the configuration if a change
- /// is detected.
- /// </summary>
- /// <param name="repository">The repository to configure.</param>
- /// <param name="configFile">The XML file to load the configuration from.</param>
- /// <remarks>
- /// <para>
- /// The configuration file must be valid XML. It must contain
- /// at least one element called <c>log4net</c> that holds
- /// the configuration data.
- /// </para>
- /// <para>
- /// The configuration file will be monitored using a <see cref="FileSystemWatcher"/>
- /// and depends on the behavior of that class.
- /// </para>
- /// <para>
- /// For more information on how to configure log4net using
- /// a separate configuration file, see <see cref="M:Configure(FileInfo)"/>.
- /// </para>
- /// </remarks>
- /// <seealso cref="M:Configure(FileInfo)"/>
- static public ICollection ConfigureAndWatch(ILoggerRepository repository, FileInfo configFile)
- {
- ArrayList configurationMessages = new ArrayList();
- using (new LogLog.LogReceivedAdapter(configurationMessages))
- {
- InternalConfigureAndWatch(repository, configFile);
- }
- repository.ConfigurationMessages = configurationMessages;
- return configurationMessages;
- }
-
- static private 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)
- /// <summary>
- /// Class used to watch config files.
- /// </summary>
- /// <remarks>
- /// <para>
- /// Uses the <see cref="FileSystemWatcher"/> 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 <see cref="TimeoutMillis"/> time before delivering
- /// the event notification. If any further <see cref="FileSystemWatcher"/>
- /// change notifications arrive while the timer is waiting it
- /// is reset and waits again for <see cref="TimeoutMillis"/> to
- /// elapse.
- /// </para>
- /// </remarks>
- private sealed class ConfigureAndWatchHandler : IDisposable
- {
- /// <summary>
- /// Holds the FileInfo used to configure the XmlConfigurator
- /// </summary>
- private FileInfo m_configFile;
- /// <summary>
- /// Holds the repository being configured.
- /// </summary>
- private ILoggerRepository m_repository;
- /// <summary>
- /// The timer used to compress the notification events.
- /// </summary>
- private Timer m_timer;
- /// <summary>
- /// The default amount of time to wait after receiving notification
- /// before reloading the config file.
- /// </summary>
- private const int TimeoutMillis = 500;
- /// <summary>
- /// Watches file for changes. This object should be disposed when no longer
- /// needed to free system handles on the watched resources.
- /// </summary>
- private FileSystemWatcher m_watcher;
- /// <summary>
- /// Initializes a new instance of the <see cref="ConfigureAndWatchHandler" /> class to
- /// watch a specified config file used to configure a repository.
- /// </summary>
- /// <param name="repository">The repository to configure.</param>
- /// <param name="configFile">The configuration file to watch.</param>
- /// <remarks>
- /// <para>
- /// Initializes a new instance of the <see cref="ConfigureAndWatchHandler" /> class.
- /// </para>
- /// </remarks>
- #if NET_4_0 || MONO_4_0 || NETSTANDARD1_3
- [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);
- }
- /// <summary>
- /// Event handler used by <see cref="ConfigureAndWatchHandler"/>.
- /// </summary>
- /// <param name="source">The <see cref="FileSystemWatcher"/> firing the event.</param>
- /// <param name="e">The argument indicates the file that caused the event to be fired.</param>
- /// <remarks>
- /// <para>
- /// This handler reloads the configuration from the file when the event is fired.
- /// </para>
- /// </remarks>
- 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);
- }
- /// <summary>
- /// Event handler used by <see cref="ConfigureAndWatchHandler"/>.
- /// </summary>
- /// <param name="source">The <see cref="FileSystemWatcher"/> firing the event.</param>
- /// <param name="e">The argument indicates the file that caused the event to be fired.</param>
- /// <remarks>
- /// <para>
- /// This handler reloads the configuration from the file when the event is fired.
- /// </para>
- /// </remarks>
- 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);
- }
- /// <summary>
- /// Called by the timer when the configuration has been updated.
- /// </summary>
- /// <param name="state">null</param>
- private void OnWatchedFileChange(object state)
- {
- XmlConfigurator.InternalConfigure(m_repository, m_configFile);
- }
- /// <summary>
- /// Release the handles held by the watcher and timer.
- /// </summary>
- #if NET_4_0 || MONO_4_0 || NETSTANDARD1_3
- [System.Security.SecuritySafeCritical]
- #endif
- public void Dispose()
- {
- m_watcher.EnableRaisingEvents = false;
- m_watcher.Dispose();
- m_timer.Dispose();
- }
- }
- #endif
- #endregion ConfigureAndWatchHandler
- #region Private Static Methods
- /// <summary>
- /// Configures the specified repository using a <c>log4net</c> element.
- /// </summary>
- /// <param name="repository">The hierarchy to configure.</param>
- /// <param name="element">The element to parse.</param>
- /// <remarks>
- /// <para>
- /// Loads the log4net configuration from the XML element
- /// supplied as <paramref name="element"/>.
- /// </para>
- /// <para>
- /// This method is ultimately called by one of the Configure methods
- /// to load the configuration from an <see cref="XmlElement"/>.
- /// </para>
- /// </remarks>
- static private 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
- XmlDocument newDoc = new XmlDocument();
- 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
- /// <summary>
- /// Maps repository names to ConfigAndWatchHandler instances to allow a particular
- /// ConfigAndWatchHandler to dispose of its FileSystemWatcher when a repository is
- /// reconfigured.
- /// </summary>
- private readonly static Hashtable m_repositoryName2ConfigAndWatchHandler = new Hashtable();
- /// <summary>
- /// The fully qualified type of the XmlConfigurator class.
- /// </summary>
- /// <remarks>
- /// Used by the internal logger to record the Type of the
- /// log message.
- /// </remarks>
- private readonly static Type declaringType = typeof(XmlConfigurator);
- #endregion Private Static Fields
- }
- }
|