Fixed: #17453 - Logging module (Log4net initialization) is throwing exceptions in Medium Trust

--HG--
branch : contributions
This commit is contained in:
attilah
2011-03-08 22:30:26 +01:00
parent 3f5e444acb
commit 556be00e5d
6 changed files with 1637 additions and 1 deletions

View File

@@ -20,7 +20,7 @@ namespace Orchard.Logging {
protected override void Load(ContainerBuilder moduleBuilder) {
// by default, use Orchard's logger that delegates to Castle's logger factory
moduleBuilder.RegisterType<CastleLoggerFactory>().As<ILoggerFactory>().InstancePerLifetimeScope();
moduleBuilder.RegisterType<Log4netFactory>().As<Castle.Core.Logging.ILoggerFactory>().InstancePerLifetimeScope();
moduleBuilder.RegisterType<OrchardLog4netFactory>().As<Castle.Core.Logging.ILoggerFactory>().InstancePerLifetimeScope();
// call CreateLogger in response to the request for an ILogger implementation
moduleBuilder.Register(CreateLogger).As<ILogger>().InstancePerDependency();

View File

@@ -0,0 +1,58 @@
using System;
using System.Configuration;
using System.IO;
using System.Web;
using System.Web.Hosting;
using Castle.Core.Logging;
using Castle.Services.Logging.Log4netIntegration;
using log4net;
using log4net.Config;
using Orchard.Environment.Extensions.Helpers;
namespace Orchard.Logging {
public class OrchardLog4netFactory : AbstractLoggerFactory {
public OrchardLog4netFactory()
: this(ConfigurationManager.AppSettings["log4net.Config"]) {
}
public OrchardLog4netFactory(String configFilename) {
if (!String.IsNullOrWhiteSpace(configFilename)) {
var mappedConfigFilename = configFilename;
if (HostingEnvironment.IsHosted) {
if (!VirtualPathUtility.IsAppRelative(mappedConfigFilename)) {
if (!mappedConfigFilename.StartsWith("/")) {
mappedConfigFilename = "~/" + mappedConfigFilename;
}
else {
mappedConfigFilename = "~" + mappedConfigFilename;
}
}
mappedConfigFilename = HostingEnvironment.MapPath(mappedConfigFilename);
}
OrchardXmlConfigurator.Configure(mappedConfigFilename);
}
}
/// <summary>
/// Configures log4net with a stream containing XML.
/// </summary>
/// <param name = "config"></param>
public OrchardLog4netFactory(Stream config) {
OrchardXmlConfigurator.Configure(config);
}
public override Castle.Core.Logging.ILogger Create(string name, LoggerLevel level) {
throw new NotSupportedException("Logger levels cannot be set at runtime. Please review your configuration file.");
}
public override Castle.Core.Logging.ILogger Create(string name) {
return new OrchardLog4netLogger(LogManager.GetLogger(name), this);
}
}
}

View File

@@ -0,0 +1,347 @@
// Copyright 2004-2010 Castle Project - http://www.castleproject.org/
//
// Licensed 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.
using System;
using System.Globalization;
using log4net;
using log4net.Core;
using log4net.Util;
using Logger = Castle.Core.Logging.ILogger;
namespace Orchard.Logging {
[Serializable]
public class OrchardLog4netLogger : MarshalByRefObject, Logger {
private static readonly Type declaringType = typeof(OrchardLog4netLogger);
public OrchardLog4netLogger(log4net.Core.ILogger logger, OrchardLog4netFactory factory) {
Logger = logger;
Factory = factory;
}
internal OrchardLog4netLogger() {
}
internal OrchardLog4netLogger(ILog log, OrchardLog4netFactory factory)
: this(log.Logger, factory) {
}
public bool IsDebugEnabled {
get { return Logger.IsEnabledFor(Level.Debug); }
}
public bool IsErrorEnabled {
get { return Logger.IsEnabledFor(Level.Error); }
}
public bool IsFatalEnabled {
get { return Logger.IsEnabledFor(Level.Fatal); }
}
public bool IsInfoEnabled {
get { return Logger.IsEnabledFor(Level.Info); }
}
public bool IsWarnEnabled {
get { return Logger.IsEnabledFor(Level.Warn); }
}
protected internal OrchardLog4netFactory Factory { get; set; }
protected internal log4net.Core.ILogger Logger { get; set; }
public override string ToString() {
return Logger.ToString();
}
public virtual Logger CreateChildLogger(String name) {
return Factory.Create(Logger.Name + "." + name);
}
public void Debug(String message) {
if (IsDebugEnabled) {
Logger.Log(declaringType, Level.Debug, message, null);
}
}
public void Debug(Func<string> messageFactory) {
if (IsDebugEnabled) {
Logger.Log(declaringType, Level.Debug, messageFactory.Invoke(), null);
}
}
public void Debug(String message, Exception exception) {
if (IsDebugEnabled) {
Logger.Log(declaringType, Level.Debug, message, exception);
}
}
public void DebugFormat(String format, params Object[] args) {
if (IsDebugEnabled) {
Logger.Log(declaringType, Level.Debug, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null);
}
}
public void DebugFormat(Exception exception, String format, params Object[] args) {
if (IsDebugEnabled) {
Logger.Log(declaringType, Level.Debug, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), exception);
}
}
public void DebugFormat(IFormatProvider formatProvider, String format, params Object[] args) {
if (IsDebugEnabled) {
Logger.Log(declaringType, Level.Debug, new SystemStringFormat(formatProvider, format, args), null);
}
}
public void DebugFormat(Exception exception, IFormatProvider formatProvider, String format, params Object[] args) {
if (IsDebugEnabled) {
Logger.Log(declaringType, Level.Debug, new SystemStringFormat(formatProvider, format, args), exception);
}
}
public void Error(String message) {
if (IsErrorEnabled) {
Logger.Log(declaringType, Level.Error, message, null);
}
}
public void Error(Func<string> messageFactory) {
if (IsErrorEnabled) {
Logger.Log(declaringType, Level.Error, messageFactory.Invoke(), null);
}
}
public void Error(String message, Exception exception) {
if (IsErrorEnabled) {
Logger.Log(declaringType, Level.Error, message, exception);
}
}
public void ErrorFormat(String format, params Object[] args) {
if (IsErrorEnabled) {
Logger.Log(declaringType, Level.Error, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null);
}
}
public void ErrorFormat(Exception exception, String format, params Object[] args) {
if (IsErrorEnabled) {
Logger.Log(declaringType, Level.Error, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), exception);
}
}
public void ErrorFormat(IFormatProvider formatProvider, String format, params Object[] args) {
if (IsErrorEnabled) {
Logger.Log(declaringType, Level.Error, new SystemStringFormat(formatProvider, format, args), null);
}
}
public void ErrorFormat(Exception exception, IFormatProvider formatProvider, String format, params Object[] args) {
if (IsErrorEnabled) {
Logger.Log(declaringType, Level.Error, new SystemStringFormat(formatProvider, format, args), exception);
}
}
public void Fatal(String message) {
if (IsFatalEnabled) {
Logger.Log(declaringType, Level.Fatal, message, null);
}
}
public void Fatal(Func<string> messageFactory) {
if (IsFatalEnabled) {
Logger.Log(declaringType, Level.Fatal, messageFactory.Invoke(), null);
}
}
public void Fatal(String message, Exception exception) {
if (IsFatalEnabled) {
Logger.Log(declaringType, Level.Fatal, message, exception);
}
}
public void FatalFormat(String format, params Object[] args) {
if (IsFatalEnabled) {
Logger.Log(declaringType, Level.Fatal, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null);
}
}
public void FatalFormat(Exception exception, String format, params Object[] args) {
if (IsFatalEnabled) {
Logger.Log(declaringType, Level.Fatal, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), exception);
}
}
public void FatalFormat(IFormatProvider formatProvider, String format, params Object[] args) {
if (IsFatalEnabled) {
Logger.Log(declaringType, Level.Fatal, new SystemStringFormat(formatProvider, format, args), null);
}
}
public void FatalFormat(Exception exception, IFormatProvider formatProvider, String format, params Object[] args) {
if (IsFatalEnabled) {
Logger.Log(declaringType, Level.Fatal, new SystemStringFormat(formatProvider, format, args), exception);
}
}
public void Info(String message) {
if (IsInfoEnabled) {
Logger.Log(declaringType, Level.Info, message, null);
}
}
public void Info(Func<string> messageFactory) {
if (IsInfoEnabled) {
Logger.Log(declaringType, Level.Info, messageFactory.Invoke(), null);
}
}
public void Info(String message, Exception exception) {
if (IsInfoEnabled) {
Logger.Log(declaringType, Level.Info, message, exception);
}
}
public void InfoFormat(String format, params Object[] args) {
if (IsInfoEnabled) {
Logger.Log(declaringType, Level.Info, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null);
}
}
public void InfoFormat(Exception exception, String format, params Object[] args) {
if (IsInfoEnabled) {
Logger.Log(declaringType, Level.Info, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), exception);
}
}
public void InfoFormat(IFormatProvider formatProvider, String format, params Object[] args) {
if (IsInfoEnabled) {
Logger.Log(declaringType, Level.Info, new SystemStringFormat(formatProvider, format, args), null);
}
}
public void InfoFormat(Exception exception, IFormatProvider formatProvider, String format, params Object[] args) {
if (IsInfoEnabled) {
Logger.Log(declaringType, Level.Info, new SystemStringFormat(formatProvider, format, args), exception);
}
}
public void Warn(String message) {
if (IsWarnEnabled) {
Logger.Log(declaringType, Level.Warn, message, null);
}
}
public void Warn(Func<string> messageFactory) {
if (IsWarnEnabled) {
Logger.Log(declaringType, Level.Warn, messageFactory.Invoke(), null);
}
}
public void Warn(String message, Exception exception) {
if (IsWarnEnabled) {
Logger.Log(declaringType, Level.Warn, message, exception);
}
}
public void WarnFormat(String format, params Object[] args) {
if (IsWarnEnabled) {
Logger.Log(declaringType, Level.Warn, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), null);
}
}
public void WarnFormat(Exception exception, String format, params Object[] args) {
if (IsWarnEnabled) {
Logger.Log(declaringType, Level.Warn, new SystemStringFormat(CultureInfo.InvariantCulture, format, args), exception);
}
}
public void WarnFormat(IFormatProvider formatProvider, String format, params Object[] args) {
if (IsWarnEnabled) {
Logger.Log(declaringType, Level.Warn, new SystemStringFormat(formatProvider, format, args), null);
}
}
public void WarnFormat(Exception exception, IFormatProvider formatProvider, String format, params Object[] args) {
if (IsWarnEnabled) {
Logger.Log(declaringType, Level.Warn, new SystemStringFormat(formatProvider, format, args), exception);
}
}
[Obsolete("Use IsFatalEnabled instead")]
public bool IsFatalErrorEnabled {
get {
return Logger.IsEnabledFor(Level.Fatal);
}
}
[Obsolete("Use DebugFormat instead")]
public void Debug(string format, params object[] args) {
if (IsDebugEnabled) {
Logger.Log(declaringType, Level.Debug, string.Format(format, args), null);
}
}
[Obsolete("Use ErrorFormat instead")]
public void Error(string format, params object[] args) {
if (IsErrorEnabled) {
Logger.Log(declaringType, Level.Error, string.Format(format, args), null);
}
}
[Obsolete("Use FatalFormat instead")]
public void Fatal(string format, params object[] args) {
if (IsFatalEnabled) {
Logger.Log(declaringType, Level.Fatal, string.Format(format, args), null);
}
}
[Obsolete("Use Fatal instead")]
public void FatalError(string message) {
if (IsFatalErrorEnabled) {
Logger.Log(declaringType, Level.Fatal, message, null);
}
}
[Obsolete("Use FatalFormat instead")]
public void FatalError(string format, params object[] args) {
if (IsFatalErrorEnabled) {
Logger.Log(declaringType, Level.Fatal, string.Format(format, args), null);
}
}
[Obsolete("Use Fatal instead")]
public void FatalError(string message, Exception exception) {
if (IsFatalErrorEnabled) {
Logger.Log(declaringType, Level.Fatal, message, exception);
}
}
[Obsolete("Use InfoFormat instead")]
public void Info(string format, params object[] args) {
if (IsInfoEnabled) {
Logger.Log(declaringType, Level.Info, string.Format(format, args), null);
}
}
[Obsolete("Use WarnFormat instead")]
public void Warn(string format, params object[] args) {
if (IsWarnEnabled) {
Logger.Log(declaringType, Level.Warn, string.Format(format, args), null);
}
}
}
}

