254 lines · 10.1 KB · C#
RawDownload
//******************************************************************************************************//  LoggerInternal.cs - Gbtc////  Copyright © 2016, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the Eclipse Public License -v 1.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.opensource.org/licenses/eclipse-1.0.php////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  10/24/2016 - Steven E. Chisholm//       Generated original version of source code. //       ////****************************************************************************************************** using System;using System.Collections.Concurrent;using System.Collections.Generic;using GSF.Collections;using GSF.Threading; namespace GSF.Diagnostics{    /// <summary>    /// The fundamental functionality of <see cref="Logger"/>.    /// </summary>    internal class LoggerInternal        : IDisposable    {        private bool m_disposing;        private readonly object m_syncRoot;        private readonly Dictionary<Type, LogPublisherInternal> m_typeIndexCache;        private readonly List<LogPublisherInternal> m_allPublishers;        private readonly List<NullableWeakReference> m_subscribers;        private readonly ConcurrentQueue<Tuple<LogMessage, LogPublisherInternal>> m_messages;        private readonly ScheduledTask m_routingTask;        private readonly ScheduledTask m_calculateRoutingTable;        public bool RoutingTablesValid { get; private set; }         /// <summary>        /// Creates a <see cref="LoggerInternal"/>.        /// </summary>        public LoggerInternal(out LoggerInternal loggerClass)        {            //Needed to set the member variable of Logger.             //This is because ScheduleTask will call Logger before Logger's static constructor is completed.            loggerClass = this;             m_syncRoot = new object();            m_typeIndexCache = new Dictionary<Type, LogPublisherInternal>();            m_allPublishers = new List<LogPublisherInternal>();            m_subscribers = new List<NullableWeakReference>();            m_messages = new ConcurrentQueue<Tuple<LogMessage, LogPublisherInternal>>();             // Since ScheduledTask calls ShutdownHandler, which calls Logger. This initialization method cannot occur            // until after the Logger static class has finished initializing.            m_calculateRoutingTable = new ScheduledTask();            m_calculateRoutingTable.Running += CalculateRoutingTable;            m_calculateRoutingTable.IgnoreShutdownEvent();            m_routingTask = new ScheduledTask(ThreadingMode.DedicatedForeground);            m_routingTask.Running += RoutingTask;            m_routingTask.IgnoreShutdownEvent();        }         /// <summary>        /// Recalculates the entire routing table on a separate thread.        /// </summary>        /// <param name="sender"></param>        /// <param name="e"></param>        private void CalculateRoutingTable(object sender, EventArgs<ScheduledTaskRunningReason> e)        {            lock (m_syncRoot)            {                //Some other thread won on the race condition.                if (RoutingTablesValid)                    return;                 var subscribers = new List<LogSubscriberInternal>(m_subscribers.Count);                m_subscribers.RemoveWhere(x =>                    {                        LogSubscriberInternal subscriber = (LogSubscriberInternal)x.Target;                        if (subscriber != null)                        {                            subscribers.Add(subscriber);                            return false;                        }                        return true;                    });                 foreach (var pub in m_allPublishers)                {                    CalculateRoutingTableForPublisherSync(subscribers, pub);                }                RoutingTablesValid = true;            }        }         /// <summary>        /// This method should be called with a lock on m_syncRoot        /// </summary>        private void CalculateRoutingTableForPublisherSync(List<LogSubscriberInternal> subscribers, LogPublisherInternal publisher)        {            MessageAttributeFilterCollection filterCollection = new MessageAttributeFilterCollection();            foreach (var sub in subscribers)            {                filterCollection.Add(sub.GetSubscription(publisher), sub);            }            publisher.SubscriptionFilterCollection = filterCollection;        }         private void RoutingTask(object sender, EventArgs<ScheduledTaskRunningReason> e)        {            lock (m_syncRoot)            {                if (!RoutingTablesValid)                {                    CalculateRoutingTable(null, null);                }                 Tuple<LogMessage, LogPublisherInternal> messageTuple;                while (m_messages.TryDequeue(out messageTuple))                {                    var publisher = messageTuple.Item2;                    var message = messageTuple.Item1;                     foreach (var route in publisher.SubscriptionFilterCollection.Routes)                    {                        var filter = route.Item1;                        var subscriber = route.Item2.Target as LogSubscriberInternal;                         if (subscriber == null)                            RecalculateRoutingTable();                        else if (filter.IsSubscribedTo(message.LogMessageAttributes))                            subscriber.RaiseLogMessages(message);                    }                }            }        }         /// <summary>        /// Creates a <see cref="LogSubscriber"/> that can subscribe to log messages.        /// </summary>        /// <returns></returns>        public LogSubscriberInternal CreateSubscriber()        {            lock (m_syncRoot)            {                if (m_disposing)                    return LogSubscriberInternal.DisposedSubscriber;                var s = new LogSubscriberInternal(RecalculateRoutingTable);                m_subscribers.Add(s.Reference);                return s;            }        }         /// <summary>        /// Invalidates the current routing table.        /// </summary>        private void RecalculateRoutingTable()        {            RoutingTablesValid = false;            //Wait some time before recalculating the routing tables. This will be done automatically             //if a message is routed before the table is recalculated.            m_calculateRoutingTable.Start(10);        }         /// <summary>        /// Handles the routing of messages through the logging system.        /// </summary>        /// <param name="message">the message to route</param>        /// <param name="publisher">the publisher that is originating this message.</param>        public void OnNewMessage(LogMessage message, LogPublisherInternal publisher)        {            m_messages.Enqueue(Tuple.Create(message, publisher));            m_routingTask.Start(50); //Allow a 50ms delay for multiple logs to queue if in a burst period.        }         /// <summary>        /// Creates a type topic on a specified type.        /// </summary>        /// <param name="type">the type to create the topic from</param>        /// <returns></returns>        public LogPublisherInternal CreateType(Type type)        {            if ((object)type == null)                throw new ArgumentNullException(nameof(type));             LogPublisherInternal publisher;            lock (m_syncRoot)            {                if (!m_typeIndexCache.TryGetValue(type, out publisher))                {                    publisher = new LogPublisherInternal(this, type);                    m_typeIndexCache.Add(type, publisher);                    if (!m_disposing)                    {                        m_allPublishers.Add(publisher);                        var lst = new List<LogSubscriberInternal>();                        foreach (var logSubscriberInternal in m_subscribers)                        {                            LogSubscriberInternal target = logSubscriberInternal.Target as LogSubscriberInternal;                            if (target != null)                            {                                lst.Add(target);                            }                        }                        CalculateRoutingTableForPublisherSync(lst, publisher);                    }                }            }            return publisher;        }         /// <summary>        /// Gracefully terminate all message routing. Function blocks until all termination is successful.        /// </summary>        public void Dispose()        {            if (m_disposing)                return;             lock (m_syncRoot)            {                //Ensure that setting disposing is in a synchronized context.                m_disposing = true;            }             //These scheduled tasks block when dispose is called. Therefore do not put these in a lock on the base class.            m_calculateRoutingTable.Dispose();            m_routingTask.Dispose();             lock (m_syncRoot)            {                m_allPublishers.Clear();                foreach (var sub in m_subscribers)                {                    (sub.Target as LogSubscriberInternal)?.Dispose();                }                m_subscribers.Clear();            }        }      }}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t