using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Xml;
using System.Xml.Schema;
namespace Wayne.Lib.Log
{
///
/// The Log config handles the application-wide setup for the logging. It is read from a
/// XML file that should be in accordance with the LogConfig.xsd schema.
///
internal class LogConfig : IDisposable
{
#region Private Class LogConfigWrapper
private class LogConfigWrapper
{
public LogConfigWrapper(LogConfig logConfig, XmlNode logConfigNode, string ns)
{
LogConfigBuilder = new LogConfigBuilder(logConfigNode, ns);
foreach (LogConfigOutput output in LogConfigBuilder.Outputs)
LogWriters.Add(logConfig.GetLogWriter(LogConfigBuilder.Name, output));
}
public readonly LogConfigBuilder LogConfigBuilder;
public readonly List LogWriters = new List();
public override string ToString()
{
return LogConfigBuilder.ToString();
}
}
#endregion
#region Fields
private bool disposed;
private readonly string ns;
private readonly System.Xml.Schema.XmlSchema schema;
private readonly object configReadingLock = new object();
private readonly Dictionary logWriterPreparedDict = new Dictionary();
private readonly Dictionary> cachedEntityCategoryDict = new Dictionary>();
private readonly List createdLogWriters = new List();
private readonly List logConfigWrappers = new List();
private readonly System.Threading.Timer cleaningTimer;
#endregion
#region Construction & Finalizer
///
/// Initializes a new instance of the LogConfig class.
///
internal LogConfig()
{
// Read the LogConfig.xsd file from an embedded resource stream and validate it.
bool schemaValid = true;
StringBuilder errors = new StringBuilder();
using(var logConfigSchemaStream = Assemblies.GetManifestResourceStreamWithPartialName("LogConfig.xsd", System.Reflection.Assembly.GetExecutingAssembly()))
{
schema = XmlSchema.Read(logConfigSchemaStream,
(
delegate(object sender, ValidationEventArgs args)
{
Debug.WriteLine(args.Message);
if (args.Severity == XmlSeverityType.Error)
{
schemaValid = false;
errors.Append(args.Message);
errors.Append("\r\n");
}
}
));
}
if ((schema == null) || !schemaValid)
{
Debug.WriteLine("Schema LogConfig was not valid!!!!");
Debug.WriteLine(errors.ToString());
throw new LogException(LogExceptionType.CorruptConfigSchemaFile, errors.ToString());
}
ns = schema.TargetNamespace;
cleaningTimer = new System.Threading.Timer(PerformListCleaning, null, 120000, 120000); // Every second minute.
}
///
/// Finalizer.
///
~LogConfig()
{
Dispose(false);
}
#endregion
#region IDisposable Members
///
/// Disposes the LogConfig's resources.
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Internal dispose method.
///
///
private void Dispose(bool disposing)
{
if (!disposed)
{
disposed = true;
if (disposing)
{
lock (configReadingLock)
{
CloseAllLogWritersLocked("Shutting down logger.");
}
cleaningTimer.Dispose();
}
}
}
#endregion
#region Properties
public bool IsConfigured
{
get { return logConfigWrappers.Count > 0; }
}
#endregion
#region Methods: Refresh
///
/// Refreshes the log configuration from the configuration xml file.
///
/// A list of config files (the file is in the form of a string[]).
///
///
/// If no log configuration file is specified.
/// If the specified log configuration file is missing.
/// If the log configuration file is invalid.
internal void Refresh(List configFiles, LogConfigOutput[] leftoverLogLinesConfigOutputList, LogConfigOutput[] leftoverLogEntitiesConfigOutputList)
{
if (disposed)
return;
lock (configReadingLock)
{
CloseAllLogWritersLocked("Configuration Refreshed.");
logConfigWrappers.Clear();
foreach (string[] configFile in configFiles)
{
XmlDocument logConfigXml = new XmlDocument();
// Load and validate the config file.
bool valid = true;
try
{
logConfigXml.Schemas.Add(schema);
logConfigXml.LoadXml(string.Join("\r\n", configFile));
if (logConfigXml.DocumentElement != null)
{
XmlAttribute namespaceAttribute = logConfigXml.DocumentElement.Attributes["xmlns"];
if ((namespaceAttribute == null) || !namespaceAttribute.Value.Equals(schema.TargetNamespace, StringComparison.InvariantCultureIgnoreCase))
{
System.Diagnostics.Debug.WriteLine(string.Concat("The log config XML doesn't have the correct namespace. Wanted: \"", schema.TargetNamespace, "\", was: \"", namespaceAttribute.Value, "\""));
valid = false;
}
else
{
logConfigXml.Validate((
delegate(object sender, System.Xml.Schema.ValidationEventArgs args)
{
System.Diagnostics.Debug.WriteLine(args.Message);
valid = false;
}));
}
}
}
catch (XmlException) { }
if (!valid)
throw new LogException(LogExceptionType.InvalidLogConfigFile, string.Join("\r\n", configFile));
XmlNode logConfigFileNode = logConfigXml["LogConfigFile", ns];
if (logConfigFileNode != null)
{
foreach (XmlNode logConfigNode in logConfigFileNode.ChildNodes)
{
if ((logConfigNode.NodeType == XmlNodeType.Element) && (logConfigNode.LocalName == "LogConfig"))
{
XmlAttribute enabledAttribute = logConfigNode.Attributes["Enabled"];
if ((enabledAttribute != null) && !XmlConvert.ToBoolean(enabledAttribute.Value))
continue;
logConfigWrappers.Add(new LogConfigWrapper(this, logConfigNode, ns));
}
}
}
}
}
}
///
/// Closes all LogWriters. NOTE! Must be called under the lock of configReadingLock!
///
///
private void CloseAllLogWritersLocked(string reason)
{
// Empty the buffered log writers.
foreach (LogWriter logWriter in createdLogWriters)
logWriter.Dispose(reason);
// Clear all lists and dictionaries.
createdLogWriters.Clear();
logWriterPreparedDict.Clear();
cachedEntityCategoryDict.Clear();
}
#endregion
#region Methods: GetLogWriters
///
/// Returns a list of log writers to be used with the specified identifiable entity.
///
///
///
internal LogWriter[] GetLogWriters(EntityCategory entityCategory)
{
if (disposed)
return new LogWriter[0];
LogWriter[] cachedLogWriters;
lock (configReadingLock)
{
if (!logWriterPreparedDict.TryGetValue(entityCategory, out cachedLogWriters))
{
List logWriterList = new List();
foreach (LogConfigWrapper logConfigWrapper in logConfigWrappers)
{
DebugLogLevel debugLogLevel;
if (logConfigWrapper.LogConfigBuilder.MatchFilter(entityCategory, out debugLogLevel))
{
foreach (LogWriter logWriter in logConfigWrapper.LogWriters)
{
if (!logWriterList.Contains(logWriter))
{
logWriter.CacheFilterData(entityCategory, debugLogLevel);
logWriterList.Add(logWriter);
}
}
}
}
cachedLogWriters = logWriterList.ToArray();
if(!logWriterPreparedDict.ContainsKey(entityCategory))
{
logWriterPreparedDict.Add(entityCategory, cachedLogWriters);
}
}
}
return cachedLogWriters;
}
///
/// Get all active log writers.
///
///
internal LogWriter[] GetLogWriters()
{
if (disposed)
return new LogWriter[0];
lock (configReadingLock)
{
return createdLogWriters.ToArray();
}
}
#endregion
#region Methods: GetLogWriter
///
/// Get (or creates) the appropriate logwriter depending on the output type and the output parameters.
///
private LogWriter GetLogWriter(string logName, LogConfigOutput output)
{
if (disposed)
return null;
LogConfigTextFileOutput logConfigTextFileOutput = output as LogConfigTextFileOutput;
if (logConfigTextFileOutput != null)
{
foreach (LogWriter createdLogWriter in createdLogWriters)
{
TextFileLogWriter textFileLogWriter = createdLogWriter as TextFileLogWriter;
if ((textFileLogWriter != null) && textFileLogWriter.LogConfigTextFilePath.Equals(logConfigTextFileOutput.LogConfigTextFilePath))
return textFileLogWriter;
}
TextFileLogWriter newLogWriter = new TextFileLogWriter(logName, logConfigTextFileOutput);
createdLogWriters.Add(newLogWriter);
return newLogWriter;
}
LogConfigEventLogSubscriptionOutput logConfigEventLogSubscriptionOutput = output as LogConfigEventLogSubscriptionOutput;
if (logConfigEventLogSubscriptionOutput != null)
{
foreach (LogWriter createdLogWriter in createdLogWriters)
{
EventLogSubscriptionLogWriter eventSubscriberSupplierLogWriter = createdLogWriter as EventLogSubscriptionLogWriter;
if ((eventSubscriberSupplierLogWriter != null) &&
(eventSubscriberSupplierLogWriter.SubscriberId.Equals(logConfigEventLogSubscriptionOutput.SubscriberId)) &&
(eventSubscriberSupplierLogWriter.StorageType.Equals(logConfigEventLogSubscriptionOutput.StorageType))
)
{
return eventSubscriberSupplierLogWriter;
}
}
EventLogSubscriptionLogWriter newLogWriter = new EventLogSubscriptionLogWriter(logName, logConfigEventLogSubscriptionOutput);
createdLogWriters.Add(newLogWriter);
return newLogWriter;
}
LogConfigExternalLogWriterOutput logConfigExternalLogWriterOutput = output as LogConfigExternalLogWriterOutput;
if (logConfigExternalLogWriterOutput != null)
{
foreach (LogWriter createdLogWriter in createdLogWriters)
{
ExternalLogWriterWrapper externalLogWriter = createdLogWriter as ExternalLogWriterWrapper;
if ((externalLogWriter != null) &&
externalLogWriter.ExternalLogType.Equals(logConfigExternalLogWriterOutput.ExternalLogType, StringComparison.CurrentCultureIgnoreCase) &&
externalLogWriter.ExternalLogName.Equals(logConfigExternalLogWriterOutput.ExternalLogName, StringComparison.CurrentCultureIgnoreCase))
{
return externalLogWriter;
}
}
ExternalLogWriterWrapper newLogWriter = new ExternalLogWriterWrapper(logName, logConfigExternalLogWriterOutput);
createdLogWriters.Add(newLogWriter);
return newLogWriter;
}
return null;
}
#endregion
#region Methods: Find Filter Nodes
#endregion
#region Methods: GetEntityCategory
internal EntityCategory GetEntityCategory(IIdentifiableEntity entity, object category)
{
EntityCategory result = null;
if (entity == null)
entity = IdentifiableEntity.Empty;
if (category == null)
category = string.Empty;
lock (configReadingLock)
{
//Dictionary