//******************************************************************************************************
// LogPublisher.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;
// ReSharper disable InconsistentlySynchronizedField
namespace GSF.Diagnostics
{
///
/// A publisher of log messages.
///
///
/// and can be modified so messages that are generated
/// with this instance will have this data appended to the log message.
///
/// The user can either call one of the Publish overloads to lazily publish a message, or they
/// can register a message with RegisterEvent so calling this message will incur little overhead.
/// If registering an event, the user can check to determine if the log message can be skipped altogether.
/// Registering events also allows the user to specify the auto-suppression algorithm and the depth of the stack trace that will be recorded on a message being raised.
///
///
public class LogPublisher
{
///
/// Occurs when a new is ready to be published.
///
private LoggerInternal m_logger;
private readonly LogPublisherInternal m_publisherInstance;
private readonly MessageClass m_classification;
///
/// The stack messages that existed when this publisher was created. This can be modified by the user of this publisher.
/// Any messages that get published by this class will automatically have this data added to the log message.
///
public LogStackMessages InitialStackMessages;
///
/// The stack trace that existed when this publisher was created. This can be modified by the user of this publisher.
/// Any messages that get published by this class will automatically have this data added to the log message.
///
public LogStackTrace InitialStackTrace;
///
/// The maximum number of distinct events that this publisher can generate. (Default: 20)
///
///
/// Since message suppression and collection occurs at the event name level, it is important
/// to have only a few distinct message types. This is the limit so misapplication
/// of this publisher will not cause memory impacts on the system.
///
/// It is recommended to keep the event name as a fixed string and not report any other meta data
/// with the event.
///
// ReSharper disable once ConvertToConstant.Global
public int MaxDistinctEventPublisherCount { get; set; }
///
/// Where the s of specific events are cached.
///
private readonly ConcurrentDictionary, LogEventPublisherInternal> m_lookupEventPublishers;
private LogEventPublisherInternal m_excessivePublisherEventNames;
internal LogPublisher(LoggerInternal logger, LogPublisherInternal publisherInstance, MessageClass classification)
{
m_logger = logger;
m_publisherInstance = publisherInstance;
m_classification = classification;
InitialStackMessages = Logger.GetStackMessages();
InitialStackTrace = new LogStackTrace(true, 1, 10);
MaxDistinctEventPublisherCount = 20;
m_lookupEventPublishers = new ConcurrentDictionary, LogEventPublisherInternal>();
}
///
/// Initializes an with the provided values.
///
/// the level of the message and associated flags if any
/// the name of the event.
///
public LogEventPublisher RegisterEvent(MessageLevel level, string eventName)
{
LogMessageAttributes flag = new LogMessageAttributes(m_classification, level, MessageSuppression.None, MessageFlags.None);
LogEventPublisherInternal publisher = InternalRegisterEvent(flag, eventName);
return new LogEventPublisher(this, publisher);
}
///
/// Initializes an with the provided values.
///
/// the level of the message
/// associated flags
/// the name of the event.
///
public LogEventPublisher RegisterEvent(MessageLevel level, MessageFlags flags, string eventName)
{
LogMessageAttributes flag = new LogMessageAttributes(m_classification, level, MessageSuppression.None, flags);
LogEventPublisherInternal publisher = InternalRegisterEvent(flag, eventName);
return new LogEventPublisher(this, publisher);
}
///
/// Initializes an with the provided values.
///
/// the level of the message
///
///
///
///
///
public LogEventPublisher RegisterEvent(MessageLevel level, string eventName, int stackTraceDepth, MessageRate messagesPerSecond, int burstLimit)
{
LogMessageAttributes flag = new LogMessageAttributes(m_classification, level, MessageSuppression.None, MessageFlags.None);
LogEventPublisherInternal publisher = InternalRegisterEvent(flag, eventName, stackTraceDepth, messagesPerSecond, burstLimit);
return new LogEventPublisher(this, publisher);
}
///
/// Initializes an with the provided values.
///
/// the level of the message
/// associated flags
///
///
///
///
///
public LogEventPublisher RegisterEvent(MessageLevel level, MessageFlags flags, string eventName, int stackTraceDepth, MessageRate messagesPerSecond, int burstLimit)
{
LogMessageAttributes flag = new LogMessageAttributes(m_classification, level, MessageSuppression.None, flags);
LogEventPublisherInternal publisher = InternalRegisterEvent(flag, eventName, stackTraceDepth, messagesPerSecond, burstLimit);
return new LogEventPublisher(this, publisher);
}
///
/// Gets the full name of the type.
///
///
public override string ToString()
{
return m_publisherInstance.TypeData.TypeName + " (" + m_publisherInstance.TypeData.AssemblyName + ")";
}
///
/// Raises a log message with the provided data.
///
/// the level of the message
/// A short name about what this message is detailing. Typically this will be a few words.
/// A longer message than giving more specifics about the actual message.
/// Typically, this will be up to 1 line of text.
/// A long text field with the details of the message.
/// An exception object if one is provided.
public void Publish(MessageLevel level, string eventName, string message = null, string details = null, Exception exception = null)
{
LogMessageAttributes flag = new LogMessageAttributes(m_classification, level, MessageSuppression.None, MessageFlags.None);
InternalRegisterEvent(flag, eventName).Publish(null, message, details, exception, InitialStackMessages, InitialStackTrace);
}
///
/// Raises a log message with the provided data.
///
/// the level of the message
/// associated flags
/// A short name about what this message is detailing. Typically this will be a few words.
/// A longer message than giving more specifics about the actual message.
/// Typically, this will be up to 1 line of text.
/// A long text field with the details of the message.
/// An exception object if one is provided.
public void Publish(MessageLevel level, MessageFlags flags, string eventName, string message = null, string details = null, Exception exception = null)
{
LogMessageAttributes flag = new LogMessageAttributes(m_classification, level, MessageSuppression.None, flags);
InternalRegisterEvent(flag, eventName).Publish(null, message, details, exception, InitialStackMessages, InitialStackTrace);
}
///
/// Initializes an with a series of settings.
///
///
/// the name of the event.
///
private LogEventPublisherInternal InternalRegisterEvent(LogMessageAttributes attributes, string eventName)
{
if (eventName == null)
eventName = string.Empty;
LogEventPublisherInternal publisher;
if (m_lookupEventPublishers.TryGetValue(Tuple.Create(attributes, eventName), out publisher))
{
return publisher;
}
//If messages events are unclassified allow a higher message throughput rate.
double messagesPerSecond = 1;
int burstRate = 20;
if (eventName.Length == 0)
{
messagesPerSecond = 5;
burstRate = 100;
}
return InternalRegisterNewEvent(attributes, eventName, 0, messagesPerSecond, burstRate);
}
///
/// Initializes an with a series of settings.
///
///
///
///
///
///
///
private LogEventPublisherInternal InternalRegisterEvent(LogMessageAttributes attributes, string eventName, int stackTraceDepth, MessageRate messagesPerSecond, int burstLimit)
{
if (eventName == null)
eventName = string.Empty;
LogEventPublisherInternal publisher;
if (m_lookupEventPublishers.TryGetValue(Tuple.Create(attributes, eventName), out publisher))
{
return publisher;
}
return InternalRegisterNewEvent(attributes, eventName, stackTraceDepth, messagesPerSecond, burstLimit);
}
private LogEventPublisherInternal InternalRegisterNewEvent(LogMessageAttributes attributes, string eventName, int stackTraceDepth, double messagesPerSecond, int burstLimit)
{
//Note: A race condition can cause more then the maximum number of entries to exist, however, this is not a concern.
if (m_lookupEventPublishers.Count > MaxDistinctEventPublisherCount)
{
if (m_excessivePublisherEventNames == null)
{
var owner1 = new LogEventPublisherDetails(m_publisherInstance.TypeData,
"Excessive Event Names: Event names for this publisher has been limited to " + MaxDistinctEventPublisherCount.ToString() +
"Please adjust MaxDistinctEventPublisherCount if this is not a bug and this publisher can truly create this many publishers.");
m_excessivePublisherEventNames = new LogEventPublisherInternal(attributes, owner1, m_publisherInstance, m_logger, stackTraceDepth, messagesPerSecond, burstLimit);
}
return m_excessivePublisherEventNames;
}
LogEventPublisherInternal publisher;
var owner = new LogEventPublisherDetails(m_publisherInstance.TypeData, eventName);
publisher = new LogEventPublisherInternal(attributes, owner, m_publisherInstance, m_logger, stackTraceDepth, messagesPerSecond, burstLimit);
publisher = m_lookupEventPublishers.GetOrAdd(Tuple.Create(attributes, eventName), publisher);
return publisher;
}
}
}