#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 // .NET Compact Framework 1.0 has no support for reading assembly attributes #if !NETCF using System; using System.Collections; using System.Reflection; using System.IO; using log4net.Util; using log4net.Repository; using log4net.Repository.Hierarchy; namespace log4net.Config { /// /// Assembly level attribute to configure the . /// /// /// /// This attribute may only be used at the assembly scope and can only /// be used once per assembly. /// /// /// Use this attribute to configure the /// without calling one of the /// methods. /// /// /// If neither of the or /// properties are set the configuration is loaded from the application's .config file. /// If set the property takes priority over the /// property. The property /// specifies a path to a file to load the config from. The path is relative to the /// application's base directory; . /// The property is used as a postfix to the assembly file name. /// The config file must be located in the application's base directory; . /// For example in a console application setting the to /// config has the same effect as not specifying the or /// properties. /// /// /// The property can be set to cause the /// to watch the configuration file for changes. /// /// /// /// Log4net will only look for assembly level configuration attributes once. /// When using the log4net assembly level attributes to control the configuration /// of log4net you must ensure that the first call to any of the /// methods is made from the assembly with the configuration /// attributes. /// /// /// If you cannot guarantee the order in which log4net calls will be made from /// different assemblies you must use programmatic configuration instead, i.e. /// call the method directly. /// /// /// /// Nicko Cadell /// Gert Driesen [AttributeUsage(AttributeTargets.Assembly)] [Serializable] public /*sealed*/ class XmlConfiguratorAttribute : ConfiguratorAttribute { // // Class is not sealed because DOMConfiguratorAttribute extends it while it is obsoleted // /// /// Default constructor /// /// /// /// Default constructor /// /// public XmlConfiguratorAttribute() : base(0) /* configurator priority 0 */ { } #region Public Instance Properties /// /// Gets or sets the filename of the configuration file. /// /// /// The filename of the configuration file. /// /// /// /// If specified, this is the name of the configuration file to use with /// the . This file path is relative to the /// application base directory (). /// /// /// The takes priority over the . /// /// public string ConfigFile { get { return m_configFile; } set { m_configFile = value; } } /// /// Gets or sets the extension of the configuration file. /// /// /// The extension of the configuration file. /// /// /// /// If specified this is the extension for the configuration file. /// The path to the config file is built by using the application /// base directory (), /// the assembly file name and the config file extension. /// /// /// If the is set to MyExt then /// possible config file names would be: MyConsoleApp.exe.MyExt or /// MyClassLibrary.dll.MyExt. /// /// /// The takes priority over the . /// /// public string ConfigFileExtension { get { return m_configFileExtension; } set { m_configFileExtension = value; } } /// /// Gets or sets a value indicating whether to watch the configuration file. /// /// /// true if the configuration should be watched, false otherwise. /// /// /// /// If this flag is specified and set to true then the framework /// will watch the configuration file and will reload the config each time /// the file is modified. /// /// /// The config file can only be watched if it is loaded from local disk. /// In a No-Touch (Smart Client) deployment where the application is downloaded /// from a web server the config file may not reside on the local disk /// and therefore it may not be able to watch it. /// /// /// Watching configuration is not supported on the SSCLI. /// /// public bool Watch { get { return m_configureAndWatch; } set { m_configureAndWatch = value; } } #endregion Public Instance Properties #region Override ConfiguratorAttribute /// /// Configures the for the specified assembly. /// /// The assembly that this attribute was defined on. /// The repository to configure. /// /// /// Configure the repository using the . /// The specified must extend the /// class otherwise the will not be able to /// configure it. /// /// /// The does not extend . override public void Configure(Assembly sourceAssembly, ILoggerRepository targetRepository) { IList configurationMessages = new ArrayList(); using (new LogLog.LogReceivedAdapter(configurationMessages)) { string applicationBaseDirectory = null; try { applicationBaseDirectory = SystemInfo.ApplicationBaseDirectory; } catch { // Ignore this exception because it is only thrown when ApplicationBaseDirectory is a file // and the application does not have PathDiscovery permission } if (applicationBaseDirectory == null || (new Uri(applicationBaseDirectory)).IsFile) { ConfigureFromFile(sourceAssembly, targetRepository); } else { ConfigureFromUri(sourceAssembly, targetRepository); } } targetRepository.ConfigurationMessages = configurationMessages; } #endregion /// /// Attempt to load configuration from the local file system /// /// The assembly that this attribute was defined on. /// The repository to configure. private void ConfigureFromFile(Assembly sourceAssembly, ILoggerRepository targetRepository) { // Work out the full path to the config file string fullPath2ConfigFile = null; // Select the config file if (m_configFile == null || m_configFile.Length == 0) { if (m_configFileExtension == null || m_configFileExtension.Length == 0) { // Use the default .config file for the AppDomain try { fullPath2ConfigFile = SystemInfo.ConfigurationFileLocation; } catch(Exception ex) { LogLog.Error(declaringType, "XmlConfiguratorAttribute: Exception getting ConfigurationFileLocation. Must be able to resolve ConfigurationFileLocation when ConfigFile and ConfigFileExtension properties are not set.", ex); } } else { // Force the extension to start with a '.' if (m_configFileExtension[0] != '.') { m_configFileExtension = "." + m_configFileExtension; } string applicationBaseDirectory = null; try { applicationBaseDirectory = SystemInfo.ApplicationBaseDirectory; } catch(Exception ex) { LogLog.Error(declaringType, "Exception getting ApplicationBaseDirectory. Must be able to resolve ApplicationBaseDirectory and AssemblyFileName when ConfigFileExtension property is set.", ex); } if (applicationBaseDirectory != null) { fullPath2ConfigFile = Path.Combine(applicationBaseDirectory, SystemInfo.AssemblyFileName(sourceAssembly) + m_configFileExtension); } } } else { string applicationBaseDirectory = null; try { applicationBaseDirectory = SystemInfo.ApplicationBaseDirectory; } catch(Exception ex) { LogLog.Warn(declaringType, "Exception getting ApplicationBaseDirectory. ConfigFile property path ["+m_configFile+"] will be treated as an absolute path.", ex); } if (applicationBaseDirectory != null) { // Just the base dir + the config file fullPath2ConfigFile = Path.Combine(applicationBaseDirectory, m_configFile); } else { fullPath2ConfigFile = m_configFile; } } if (fullPath2ConfigFile != null) { ConfigureFromFile(targetRepository, new FileInfo(fullPath2ConfigFile)); } } /// /// Configure the specified repository using a /// /// The repository to configure. /// the FileInfo pointing to the config file private void ConfigureFromFile(ILoggerRepository targetRepository, FileInfo configFile) { #if (SSCLI) if (m_configureAndWatch) { LogLog.Warn(declaringType, "XmlConfiguratorAttribute: Unable to watch config file not supported on SSCLI"); } XmlConfigurator.Configure(targetRepository, configFile); #else // Do we configure just once or do we configure and then watch? if (m_configureAndWatch) { XmlConfigurator.ConfigureAndWatch(targetRepository, configFile); } else { XmlConfigurator.Configure(targetRepository, configFile); } #endif } /// /// Attempt to load configuration from a URI /// /// The assembly that this attribute was defined on. /// The repository to configure. private void ConfigureFromUri(Assembly sourceAssembly, ILoggerRepository targetRepository) { // Work out the full path to the config file Uri fullPath2ConfigFile = null; // Select the config file if (m_configFile == null || m_configFile.Length == 0) { if (m_configFileExtension == null || m_configFileExtension.Length == 0) { string systemConfigFilePath = null; try { systemConfigFilePath = SystemInfo.ConfigurationFileLocation; } catch(Exception ex) { LogLog.Error(declaringType, "XmlConfiguratorAttribute: Exception getting ConfigurationFileLocation. Must be able to resolve ConfigurationFileLocation when ConfigFile and ConfigFileExtension properties are not set.", ex); } if (systemConfigFilePath != null) { Uri systemConfigFileUri = new Uri(systemConfigFilePath); // Use the default .config file for the AppDomain fullPath2ConfigFile = systemConfigFileUri; } } else { // Force the extension to start with a '.' if (m_configFileExtension[0] != '.') { m_configFileExtension = "." + m_configFileExtension; } string systemConfigFilePath = null; try { systemConfigFilePath = SystemInfo.ConfigurationFileLocation; } catch(Exception ex) { LogLog.Error(declaringType, "XmlConfiguratorAttribute: Exception getting ConfigurationFileLocation. Must be able to resolve ConfigurationFileLocation when the ConfigFile property are not set.", ex); } if (systemConfigFilePath != null) { UriBuilder builder = new UriBuilder(new Uri(systemConfigFilePath)); // Remove the current extension from the systemConfigFileUri path string path = builder.Path; int startOfExtension = path.LastIndexOf("."); if (startOfExtension >= 0) { path = path.Substring(0, startOfExtension); } path += m_configFileExtension; builder.Path = path; fullPath2ConfigFile = builder.Uri; } } } else { string applicationBaseDirectory = null; try { applicationBaseDirectory = SystemInfo.ApplicationBaseDirectory; } catch(Exception ex) { LogLog.Warn(declaringType, "Exception getting ApplicationBaseDirectory. ConfigFile property path ["+m_configFile+"] will be treated as an absolute URI.", ex); } if (applicationBaseDirectory != null) { // Just the base dir + the config file fullPath2ConfigFile = new Uri(new Uri(applicationBaseDirectory), m_configFile); } else { fullPath2ConfigFile = new Uri(m_configFile); } } if (fullPath2ConfigFile != null) { if (fullPath2ConfigFile.IsFile) { // The m_configFile could be an absolute local path, therefore we have to be // prepared to switch back to using FileInfos here ConfigureFromFile(targetRepository, new FileInfo(fullPath2ConfigFile.LocalPath)); } else { if (m_configureAndWatch) { LogLog.Warn(declaringType, "XmlConfiguratorAttribute: Unable to watch config file loaded from a URI"); } XmlConfigurator.Configure(targetRepository, fullPath2ConfigFile); } } } #region Private Instance Fields private string m_configFile = null; private string m_configFileExtension = null; private bool m_configureAndWatch = false; #endregion Private Instance Fields #region Private Static Fields /// /// The fully qualified type of the XmlConfiguratorAttribute class. /// /// /// Used by the internal logger to record the Type of the /// log message. /// private readonly static Type declaringType = typeof(XmlConfiguratorAttribute); #endregion Private Static Fields } } #endif // !NETCF