View File

@@ -0,0 +1,325 @@
using System;
using System.Xml;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Net;
using log4net;
using log4net.Appender;
using log4net.Util;
using log4net.Repository;
using log4net.Repository.Hierarchy;
namespace Orchard.Logging {
public class OrchardXmlConfigurator {
#region Private Instance Constructors
/// <summary>
/// Private constructor
/// </summary>
private OrchardXmlConfigurator() {
}
#endregion Protected Instance Constructors
#region Configure static methods
/// <summary>
/// Configures log4net using the specified configuration file.
/// </summary>
/// <param name="configFilename">The name of 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>&lt;configuration&gt;</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(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>
static public void Configure(string configFilename) {
Configure(LogManager.GetRepository(Assembly.GetCallingAssembly()), configFilename);
}
/// <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 void Configure(Stream configStream) {
Configure(LogManager.GetRepository(Assembly.GetCallingAssembly()), configStream);
}
/// <summary>
/// Configures the <see cref="ILoggerRepository"/> using the specified configuration
/// file.
/// </summary>
/// <param name="repository">The repository to configure.</param>
/// <param name="configFilename">The name of 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>&lt;configuration&gt;</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(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>
static public void Configure(ILoggerRepository repository, string configFilename) {
LogLog.Debug("XmlConfigurator: configuring repository [" + repository.Name + "] using file [" + configFilename + "]");
if (String.IsNullOrWhiteSpace(configFilename)) {
LogLog.Error("XmlConfigurator: Configure called with null 'configFilename' parameter");
}
else {
// Have to use File.Exists() rather than configFile.Exists()
// because configFile.Exists() caches the value, not what we want.
if (File.Exists(configFilename)) {
// Open the file for reading
FileStream fs = null;
// Try hard to open the file
for (int retry = 5; --retry >= 0; ) {
try {
fs = new FileStream(configFilename, FileMode.Open, FileAccess.Read, FileShare.Read);
break;
}
catch (IOException ex) {
if (retry == 0) {
LogLog.Error("XmlConfigurator: Failed to open XML config file [" + configFilename + "]", ex);
// The stream cannot be valid
fs = null;
}
System.Threading.Thread.Sleep(250);
}
}
if (fs != null) {
try {
// Load the configuration from the stream
Configure(repository, fs);
}
finally {
// Force the file closed whatever happens
fs.Close();
}
}
}
else {
LogLog.Debug("XmlConfigurator: config file [" + configFilename + "] not found. Configuration unchanged.");
}
}
}
/// <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 void Configure(ILoggerRepository repository, Stream configStream) {
LogLog.Debug("XmlConfigurator: configuring repository [" + repository.Name + "] using stream");
if (configStream == null) {
LogLog.Error("XmlConfigurator: Configure called with null 'configStream' parameter");
}
else {
// Load the config file into a document
XmlDocument doc = new XmlDocument();
try {
// Create a text reader for the file stream
XmlTextReader xmlReader = new XmlTextReader(configStream);
xmlReader.DtdProcessing = DtdProcessing.Parse;
// Specify that the reader should not perform validation
XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.None;
// load the data into the document
doc.Load(xmlReader);
}
catch (Exception ex) {
LogLog.Error("XmlConfigurator: Error while loading XML configuration", ex);
// The document is invalid
doc = null;
}
if (doc != null) {
LogLog.Debug("XmlConfigurator: loading XML configuration");
// Configure using the 'log4net' element
XmlNodeList configNodeList = doc.GetElementsByTagName("log4net");
if (configNodeList.Count == 0) {
LogLog.Debug("XmlConfigurator: XML configuration does not contain a <log4net> element. Configuration Aborted.");
}
else if (configNodeList.Count > 1) {
LogLog.Error("XmlConfigurator: XML configuration contains [" + configNodeList.Count + "] <log4net> elements. Only one is allowed. Configuration Aborted.");
}
else {
ConfigureFromXml(repository, configNodeList[0] as XmlElement);
}
}
}
}
#endregion Configure static methods
#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 ConfigureFromXml(ILoggerRepository repository, XmlElement element) {
if (element == null) {
LogLog.Error("XmlConfigurator: ConfigureFromXml called with null 'element' parameter");
}
else if (repository == null) {
LogLog.Error("XmlConfigurator: ConfigureFromXml called with null 'repository' parameter");
}
else {
LogLog.Debug("XmlConfigurator: Configuring Repository [" + repository.Name + "]");
//
// Since we're not reinventing the whole Hierarchy class from log4net to add out optimizations to XmlRepositoryConfigurator
// we've to check the neccessary casts here to be able to complete the configuration.
//
// Needed to fire configuration changed event
LoggerRepositorySkeleton repositorySkeleton = repository as LoggerRepositorySkeleton;
// Needed to XmlHierarchyConfigurator
Hierarchy hierarchy = repository as Hierarchy;
if (repositorySkeleton == null || hierarchy == null) {
LogLog.Warn("XmlConfigurator: Repository [" + repository + "] does not support the XmlConfigurator");
}
else {
OrchardXmlHierarchyConfigurator configurator = new OrchardXmlHierarchyConfigurator(hierarchy);
// 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
configurator.Configure(newElement);
repositorySkeleton.RaiseConfigurationChanged(EventArgs.Empty);
}
}
}
#endregion Private Static Methods
}
}

