//****************************************************************************************************** // LogMessage.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.Data; using System.IO; using System.Reflection; using System.Text; using System.Threading; using GSF.Immutable; using GSF.IO; namespace GSF.Diagnostics { /// /// An individual log message. /// public sealed class LogMessage { /// /// Contains details about the that published this . /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] public readonly LogEventPublisherDetails EventPublisherDetails; /// /// The message stack that existed when the was originally constructed. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] public readonly LogStackMessages InitialStackMessages; /// /// The stack trace that existed when the was originally constructed. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] public readonly LogStackTrace InitialStackTrace; /// /// The message stack that existed when this was published. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] public readonly LogStackMessages CurrentStackMessages; /// /// The stack trace that existed when this was published. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] public readonly LogStackTrace CurrentStackTrace; /// /// The time that the message was created. /// public readonly DateTime UtcTime; /// /// The classification of the message /// public MessageClass Classification => LogMessageAttributes.Classification; /// /// The level associated with the message /// public MessageLevel Level => LogMessageAttributes.Level; /// /// The flags associated with the message /// public MessageFlags Flags => LogMessageAttributes.Flags; /// /// The suppression level assigned to this log message /// public MessageSuppression MessageSuppression => LogMessageAttributes.MessageSuppression; /// /// The associated with that generated the message. /// public string TypeName => EventPublisherDetails.TypeData.TypeName; /// /// The associated with that generated the message. /// public string AssemblyName => EventPublisherDetails.TypeData.AssemblyName; /// /// All related types such as interfaces/parent classes for the current type. /// public ImmutableList RelatedTypes => EventPublisherDetails.TypeData.RelatedTypes; /// /// The event name of this log message. /// public string EventName => EventPublisherDetails.EventName; internal readonly LogMessageAttributes LogMessageAttributes; /// /// A specific message about the event giving more specifics about the actual message. /// Typically, this will be up to 1 line of text. /// Can be String.Empty. /// public readonly string Message; /// /// A long text field with the details of the message. /// Can be String.Empty. /// public readonly string Details; /// /// An exception object if one is provided. /// Can be null. /// Since the exception is not serialized to the disk, it will be null when loaded. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")] public readonly Exception Exception; /// /// A string representation of the exception. Can be String.Empty. /// If loaded from the disk, since exception objects cannot be serialized, /// the will be null and /// this field will have the string representation of /// public readonly string ExceptionString; /// /// The Managed Thread ID of the thread that created this message. This /// is primarily to assist in future log viewing applications /// where it is beneficial to track the thread. /// public readonly int ManagedThreadID; /// /// A sequence number maintained by each thread thread of the previous /// first chance exception that was thrown. This is used to assist /// LogFileViewer associate log messages with properly handled /// first chance exceptions. /// public readonly int PreviousFirstChanceExceptionSequenceNumber; /// /// Loads a log messages from the supplied stream /// /// the stream to load the log message from. /// A save helper that will condense objects internal LogMessage(Stream stream, LogMessageSaveHelper saveHelper = null) { if (saveHelper == null) saveHelper = LogMessageSaveHelper.Create(true); byte version = stream.ReadNextByte(); switch (version) { case 1: EventPublisherDetails = saveHelper.LoadEventPublisherDetails(stream); InitialStackMessages = saveHelper.LoadStackMessages(stream); InitialStackTrace = saveHelper.LoadStackTrace(stream); CurrentStackMessages = saveHelper.LoadStackMessages(stream); CurrentStackTrace = saveHelper.LoadStackTrace(stream); UtcTime = stream.ReadDateTime(); LogMessageAttributes = new LogMessageAttributes(stream); Message = stream.ReadString(); Details = stream.ReadString(); Exception = null; ExceptionString = stream.ReadString(); ManagedThreadID = -1; PreviousFirstChanceExceptionSequenceNumber = -1; break; case 2: EventPublisherDetails = saveHelper.LoadEventPublisherDetails(stream); InitialStackMessages = saveHelper.LoadStackMessages(stream); InitialStackTrace = saveHelper.LoadStackTrace(stream); CurrentStackMessages = saveHelper.LoadStackMessages(stream); CurrentStackTrace = saveHelper.LoadStackTrace(stream); UtcTime = stream.ReadDateTime(); LogMessageAttributes = new LogMessageAttributes(stream); Message = stream.ReadString(); Details = stream.ReadString(); Exception = null; ExceptionString = stream.ReadString(); ManagedThreadID = stream.ReadInt32(); PreviousFirstChanceExceptionSequenceNumber = -1; break; case 3: EventPublisherDetails = saveHelper.LoadEventPublisherDetails(stream); InitialStackMessages = saveHelper.LoadStackMessages(stream); InitialStackTrace = saveHelper.LoadStackTrace(stream); CurrentStackMessages = saveHelper.LoadStackMessages(stream); CurrentStackTrace = saveHelper.LoadStackTrace(stream); UtcTime = stream.ReadDateTime(); LogMessageAttributes = new LogMessageAttributes(stream); Message = saveHelper.LoadString(stream); Details = saveHelper.LoadString(stream); Exception = null; ExceptionString = saveHelper.LoadString(stream); ManagedThreadID = stream.ReadInt32(); PreviousFirstChanceExceptionSequenceNumber = -1; break; case 4: EventPublisherDetails = saveHelper.LoadEventPublisherDetails(stream); InitialStackMessages = saveHelper.LoadStackMessages(stream); InitialStackTrace = saveHelper.LoadStackTrace(stream); CurrentStackMessages = saveHelper.LoadStackMessages(stream); CurrentStackTrace = saveHelper.LoadStackTrace(stream); UtcTime = stream.ReadDateTime(); LogMessageAttributes = new LogMessageAttributes(stream); Message = saveHelper.LoadString(stream); Details = saveHelper.LoadString(stream); Exception = null; ExceptionString = saveHelper.LoadString(stream); ManagedThreadID = stream.ReadInt32(); PreviousFirstChanceExceptionSequenceNumber = stream.ReadInt32(); break; default: throw new VersionNotFoundException(); } } /// /// Creates a log message /// internal LogMessage(LogEventPublisherDetails eventPublisherDetails, LogStackMessages initialStackMessages, LogStackTrace initialStackTrace, LogStackMessages currentStackMessages, LogStackTrace currentStackTrace, LogMessageAttributes flags, string message, string details, Exception exception) { if (eventPublisherDetails == null) throw new ArgumentNullException(nameof(eventPublisherDetails)); if (exception != null) { ExceptionString = exception.ToString(); } else { ExceptionString = string.Empty; } EventPublisherDetails = eventPublisherDetails; InitialStackMessages = initialStackMessages ?? LogStackMessages.Empty; InitialStackTrace = initialStackTrace ?? LogStackTrace.Empty; CurrentStackMessages = currentStackMessages ?? LogStackMessages.Empty; CurrentStackTrace = currentStackTrace ?? LogStackTrace.Empty; UtcTime = DateTime.UtcNow; LogMessageAttributes = flags; Message = message ?? string.Empty; Details = details ?? string.Empty; Exception = exception; ManagedThreadID = Thread.CurrentThread.ManagedThreadId; PreviousFirstChanceExceptionSequenceNumber = Logger.PreviousFirstChanceExceptionSequenceNumber; } /// Returns a string that represents the current object. /// A string that represents the current object. /// 2 public override string ToString() { return GetMessage(); } /// /// Writes the log data to the stream /// /// /// internal void Save(Stream stream, LogMessageSaveHelper saveHelper = null) { if (saveHelper == null) saveHelper = LogMessageSaveHelper.Create(true); stream.Write((byte)4); saveHelper.SaveEventPublisherDetails(stream, EventPublisherDetails); saveHelper.SaveStackMessages(stream, InitialStackMessages); saveHelper.SaveStackTrace(stream, InitialStackTrace); saveHelper.SaveStackMessages(stream, CurrentStackMessages); saveHelper.SaveStackTrace(stream, CurrentStackTrace); stream.Write(UtcTime); LogMessageAttributes.Save(stream); saveHelper.SaveString(stream, Message); saveHelper.SaveString(stream, Details); saveHelper.SaveString(stream, ExceptionString); stream.Write(ManagedThreadID); stream.Write(PreviousFirstChanceExceptionSequenceNumber); } /// /// Gets the details of the message. /// /// public string GetMessage() { StringBuilder sb = new StringBuilder(); sb.Append("Time: "); sb.Append(UtcTime.ToLocalTime()); sb.Append(" - "); sb.Append(Classification.ToString()); sb.Append(" - "); sb.Append(Level.ToString()); sb.Append(" - "); sb.Append(Flags.ToString()); sb.Append(" - "); sb.Append(MessageSuppression.ToString()); sb.AppendLine(); sb.Append("Event Name: "); sb.AppendLine(EventPublisherDetails.EventName); if (Message.Length > 0) { sb.Append("Message: "); sb.AppendLine(Message); } if (Details.Length > 0) { sb.Append("Details: "); sb.AppendLine(Details); } if (ExceptionString.Length > 0) { sb.AppendLine("Exception: "); sb.AppendLine(ExceptionString); } if (EventPublisherDetails.TypeData.TypeName.Length > 0) { sb.AppendLine("Message Type: " + EventPublisherDetails.TypeData.TypeName); } if (EventPublisherDetails.TypeData.AssemblyName.Length > 0) { sb.AppendLine($"Message Assembly: {EventPublisherDetails.TypeData.AssemblyName} ({EventPublisherDetails.TypeData.AssemblyVersion}) "); } sb.AppendLine($"Managed Thread Id: {ManagedThreadID}"); if (!ReferenceEquals(InitialStackMessages, LogStackMessages.Empty)) { sb.AppendLine(); sb.AppendLine("Initial Stack Messages: "); sb.AppendLine(InitialStackMessages.ToString()); } if (!ReferenceEquals(InitialStackTrace, LogStackTrace.Empty)) { sb.AppendLine(); sb.AppendLine("Initial Stack Trace: "); sb.AppendLine(InitialStackTrace.ToString()); } if (!ReferenceEquals(CurrentStackMessages, LogStackMessages.Empty)) { sb.AppendLine(); sb.AppendLine("Current Stack Messages: "); sb.AppendLine(CurrentStackMessages.ToString()); } if (!ReferenceEquals(CurrentStackTrace, LogStackTrace.Empty)) { sb.AppendLine(); sb.AppendLine("Current Stack Trace: "); sb.AppendLine(CurrentStackTrace.ToString()); } return sb.ToString(); } } }