123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Text;
- using System.Xml;
- using System.Xml.Schema;
- namespace Wayne.Lib.Log
- {
- /// <summary>
- /// 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.
- /// </summary>
- 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<LogWriter> LogWriters = new List<LogWriter>();
- 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<EntityCategory, LogWriter[]> logWriterPreparedDict = new Dictionary<EntityCategory, LogWriter[]>();
- private readonly Dictionary<IIdentifiableEntity, Dictionary<object, EntityCategory>> cachedEntityCategoryDict = new Dictionary<IIdentifiableEntity, Dictionary<object, EntityCategory>>();
- private readonly List<LogWriter> createdLogWriters = new List<LogWriter>();
- private readonly List<LogConfigWrapper> logConfigWrappers = new List<LogConfigWrapper>();
- private readonly System.Threading.Timer cleaningTimer;
- #endregion
- #region Construction & Finalizer
- /// <summary>
- /// Initializes a new instance of the LogConfig class.
- /// </summary>
- 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.
- }
- /// <summary>
- /// Finalizer.
- /// </summary>
- ~LogConfig()
- {
- Dispose(false);
- }
- #endregion
- #region IDisposable Members
- /// <summary>
- /// Disposes the LogConfig's resources.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- /// <summary>
- /// Internal dispose method.
- /// </summary>
- /// <param name="disposing"></param>
- 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
- /// <summary>
- /// Refreshes the log configuration from the configuration xml file.
- /// </summary>
- /// <param name="configFiles">A list of config files (the file is in the form of a string[]).</param>
- /// <param name="leftoverLogLinesConfigOutputList"></param>
- /// <param name="leftoverLogEntitiesConfigOutputList"></param>
- /// <exception cref="Exception">If no log configuration file is specified.</exception>
- /// <exception cref="Exception">If the specified log configuration file is missing.</exception>
- /// <exception cref="Exception">If the log configuration file is invalid.</exception>
- internal void Refresh(List<string[]> 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));
- }
- }
- }
- }
- }
- }
- /// <summary>
- /// Closes all LogWriters. NOTE! Must be called under the lock of configReadingLock!
- /// </summary>
- /// <param name="reason"></param>
- 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
- /// <summary>
- /// Returns a list of log writers to be used with the specified identifiable entity.
- /// </summary>
- /// <param name="entityCategory"></param>
- /// <returns></returns>
- internal LogWriter[] GetLogWriters(EntityCategory entityCategory)
- {
- if (disposed)
- return new LogWriter[0];
- LogWriter[] cachedLogWriters;
- lock (configReadingLock)
- {
- if (!logWriterPreparedDict.TryGetValue(entityCategory, out cachedLogWriters))
- {
- List<LogWriter> logWriterList = new List<LogWriter>();
- 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;
- }
- /// <summary>
- /// Get all active log writers.
- /// </summary>
- /// <returns></returns>
- internal LogWriter[] GetLogWriters()
- {
- if (disposed)
- return new LogWriter[0];
- lock (configReadingLock)
- {
- return createdLogWriters.ToArray();
- }
- }
- #endregion
- #region Methods: GetLogWriter
- /// <summary>
- /// Get (or creates) the appropriate logwriter depending on the output type and the output parameters.
- /// </summary>
- 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<object, EntityCategory> entityCategoriesDict;
- //if (cachedEntityCategoryDict.TryGetValue(entity, out entityCategoriesDict))
- //{
- //if (!entityCategoriesDict.TryGetValue(category, out result))
- //{
- result = new EntityCategory(entity, category);
- // entityCategoriesDict[category] = result;
- //}
- //}
- //else
- //{
- // result = new EntityCategory(entity, category);
- // entityCategoriesDict = new Dictionary<object, EntityCategory>();
- // entityCategoriesDict[category] = result;
- // cachedEntityCategoryDict[entity] = entityCategoriesDict;
- //}
- }
- result.Touch();
- return result;
- }
- #endregion
- #region Methods: Cleaning of internal lists
- private void PerformListCleaning(object o)
- {
- lock (configReadingLock)
- {
- DateTime oldestAllowedTouch = DateTime.Now.AddMinutes(-5); // 5 minutes is the longest time an untouched EntityCategory is cached in the log library.
- #region Clean the logWriterPreparedDict and the cachedEntityCategoryDict.
- List<EntityCategory> entityCategoriesToRemove = new List<EntityCategory>();
- foreach (EntityCategory entityCategory in logWriterPreparedDict.Keys)
- {
- if (entityCategory.LastTouched < oldestAllowedTouch)
- entityCategoriesToRemove.Add(entityCategory);
- }
- //Remove from the dict.
- foreach (EntityCategory entityCategoryToRemove in entityCategoriesToRemove)
- {
- logWriterPreparedDict.Remove(entityCategoryToRemove);
- Dictionary<object, EntityCategory> entityCategoriesDict;
- if (cachedEntityCategoryDict.TryGetValue(entityCategoryToRemove.Entity, out entityCategoriesDict))
- {
- entityCategoriesDict.Remove(entityCategoryToRemove.Category);
- if (entityCategoriesDict.Count == 0)
- cachedEntityCategoryDict.Remove(entityCategoryToRemove.Entity);
- }
- }
- #endregion
- foreach (LogWriter logWriter in createdLogWriters)
- logWriter.PerformListCleaning(oldestAllowedTouch);
- }
- }
- #endregion
- #region Methods: UnregisterEntity
- internal void UnregisterEntity(IIdentifiableEntity entity)
- {
- lock (configReadingLock)
- {
- #region Clean the logWriterPreparedDict
- List<EntityCategory> entityCategoriesToRemove = new List<EntityCategory>();
- foreach (EntityCategory entityCategory in logWriterPreparedDict.Keys)
- {
- if (IdentifiableEntity.Equals(entityCategory.Entity, entity))
- entityCategoriesToRemove.Add(entityCategory);
- }
- //Remove from the list.
- foreach (EntityCategory entityCategoryToRemove in entityCategoriesToRemove)
- logWriterPreparedDict.Remove(entityCategoryToRemove);
- #endregion
- foreach (LogWriter logWriter in createdLogWriters)
- logWriter.RemoveEntity(entity);
- }
- }
- #endregion
- }
- }
|