View File

@@ -0,0 +1,902 @@
using System;
using System.Collections;
using System.Globalization;
using System.Reflection;
using System.Xml;
using log4net.Appender;
using log4net.Util;
using log4net.Core;
using log4net.ObjectRenderer;
using log4net.Repository.Hierarchy;
namespace Orchard.Logging {
/// <summary>
/// Initializes the log4net environment using an XML DOM.
/// </summary>
/// <remarks>
/// <para>
/// Configures a <see cref="Hierarchy"/> using an XML DOM.
/// </para>
/// </remarks>
/// <author>Nicko Cadell</author>
/// <author>Gert Driesen</author>
public class OrchardXmlHierarchyConfigurator {
private enum ConfigUpdateMode {
Merge,
Overwrite
}
#region Public Instance Constructors
/// <summary>
/// Construct the configurator for a hierarchy
/// </summary>
/// <param name="hierarchy">The hierarchy to build.</param>
/// <remarks>
/// <para>
/// Initializes a new instance of the <see cref="OrchardXmlHierarchyConfigurator" /> class
/// with the specified <see cref="Hierarchy" />.
/// </para>
/// </remarks>
public OrchardXmlHierarchyConfigurator(Hierarchy hierarchy) {
m_hierarchy = hierarchy;
m_appenderBag = new Hashtable();
}
#endregion Public Instance Constructors
#region Public Instance Methods
/// <summary>
/// Configure the hierarchy by parsing a DOM tree of XML elements.
/// </summary>
/// <param name="element">The root element to parse.</param>
/// <remarks>
/// <para>
/// Configure the hierarchy by parsing a DOM tree of XML elements.
/// </para>
/// </remarks>
public void Configure(XmlElement element) {
if (element == null || m_hierarchy == null) {
return;
}
string rootElementName = element.LocalName;
if (rootElementName != CONFIGURATION_TAG) {
LogLog.Error("XmlHierarchyConfigurator: Xml element is - not a <" + CONFIGURATION_TAG + "> element.");
return;
}
if (!LogLog.InternalDebugging) {
// Look for a debug attribute to enable internal debug
string debugAttribute = element.GetAttribute(INTERNAL_DEBUG_ATTR);
LogLog.Debug("XmlHierarchyConfigurator: " + INTERNAL_DEBUG_ATTR + " attribute [" + debugAttribute + "].");
if (debugAttribute.Length > 0 && debugAttribute != "null") {
LogLog.InternalDebugging = OptionConverter.ToBoolean(debugAttribute, true);
}
else {
LogLog.Debug("XmlHierarchyConfigurator: Ignoring " + INTERNAL_DEBUG_ATTR + " attribute.");
}
string confDebug = element.GetAttribute(CONFIG_DEBUG_ATTR);
if (confDebug.Length > 0 && confDebug != "null") {
LogLog.Warn("XmlHierarchyConfigurator: The \"" + CONFIG_DEBUG_ATTR + "\" attribute is deprecated.");
LogLog.Warn("XmlHierarchyConfigurator: Use the \"" + INTERNAL_DEBUG_ATTR + "\" attribute instead.");
LogLog.InternalDebugging = OptionConverter.ToBoolean(confDebug, true);
}
}
// Default mode is merge
ConfigUpdateMode configUpdateMode = ConfigUpdateMode.Merge;
// Look for the config update attribute
string configUpdateModeAttribute = element.GetAttribute(CONFIG_UPDATE_MODE_ATTR);
if (configUpdateModeAttribute != null && configUpdateModeAttribute.Length > 0) {
// Parse the attribute
try {
configUpdateMode = (ConfigUpdateMode)OptionConverter.ConvertStringTo(typeof(ConfigUpdateMode), configUpdateModeAttribute);
}
catch {
LogLog.Error("XmlHierarchyConfigurator: Invalid " + CONFIG_UPDATE_MODE_ATTR + " attribute value [" + configUpdateModeAttribute + "]");
}
}
// IMPL: The IFormatProvider argument to Enum.ToString() is deprecated in .NET 2.0
LogLog.Debug("XmlHierarchyConfigurator: Configuration update mode [" + configUpdateMode.ToString() + "].");
// Only reset configuration if overwrite flag specified
if (configUpdateMode == ConfigUpdateMode.Overwrite) {
// Reset to original unset configuration
m_hierarchy.ResetConfiguration();
LogLog.Debug("XmlHierarchyConfigurator: Configuration reset before reading config.");
}
// Try to retrieve the environment variables
try {
m_environmentVariables = System.Environment.GetEnvironmentVariables();
}
catch (System.Security.SecurityException) {
m_environmentVariables = null;
// This security exception will occur if the caller does not have
// unrestricted environment permission. If this occurs the expansion
// will be skipped with the following warning message.
LogLog.Debug("XmlHierarchyConfigurator: Security exception while trying to expand environment variables. Error Ignored. No Expansion.");
}
/* Building Appender objects, placing them in a local namespace
for future reference */
/* Process all the top level elements */
foreach (XmlNode currentNode in element.ChildNodes) {
if (currentNode.NodeType == XmlNodeType.Element) {
XmlElement currentElement = (XmlElement)currentNode;
if (currentElement.LocalName == LOGGER_TAG) {
ParseLogger(currentElement);
}
else if (currentElement.LocalName == CATEGORY_TAG) {
// TODO: deprecated use of category
ParseLogger(currentElement);
}
else if (currentElement.LocalName == ROOT_TAG) {
ParseRoot(currentElement);
}
else if (currentElement.LocalName == RENDERER_TAG) {
ParseRenderer(currentElement);
}
else if (currentElement.LocalName == APPENDER_TAG) {
// We ignore appenders in this pass. They will
// be found and loaded if they are referenced.
}
else {
// Read the param tags and set properties on the hierarchy
SetParameter(currentElement, m_hierarchy);
}
}
}
// Lastly set the hierarchy threshold
string thresholdStr = element.GetAttribute(THRESHOLD_ATTR);
LogLog.Debug("XmlHierarchyConfigurator: Hierarchy Threshold [" + thresholdStr + "]");
if (thresholdStr.Length > 0 && thresholdStr != "null") {
Level thresholdLevel = (Level)ConvertStringTo(typeof(Level), thresholdStr);
if (thresholdLevel != null) {
m_hierarchy.Threshold = thresholdLevel;
}
else {
LogLog.Warn("XmlHierarchyConfigurator: Unable to set hierarchy threshold using value [" + thresholdStr + "] (with acceptable conversion types)");
}
}
// Done reading config
}
#endregion Public Instance Methods
#region Protected Instance Methods
/// <summary>
/// Parse appenders by IDREF.
/// </summary>
/// <param name="appenderRef">The appender ref element.</param>
/// <returns>The instance of the appender that the ref refers to.</returns>
/// <remarks>
/// <para>
/// Parse an XML element that represents an appender and return
/// the appender.
/// </para>
/// </remarks>
protected IAppender FindAppenderByReference(XmlElement appenderRef) {
string appenderName = appenderRef.GetAttribute(REF_ATTR);
IAppender appender = (IAppender)m_appenderBag[appenderName];
if (appender != null) {
return appender;
}
else {
// Find the element with that id
XmlElement element = null;
if (appenderName != null && appenderName.Length > 0) {
foreach (XmlElement curAppenderElement in appenderRef.OwnerDocument.GetElementsByTagName(APPENDER_TAG)) {
if (curAppenderElement.GetAttribute("name") == appenderName) {
element = curAppenderElement;
break;
}
}
}
if (element == null) {
LogLog.Error("XmlHierarchyConfigurator: No appender named [" + appenderName + "] could be found.");
return null;
}
else {
appender = ParseAppender(element);
if (appender != null) {
m_appenderBag[appenderName] = appender;
}
return appender;
}
}
}
/// <summary>
/// Parses an appender element.
/// </summary>
/// <param name="appenderElement">The appender element.</param>
/// <returns>The appender instance or <c>null</c> when parsing failed.</returns>
/// <remarks>
/// <para>
/// Parse an XML element that represents an appender and return
/// the appender instance.
/// </para>
/// </remarks>
protected IAppender ParseAppender(XmlElement appenderElement) {
string appenderName = appenderElement.GetAttribute(NAME_ATTR);
string typeName = appenderElement.GetAttribute(TYPE_ATTR);
LogLog.Debug("XmlHierarchyConfigurator: Loading Appender [" + appenderName + "] type: [" + typeName + "]");
try {
IAppender appender = (IAppender)Activator.CreateInstance(SystemInfo.GetTypeFromString(typeName, true, true));
appender.Name = appenderName;
foreach (XmlNode currentNode in appenderElement.ChildNodes) {
/* We're only interested in Elements */
if (currentNode.NodeType == XmlNodeType.Element) {
XmlElement currentElement = (XmlElement)currentNode;
// Look for the appender ref tag
if (currentElement.LocalName == APPENDER_REF_TAG) {
string refName = currentElement.GetAttribute(REF_ATTR);
IAppenderAttachable appenderContainer = appender as IAppenderAttachable;
if (appenderContainer != null) {
LogLog.Debug("XmlHierarchyConfigurator: Attaching appender named [" + refName + "] to appender named [" + appender.Name + "].");
IAppender referencedAppender = FindAppenderByReference(currentElement);
if (referencedAppender != null) {
appenderContainer.AddAppender(referencedAppender);
}
}
else {
LogLog.Error("XmlHierarchyConfigurator: Requesting attachment of appender named [" + refName + "] to appender named [" + appender.Name + "] which does not implement log4net.Core.IAppenderAttachable.");
}
}
else {
// For all other tags we use standard set param method
SetParameter(currentElement, appender);
}
}
}
IOptionHandler optionHandler = appender as IOptionHandler;
if (optionHandler != null) {
optionHandler.ActivateOptions();
}
LogLog.Debug("XmlHierarchyConfigurator: Created Appender [" + appenderName + "]");
return appender;
}
catch (Exception ex) {
// Yes, it's ugly. But all exceptions point to the same problem: we can't create an Appender
LogLog.Error("XmlHierarchyConfigurator: Could not create Appender [" + appenderName + "] of type [" + typeName + "]. Reported error follows.", ex);
return null;
}
}
/// <summary>
/// Parses a logger element.
/// </summary>
/// <param name="loggerElement">The logger element.</param>
/// <remarks>
/// <para>
/// Parse an XML element that represents a logger.
/// </para>
/// </remarks>
protected void ParseLogger(XmlElement loggerElement) {
// Create a new log4net.Logger object from the <logger> element.
string loggerName = loggerElement.GetAttribute(NAME_ATTR);
LogLog.Debug("XmlHierarchyConfigurator: Retrieving an instance of log4net.Repository.Logger for logger [" + loggerName + "].");
Logger log = m_hierarchy.GetLogger(loggerName) as Logger;
// Setting up a logger needs to be an atomic operation, in order
// to protect potential log operations while logger
// configuration is in progress.
lock (log) {
bool additivity = OptionConverter.ToBoolean(loggerElement.GetAttribute(ADDITIVITY_ATTR), true);
LogLog.Debug("XmlHierarchyConfigurator: Setting [" + log.Name + "] additivity to [" + additivity + "].");
log.Additivity = additivity;
ParseChildrenOfLoggerElement(loggerElement, log, false);
}
}
/// <summary>
/// Parses the root logger element.
/// </summary>
/// <param name="rootElement">The root element.</param>
/// <remarks>
/// <para>
/// Parse an XML element that represents the root logger.
/// </para>
/// </remarks>
protected void ParseRoot(XmlElement rootElement) {
Logger root = m_hierarchy.Root;
// logger configuration needs to be atomic
lock (root) {
ParseChildrenOfLoggerElement(rootElement, root, true);
}
}
/// <summary>
/// Parses the children of a logger element.
/// </summary>
/// <param name="catElement">The category element.</param>
/// <param name="log">The logger instance.</param>
/// <param name="isRoot">Flag to indicate if the logger is the root logger.</param>
/// <remarks>
/// <para>
/// Parse the child elements of a &lt;logger&gt; element.
/// </para>
/// </remarks>
protected void ParseChildrenOfLoggerElement(XmlElement catElement, Logger log, bool isRoot) {
// Remove all existing appenders from log. They will be
// reconstructed if need be.
log.RemoveAllAppenders();
foreach (XmlNode currentNode in catElement.ChildNodes) {
if (currentNode.NodeType == XmlNodeType.Element) {
XmlElement currentElement = (XmlElement)currentNode;
if (currentElement.LocalName == APPENDER_REF_TAG) {
IAppender appender = FindAppenderByReference(currentElement);
string refName = currentElement.GetAttribute(REF_ATTR);
if (appender != null) {
LogLog.Debug("XmlHierarchyConfigurator: Adding appender named [" + refName + "] to logger [" + log.Name + "].");
log.AddAppender(appender);
}
else {
LogLog.Error("XmlHierarchyConfigurator: Appender named [" + refName + "] not found.");
}
}
else if (currentElement.LocalName == LEVEL_TAG || currentElement.LocalName == PRIORITY_TAG) {
ParseLevel(currentElement, log, isRoot);
}
else {
SetParameter(currentElement, log);
}
}
}
IOptionHandler optionHandler = log as IOptionHandler;
if (optionHandler != null) {
optionHandler.ActivateOptions();
}
}
/// <summary>
/// Parses an object renderer.
/// </summary>
/// <param name="element">The renderer element.</param>
/// <remarks>
/// <para>
/// Parse an XML element that represents a renderer.
/// </para>
/// </remarks>
protected void ParseRenderer(XmlElement element) {
string renderingClassName = element.GetAttribute(RENDERING_TYPE_ATTR);
string renderedClassName = element.GetAttribute(RENDERED_TYPE_ATTR);
LogLog.Debug("XmlHierarchyConfigurator: Rendering class [" + renderingClassName + "], Rendered class [" + renderedClassName + "].");
IObjectRenderer renderer = (IObjectRenderer)OptionConverter.InstantiateByClassName(renderingClassName, typeof(IObjectRenderer), null);
if (renderer == null) {
LogLog.Error("XmlHierarchyConfigurator: Could not instantiate renderer [" + renderingClassName + "].");
return;
}
else {
try {
m_hierarchy.RendererMap.Put(SystemInfo.GetTypeFromString(renderedClassName, true, true), renderer);
}
catch (Exception e) {
LogLog.Error("XmlHierarchyConfigurator: Could not find class [" + renderedClassName + "].", e);
}
}
}
/// <summary>
/// Parses a level element.
/// </summary>
/// <param name="element">The level element.</param>
/// <param name="log">The logger object to set the level on.</param>
/// <param name="isRoot">Flag to indicate if the logger is the root logger.</param>
/// <remarks>
/// <para>
/// Parse an XML element that represents a level.
/// </para>
/// </remarks>
protected void ParseLevel(XmlElement element, Logger log, bool isRoot) {
string loggerName = log.Name;
if (isRoot) {
loggerName = "root";
}
string levelStr = element.GetAttribute(VALUE_ATTR);
LogLog.Debug("XmlHierarchyConfigurator: Logger [" + loggerName + "] Level string is [" + levelStr + "].");
if (INHERITED == levelStr) {
if (isRoot) {
LogLog.Error("XmlHierarchyConfigurator: Root level cannot be inherited. Ignoring directive.");
}
else {
LogLog.Debug("XmlHierarchyConfigurator: Logger [" + loggerName + "] level set to inherit from parent.");
log.Level = null;
}
}
else {
log.Level = log.Hierarchy.LevelMap[levelStr];
if (log.Level == null) {
LogLog.Error("XmlHierarchyConfigurator: Undefined level [" + levelStr + "] on Logger [" + loggerName + "].");
}
else {
LogLog.Debug("XmlHierarchyConfigurator: Logger [" + loggerName + "] level set to [name=\"" + log.Level.Name + "\",value=" + log.Level.Value + "].");
}
}
}
/// <summary>
/// Sets a parameter on an object.
/// </summary>
/// <param name="element">The parameter element.</param>
/// <param name="target">The object to set the parameter on.</param>
/// <remarks>
/// The parameter name must correspond to a writable property
/// on the object. The value of the parameter is a string,
/// therefore this function will attempt to set a string
/// property first. If unable to set a string property it
/// will inspect the property and its argument type. It will
/// attempt to call a static method called <c>Parse</c> on the
/// type of the property. This method will take a single
/// string argument and return a value that can be used to
/// set the property.
/// </remarks>
protected void SetParameter(XmlElement element, object target) {
// Get the property name
string name = element.GetAttribute(NAME_ATTR);
// If the name attribute does not exist then use the name of the element
if (element.LocalName != PARAM_TAG || name == null || name.Length == 0) {
name = element.LocalName;
}
// Look for the property on the target object
Type targetType = target.GetType();
Type propertyType = null;
PropertyInfo propInfo = null;
MethodInfo methInfo = null;
// Try to find a writable property
propInfo = targetType.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase);
if (propInfo != null && propInfo.CanWrite) {
// found a property
propertyType = propInfo.PropertyType;
}
else {
propInfo = null;
// look for a method with the signature Add<property>(type)
methInfo = FindMethodInfo(targetType, name);
if (methInfo != null) {
propertyType = methInfo.GetParameters()[0].ParameterType;
}
}
if (propertyType == null) {
LogLog.Error("XmlHierarchyConfigurator: Cannot find Property [" + name + "] to set object on [" + target.ToString() + "]");
}
else {
string propertyValue = null;
if (element.GetAttributeNode(VALUE_ATTR) != null) {
propertyValue = element.GetAttribute(VALUE_ATTR);
}
else if (element.HasChildNodes) {
// Concatenate the CDATA and Text nodes together
foreach (XmlNode childNode in element.ChildNodes) {
if (childNode.NodeType == XmlNodeType.CDATA || childNode.NodeType == XmlNodeType.Text) {
if (propertyValue == null) {
propertyValue = childNode.InnerText;
}
else {
propertyValue += childNode.InnerText;
}
}
}
}
if (propertyValue != null) {
if (m_environmentVariables != null) {
// Expand environment variables in the string.
propertyValue = OptionConverter.SubstituteVariables(propertyValue, m_environmentVariables);
}
Type parsedObjectConversionTargetType = null;
// Check if a specific subtype is specified on the element using the 'type' attribute
string subTypeString = element.GetAttribute(TYPE_ATTR);
if (subTypeString != null && subTypeString.Length > 0) {
// Read the explicit subtype
try {
Type subType = SystemInfo.GetTypeFromString(subTypeString, true, true);
LogLog.Debug("XmlHierarchyConfigurator: Parameter [" + name + "] specified subtype [" + subType.FullName + "]");
if (!propertyType.IsAssignableFrom(subType)) {
// Check if there is an appropriate type converter
if (OptionConverter.CanConvertTypeTo(subType, propertyType)) {
// Must re-convert to the real property type
parsedObjectConversionTargetType = propertyType;
// Use sub type as intermediary type
propertyType = subType;
}
else {
LogLog.Error("XmlHierarchyConfigurator: Subtype [" + subType.FullName + "] set on [" + name + "] is not a subclass of property type [" + propertyType.FullName + "] and there are no acceptable type conversions.");
}
}
else {
// The subtype specified is found and is actually a subtype of the property
// type, therefore we can switch to using this type.
propertyType = subType;
}
}
catch (Exception ex) {
LogLog.Error("XmlHierarchyConfigurator: Failed to find type [" + subTypeString + "] set on [" + name + "]", ex);
}
}
// Now try to convert the string value to an acceptable type
// to pass to this property.
object convertedValue = ConvertStringTo(propertyType, propertyValue);
// Check if we need to do an additional conversion
if (convertedValue != null && parsedObjectConversionTargetType != null) {
LogLog.Debug("XmlHierarchyConfigurator: Performing additional conversion of value from [" + convertedValue.GetType().Name + "] to [" + parsedObjectConversionTargetType.Name + "]");
convertedValue = OptionConverter.ConvertTypeTo(convertedValue, parsedObjectConversionTargetType);
}
if (convertedValue != null) {
if (propInfo != null) {
// Got a converted result
LogLog.Debug("XmlHierarchyConfigurator: Setting Property [" + propInfo.Name + "] to " + convertedValue.GetType().Name + " value [" + convertedValue.ToString() + "]");
try {
// Pass to the property
propInfo.SetValue(target, convertedValue, BindingFlags.SetProperty, null, null, CultureInfo.InvariantCulture);
}
catch (TargetInvocationException targetInvocationEx) {
LogLog.Error("XmlHierarchyConfigurator: Failed to set parameter [" + propInfo.Name + "] on object [" + target + "] using value [" + convertedValue + "]", targetInvocationEx.InnerException);
}
}
else if (methInfo != null) {
// Got a converted result
LogLog.Debug("XmlHierarchyConfigurator: Setting Collection Property [" + methInfo.Name + "] to " + convertedValue.GetType().Name + " value [" + convertedValue.ToString() + "]");
try {
// Pass to the property
methInfo.Invoke(target, BindingFlags.InvokeMethod, null, new object[] { convertedValue }, CultureInfo.InvariantCulture);
}
catch (TargetInvocationException targetInvocationEx) {
LogLog.Error("XmlHierarchyConfigurator: Failed to set parameter [" + name + "] on object [" + target + "] using value [" + convertedValue + "]", targetInvocationEx.InnerException);
}
}
}
else {
LogLog.Warn("XmlHierarchyConfigurator: Unable to set property [" + name + "] on object [" + target + "] using value [" + propertyValue + "] (with acceptable conversion types)");
}
}
else {
object createdObject = null;
if (propertyType == typeof(string) && !HasAttributesOrElements(element)) {
// If the property is a string and the element is empty (no attributes
// or child elements) then we special case the object value to an empty string.
// This is necessary because while the String is a class it does not have
// a default constructor that creates an empty string, which is the behavior
// we are trying to simulate and would be expected from CreateObjectFromXml
createdObject = "";
}
else {
// No value specified
Type defaultObjectType = null;
if (IsTypeConstructible(propertyType)) {
defaultObjectType = propertyType;
}
createdObject = CreateObjectFromXml(element, defaultObjectType, propertyType);
}
if (createdObject == null) {
LogLog.Error("XmlHierarchyConfigurator: Failed to create object to set param: " + name);
}
else {
if (propInfo != null) {
// Got a converted result
LogLog.Debug("XmlHierarchyConfigurator: Setting Property [" + propInfo.Name + "] to object [" + createdObject + "]");
try {
// Pass to the property
propInfo.SetValue(target, createdObject, BindingFlags.SetProperty, null, null, CultureInfo.InvariantCulture);
}
catch (TargetInvocationException targetInvocationEx) {
LogLog.Error("XmlHierarchyConfigurator: Failed to set parameter [" + propInfo.Name + "] on object [" + target + "] using value [" + createdObject + "]", targetInvocationEx.InnerException);
}
}
else if (methInfo != null) {
// Got a converted result
LogLog.Debug("XmlHierarchyConfigurator: Setting Collection Property [" + methInfo.Name + "] to object [" + createdObject + "]");
try {
// Pass to the property
methInfo.Invoke(target, BindingFlags.InvokeMethod, null, new object[] { createdObject }, CultureInfo.InvariantCulture);
}
catch (TargetInvocationException targetInvocationEx) {
LogLog.Error("XmlHierarchyConfigurator: Failed to set parameter [" + methInfo.Name + "] on object [" + target + "] using value [" + createdObject + "]", targetInvocationEx.InnerException);
}
}
}
}
}
}
/// <summary>
/// Test if an element has no attributes or child elements
/// </summary>
/// <param name="element">the element to inspect</param>
/// <returns><c>true</c> if the element has any attributes or child elements, <c>false</c> otherwise</returns>
private bool HasAttributesOrElements(XmlElement element) {
foreach (XmlNode node in element.ChildNodes) {
if (node.NodeType == XmlNodeType.Attribute || node.NodeType == XmlNodeType.Element) {
return true;
}
}
return false;
}
/// <summary>
/// Test if a <see cref="Type"/> is constructible with <c>Activator.CreateInstance</c>.
/// </summary>
/// <param name="type">the type to inspect</param>
/// <returns><c>true</c> if the type is creatable using a default constructor, <c>false</c> otherwise</returns>
private static bool IsTypeConstructible(Type type) {
if (type.IsClass && !type.IsAbstract) {
ConstructorInfo defaultConstructor = type.GetConstructor(new Type[0]);
if (defaultConstructor != null && !defaultConstructor.IsAbstract && !defaultConstructor.IsPrivate) {
return true;
}
}
return false;
}
/// <summary>
/// Look for a method on the <paramref name="targetType"/> that matches the <paramref name="name"/> supplied
/// </summary>
/// <param name="targetType">the type that has the method</param>
/// <param name="name">the name of the method</param>
/// <returns>the method info found</returns>
/// <remarks>
/// <para>
/// The method must be a public instance method on the <paramref name="targetType"/>.
/// The method must be named <paramref name="name"/> or "Add" followed by <paramref name="name"/>.
/// The method must take a single parameter.
/// </para>
/// </remarks>
private MethodInfo FindMethodInfo(Type targetType, string name) {
string requiredMethodNameA = name;
string requiredMethodNameB = "Add" + name;
MethodInfo[] methods = targetType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (MethodInfo methInfo in methods) {
if (!methInfo.IsStatic) {
if (string.Compare(methInfo.Name, requiredMethodNameA, true, System.Globalization.CultureInfo.InvariantCulture) == 0 ||
string.Compare(methInfo.Name, requiredMethodNameB, true, System.Globalization.CultureInfo.InvariantCulture) == 0) {
// Found matching method name
// Look for version with one arg only
System.Reflection.ParameterInfo[] methParams = methInfo.GetParameters();
if (methParams.Length == 1) {
return methInfo;
}
}
}
}
return null;
}
/// <summary>
/// Converts a string value to a target type.
/// </summary>
/// <param name="type">The type of object to convert the string to.</param>
/// <param name="value">The string value to use as the value of the object.</param>
/// <returns>
/// <para>
/// An object of type <paramref name="type"/> with value <paramref name="value"/> or
/// <c>null</c> when the conversion could not be performed.
/// </para>
/// </returns>
protected object ConvertStringTo(Type type, string value) {
// Hack to allow use of Level in property
if (typeof(Level) == type) {
// Property wants a level
Level levelValue = m_hierarchy.LevelMap[value];
if (levelValue == null) {
LogLog.Error("XmlHierarchyConfigurator: Unknown Level Specified [" + value + "]");
}
return levelValue;
}
return OptionConverter.ConvertStringTo(type, value);
}
/// <summary>
/// Creates an object as specified in XML.
/// </summary>
/// <param name="element">The XML element that contains the definition of the object.</param>
/// <param name="defaultTargetType">The object type to use if not explicitly specified.</param>
/// <param name="typeConstraint">The type that the returned object must be or must inherit from.</param>
/// <returns>The object or <c>null</c></returns>
/// <remarks>
/// <para>
/// Parse an XML element and create an object instance based on the configuration
/// data.
/// </para>
/// <para>
/// The type of the instance may be specified in the XML. If not
/// specified then the <paramref name="defaultTargetType"/> is used
/// as the type. However the type is specified it must support the
/// <paramref name="typeConstraint"/> type.
/// </para>
/// </remarks>
protected object CreateObjectFromXml(XmlElement element, Type defaultTargetType, Type typeConstraint) {
Type objectType = null;
// Get the object type
string objectTypeString = element.GetAttribute(TYPE_ATTR);
if (objectTypeString == null || objectTypeString.Length == 0) {
if (defaultTargetType == null) {
LogLog.Error("XmlHierarchyConfigurator: Object type not specified. Cannot create object of type [" + typeConstraint.FullName + "]. Missing Value or Type.");
return null;
}
else {
// Use the default object type
objectType = defaultTargetType;
}
}
else {
// Read the explicit object type
try {
objectType = SystemInfo.GetTypeFromString(objectTypeString, true, true);
}
catch (Exception ex) {
LogLog.Error("XmlHierarchyConfigurator: Failed to find type [" + objectTypeString + "]", ex);
return null;
}
}
bool requiresConversion = false;
// Got the object type. Check that it meets the typeConstraint
if (typeConstraint != null) {
if (!typeConstraint.IsAssignableFrom(objectType)) {
// Check if there is an appropriate type converter
if (OptionConverter.CanConvertTypeTo(objectType, typeConstraint)) {
requiresConversion = true;
}
else {
LogLog.Error("XmlHierarchyConfigurator: Object type [" + objectType.FullName + "] is not assignable to type [" + typeConstraint.FullName + "]. There are no acceptable type conversions.");
return null;
}
}
}
// Create using the default constructor
object createdObject = null;
try {
createdObject = Activator.CreateInstance(objectType);
}
catch (Exception createInstanceEx) {
LogLog.Error("XmlHierarchyConfigurator: Failed to construct object of type [" + objectType.FullName + "] Exception: " + createInstanceEx.ToString());
}
// Set any params on object
foreach (XmlNode currentNode in element.ChildNodes) {
if (currentNode.NodeType == XmlNodeType.Element) {
SetParameter((XmlElement)currentNode, createdObject);
}
}
// Check if we need to call ActivateOptions
IOptionHandler optionHandler = createdObject as IOptionHandler;
if (optionHandler != null) {
optionHandler.ActivateOptions();
}
// Ok object should be initialized
if (requiresConversion) {
// Convert the object type
return OptionConverter.ConvertTypeTo(createdObject, typeConstraint);
}
else {
// The object is of the correct type
return createdObject;
}
}
#endregion Protected Instance Methods
#region Private Constants
// String constants used while parsing the XML data
private const string CONFIGURATION_TAG = "log4net";
private const string RENDERER_TAG = "renderer";
private const string APPENDER_TAG = "appender";
private const string APPENDER_REF_TAG = "appender-ref";
private const string PARAM_TAG = "param";
// TODO: Deprecate use of category tags
private const string CATEGORY_TAG = "category";
// TODO: Deprecate use of priority tag
private const string PRIORITY_TAG = "priority";
private const string LOGGER_TAG = "logger";
private const string NAME_ATTR = "name";
private const string TYPE_ATTR = "type";
private const string VALUE_ATTR = "value";
private const string ROOT_TAG = "root";
private const string LEVEL_TAG = "level";
private const string REF_ATTR = "ref";
private const string ADDITIVITY_ATTR = "additivity";
private const string THRESHOLD_ATTR = "threshold";
private const string CONFIG_DEBUG_ATTR = "configDebug";
private const string INTERNAL_DEBUG_ATTR = "debug";
private const string CONFIG_UPDATE_MODE_ATTR = "update";
private const string RENDERING_TYPE_ATTR = "renderingClass";
private const string RENDERED_TYPE_ATTR = "renderedClass";
// flag used on the level element
private const string INHERITED = "inherited";
#endregion Private Constants
#region Private Instance Fields
/// <summary>
/// key: appenderName, value: appender.
/// </summary>
private Hashtable m_appenderBag;
/// <summary>
/// The Hierarchy being configured.
/// </summary>
private readonly Hierarchy m_hierarchy;
/// <summary>
/// The snapshot of the environment variables at configuration time, or null if an error has occured during querying them.
/// </summary>
private IDictionary m_environmentVariables;
#endregion Private Instance Fields
}
}

View File

@@ -188,6 +188,10 @@
<Compile Include="Localization\Services\DefaultLocalizedStringManager.cs" />
<Compile Include="Localization\Services\ILocalizedStringManager.cs" />
<Compile Include="Logging\OrchardFileAppender.cs" />
<Compile Include="Logging\OrchardLog4netFactory.cs" />
<Compile Include="Logging\OrchardLog4netLogger.cs" />
<Compile Include="Logging\OrchardXmlConfigurator.cs" />
<Compile Include="Logging\OrchardXmlHierarchyConfigurator.cs" />
<Compile Include="Messaging\Services\DefaultMessageManager.cs" />
<Compile Include="Mvc\Extensions\RouteExtension.cs" />
<Compile Include="Mvc\HttpContextWorkContext.cs" />