#region Copyright 2008-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0
/* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Collections.Generic;
using CSharpTest.Net.Logging.Implementation;
using System.Reflection;
using System.Runtime.Serialization;
namespace CSharpTest.Net.Logging
{
///
/// This is the class that is used to transfer log events through the system. It's basically
/// a 'picture' of the state at the time the log was written since all logging is actually
/// happening in a delayed fashion.
///
[Serializable]
[System.Diagnostics.DebuggerNonUserCode()]
public sealed partial class EventData : ISerializable
{
#region internal EventData()
///
/// Asserts that the LogFields enumeration has the same fields as this class
///
static EventData()
{
VersionInfo.Assert();
}
private object[] _data;
internal EventData(object[] values)
{
_data = values;
_data[0] = this;
//These are used heavily so they are extracted now.
Level = (LogLevels)_data[(int)LogFields.Level];
Output = (LogOutputs)_data[(int)LogFields.Output];
}
#endregion
#region Formattable Properties
/// A unique integer representing the index the event was written since program start
public int EventId { get { return (int)_data[(int)LogFields.EventId]; } }
/// Returns the time when the log event was raised
public DateTime EventTime { get { return (DateTime)_data[(int)LogFields.EventTime]; } }
/// The current process id
public Int32 ProcessId { get { return (Int32)_data[(int)LogFields.ProcessId]; } }
/// The current process name
public String ProcessName { get { return (String)_data[(int)LogFields.ProcessName]; } }
/// The current app domain's friendly name
public String AppDomainName { get { return (String)_data[(int)LogFields.AppDomainName]; } }
/// The current app domain's entry-point assembly name
public String EntryAssembly { get { return (String)_data[(int)LogFields.EntryAssembly]; } }
/// The logging thread's 'CurrentPrincipal.Identity.Name' property
public String ThreadPrincipalName { get { return (String)_data[(int)LogFields.ThreadPrincipalName]; } }
/// The managed thread id that called the log routine
public Int32 ManagedThreadId { get { return (Int32)_data[(int)LogFields.ManagedThreadId]; } }
/// The managed thread name (if any) or String.Empty
public String ManagedThreadName { get { return (String)_data[(int)LogFields.ManagedThreadName]; } }
/// The LogLevel of the log data or LogLevels.None if Log.Write() was called
public readonly LogLevels Level;
/// The outputs that should recieve this message
public readonly LogOutputs Output;
/// An instance of the Exception class or null if none provided
public Exception Exception { get { return (Exception)_data[(int)LogFields.Exception]; } }
/// The formatted string of the message or String.Empty if none
public String Message { get { return (String)_data[(int)LogFields.Message]; } }
/// The file name where the log was called from or null if no file dataList available/configured.
public String FileName { get { return (String)_data[(int)LogFields.FileName]; } }
/// The file line number where the log was called from.
public int FileLine { get { return (int)_data[(int)LogFields.FileLine]; } }
/// The file column where the log was called from.
public int FileColumn { get { return (int)_data[(int)LogFields.FileColumn]; } }
/// The assembly's version that contained the method where the log was called from.
public String MethodAssemblyVersion { get { return (String)_data[(int)LogFields.MethodAssemblyVersion]; } }
/// The assembly's name that contained the method where the log was called from.
public String MethodAssembly { get { return (String)_data[(int)LogFields.MethodAssembly]; } }
/// The type containing the method where the log was called from.
public String MethodType { get { return (String)_data[(int)LogFields.MethodType]; } }
/// The unqualified type containing the method where the log was called from.
public String MethodTypeName { get { return (String)_data[(int)LogFields.MethodTypeName]; } }
/// The method where the log was called from.
public String MethodName { get { return (String)_data[(int)LogFields.MethodName]; } }
/// The method's argument names and types.
public String MethodArgs { get { return (String)_data[(int)LogFields.MethodArgs]; } }
/// Returns the IL offset within the calling method
public int IlOffset { get { return (int)_data[(int)LogFields.IlOffset]; } }
/// returns the text given the most recent call to Log.Start() that has not yet been Disponsed
public string LogCurrent { get { string[] stack = (string[])_data[(int)LogFields.LogStack]; return stack != null && stack.Length > 0 ? stack[stack.Length-1] : null; } }
/// returns the text given to all calls to Log.Start() that has not yet been Disponsed separated by '::'
public string LogStack { get { if(_data[(int)LogFields.LogStack] == null) return null; return String.Join("::", (string[])_data[(int)LogFields.LogStack]); } }
///
/// The full method information: Type.Name(Args)
///
public string Location { get { return String.Format(Configuration.FormatProvider, Configuration.FORMAT_LOCATION, _data); } }
///
/// The full method information: Type.Name(Args)
///
public string Method { get { return _data[(int)LogFields.MethodType] == null ? null : String.Format(Configuration.FormatProvider, Configuration.FORMAT_METHOD, _data); } }
///
/// Returns the full message of Message + Location + Exception
///
public string FileLocation { get { return _data[(int)LogFields.FileName] == null ? null : String.Format(Configuration.FormatProvider, Configuration.FORMAT_FILELINE, _data); } }
///
/// Returns the full message of Message + Location + Exception
///
public string FullMessage { get { return String.Format(Configuration.FormatProvider, Configuration.FORMAT_FULLMESSAGE, _data); } }
#endregion
#region Internal-Use-Only
///
/// Used for string formatting, the order of these MUST match the order of names return by FieldNames
///
internal object[] ToObjectArray() { return _data; }
#endregion
#region ToString/Write/ToXml
///
/// Displays this log data in a default brief format
///
public override string ToString()
{
return string.Format(Configuration.InnerFormatter, "[{0:D2}] {1,8} - {2}",
_data[(int)LogFields.ManagedThreadId], _data[(int)LogFields.Level], _data[(int)LogFields.Message]);
}
///
/// Displays this log data in a specific format. Use '{FieldName}' to be substituded with it's value.
/// Sorry but it uses a case-sensitive match of fields in this class.
///
public string ToString(string format)
{
try { return String.Format(Configuration.FormatProvider, LogUtils.PrepareFormatString(format), ToObjectArray()); }
catch(Exception e) { LogUtils.LogError(e); }
return Message;
}
///
/// Writes this event to the text writter with the default brief format
///
public void Write(System.IO.TextWriter writer)
{
try { writer.WriteLine(String.Format(Configuration.FormatProvider, Configuration.FORMAT_DEFAULT, ToObjectArray())); }
catch(Exception e) { LogUtils.LogError(e); }
}
///
/// Writes this log data in a specific format. Use '{FieldName}' to be substituded with it's value.
/// Sorry but it uses a case-sensitive match of fields in this class.
///
public void Write(System.IO.TextWriter writer, string format)
{
try { writer.WriteLine(String.Format(Configuration.FormatProvider, LogUtils.PrepareFormatString(format), ToObjectArray())); }
catch(Exception e) { LogUtils.LogError(e); }
}
///
/// Writes a custom xml format for this record.
///
public void Write(System.Xml.XmlWriter writer)
{
try
{
writer.WriteStartElement("e");
try
{
writer.WriteAttributeString("id", _data[(int)LogFields.EventId].ToString());
writer.WriteAttributeString("time", ((DateTime)_data[(int)LogFields.EventTime]).ToString("u").TrimEnd('Z'));
writer.WriteAttributeString("pid", _data[(int)LogFields.ProcessId].ToString());
writer.WriteAttributeString("tid", _data[(int)LogFields.ManagedThreadId].ToString());
writer.WriteAttributeString("lvl", _data[(int)LogFields.Level].ToString());
if(_data[(int)LogFields.Message] != null)
writer.WriteAttributeString("msg", _data[(int)LogFields.Message].ToString());
if(_data[(int)LogFields.Exception] != null)
writer.WriteAttributeString("err", _data[(int)LogFields.Exception].ToString());
if(_data[(int)LogFields.MethodType] != null)
{
writer.WriteAttributeString("type", _data[(int)LogFields.MethodType].ToString());
writer.WriteAttributeString("method", _data[(int)LogFields.MethodName].ToString());
writer.WriteAttributeString("args", _data[(int)LogFields.MethodArgs].ToString());
writer.WriteAttributeString("il", _data[(int)LogFields.IlOffset].ToString());
}
if(_data[(int)LogFields.FileName] != null)
{
writer.WriteAttributeString("file", _data[(int)LogFields.FileName].ToString());
writer.WriteAttributeString("line", _data[(int)LogFields.FileLine].ToString());
writer.WriteAttributeString("col", _data[(int)LogFields.FileColumn].ToString());
}
string[] stack = _data[(int)LogFields.LogStack] as string[];
if( stack != null && stack.Length > 0 )
writer.WriteAttributeString("stack", stack[stack.Length-1]);
}
finally { writer.WriteEndElement(); }
}
catch(Exception e)
{ LogUtils.LogError(e); }
}
///
/// Returns this data as an XmlWriter text fragment who name is the class name and each attribute
/// is the field name, again maintaining the case.
///
public string ToXml()
{
System.IO.StringWriter sw = new System.IO.StringWriter();
System.Xml.XmlTextWriter writer = new System.Xml.XmlTextWriter(sw);
writer.Formatting = System.Xml.Formatting.Indented;
Write(writer);
writer.Flush();
return sw.ToString();
}
#endregion
#region Custom Serialization
/// /// Serialization Constructor for ISerializable ///
public EventData(SerializationInfo info, StreamingContext context)
{
string sver = info.GetString("rec:ver");
if (sver != String.Format("{0}:{1}", VersionInfo.FieldCount, VersionInfo.CheckSum))
throw new SerializationException();
_data = new object[VersionInfo.FieldCount];
Type t;
Dictionary values = new Dictionary(StringComparer.OrdinalIgnoreCase);
foreach (SerializationEntry entry in info)
values.Add(entry.Name, entry.Value);
for (int i = 0; i < _data.Length; i++)
{
try
{
if (i == (int)LogFields.Exception && values.ContainsKey("hasError") && (bool)values["hasError"])
_data[i] = new SharedException(info, context);
object value;
if (null != (t = VersionInfo.FieldTypes[i]) && values.TryGetValue(i.ToString(), out value))
_data[i] = value;
}
catch (Exception e) { System.Diagnostics.Trace.WriteLine(e.ToString()); }
}
//These are used heavily so they are extracted now.
_data[0] = this;
_data[(int)LogFields.Output] = this.Output = Configuration.LogOutput;
_data[(int)LogFields.Level] = Level = (LogLevels)_data[(int)LogFields.Level];
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("rec:ver", String.Format("{0}:{1}", VersionInfo.FieldCount, VersionInfo.CheckSum));
Type t;
for (int i = 0; i < _data.Length; i++)
{
try
{
if (i == (int)LogFields.Exception && _data[i] is Exception)
{ new SharedException(_data[i] as Exception).GetObjectData(info, context); info.AddValue("hasError", true); }
else
if (null != (t = VersionInfo.FieldTypes[i]) && null != _data[i])
{
object value = _data[i];
if (value.GetType() != t)
value = Convert.ChangeType(value, t);
info.AddValue(i.ToString(), value, t);
}
}
catch (Exception e) { System.Diagnostics.Trace.WriteLine(e.ToString()); }
}
}
#endregion
}
}