#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 } }