| #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 |
| { |
| /// <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 |
| 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 <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 |
| public static 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> |
| 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 |
| /// <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 |
| public static 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> |
| 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; |
| } |
| |
| /// <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> |
| 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 |
| |
| /// <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> |
| 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 |
| /// <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 |
| 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."); |
| } |
| } |
| } |
| |
| /// <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> |
| 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); |
| } |
| } |
| } |
| } |
| } |
| |
| /// <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> |
| 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 <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)"/> |
| 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 |
| |
| /// <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)"/> |
| 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) |
| /// <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 || 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); |
| } |
| |
| /// <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 || 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 |
| |
| /// <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> |
| 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 |
| |
| /// <summary> |
| /// Maps repository names to ConfigAndWatchHandler instances to allow a particular |
| /// ConfigAndWatchHandler to dispose of its FileSystemWatcher when a repository is |
| /// reconfigured. |
| /// </summary> |
| private static readonly 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 static readonly Type declaringType = typeof(XmlConfigurator); |
| |
| #endregion Private Static Fields |
| } |
| } |
| |