//******************************************************************************************************
// 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();
}
}
}