Click here to Skip to main content
15,896,201 members
Articles / Desktop Programming / Windows Forms

NLog Log and Audit Advanced Target

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
26 May 2010CPOL6 min read 42.8K   750   35  
A way to audit your business objects using NLog.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using App.Logger.Targets.ObjectHistoryLogger.Configuration;
using System.Reflection;

namespace App.Logger.Targets.ObjectHistoryLogger
{
	/// <summary>
	/// Object that inspects an object that is to be logged.
	/// </summary>
	public class OHInfo
	{
		Object _obj = null;

		public OHInfo(object obj, OHTarget target)
		{
			_obj = obj;
			Target = target;

			if (obj != null)
			{
				ObjectType = obj.GetType();
			}

			TimeStamp = DateTime.Now;
			//User = "N/A";

			InspectObject(_obj.GetType());
		}

		public OHInfo(object obj, string user, OHTarget target)
			: this(obj, target)
		{
			User = user;
		}

		public OHInfo(Type objType, OHTarget target)
		{
			ObjectType = objType;
			Target = target;
			InspectObject(objType);
		}

		/// <summary>
		/// Gets the target
		/// </summary>
		public OHTarget Target { get; private set; }

		/// <summary>
		/// Gets the source object that is to be logged.
		/// </summary>
		/// <value>The source object.</value>
		public Object SourceObject { get { return _obj; } }

		Type _objectType = null;
		/// <summary>
		/// Gets the type of the object that is to be logged.
		/// </summary>
		/// <value>The type of the object.</value>
		public Type ObjectType
		{
			get
			{
				return _objectType;
			}
			private set
			{
				_objectType = value;
			}
		}

		/// <summary>
		/// Gets the time stamp that marks the time when the object was set to be logged.
		/// </summary>
		/// <value>The time stamp.</value>
		public DateTime TimeStamp { get; private set; }

		/// <summary>
		/// Gets the name of the object.
		/// This can either be the name taken from the Type or the mapping name if supplied on the LogAttribute.
		/// </summary>
		/// <value>The name of the object.</value>
		public string ObjectName { get; private set; }

		/// <summary>
		/// Gets a value indicating whether this instance is loggable.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is loggable; otherwise, <c>false</c>.
		/// </value>
		public bool IsLoggable { get; private set; }

		/// <summary>
		/// Gets the name of the key property.
		/// The key property is the one that have the LogKeyAttribute.
		/// </summary>
		/// <value>The name of the key property.</value>
		public string KeyPropertyName { get; private set; }

		/// <summary>
		/// Gets or sets the type of the append method.
		/// </summary>
		/// <value>The type of the append.</value>
		public AppendTypes AppendType { get; set; }

		List<OHPropertyInfo> _loggableProperties = null;
		/// <summary>
		/// Gets the list of loggable properties.
		/// </summary>
		/// <value>The loggable properties.</value>
		public List<OHPropertyInfo> LoggableProperties 
		{
			get 
			{
				if (_loggableProperties == null)
					_loggableProperties = new List<OHPropertyInfo>();

				return _loggableProperties;
			}
			private set
			{
				_loggableProperties = value;
			}
		}

		/// <summary>
		/// Gets the user that is attached to this log operation.
		/// </summary>
		/// <value>The user.</value>
		public string User { get; set; }



		/// <summary>
		/// Gets the object serialized as XML.
		/// </summary>
		/// <returns></returns>
		public string GetXML()
		{
			System.IO.StringWriter strW = new System.IO.StringWriter();
			using (System.Xml.XmlTextWriter xml = new System.Xml.XmlTextWriter(strW))
			{
				xml.WriteStartDocument();
				xml.WriteStartElement(ObjectName);

				#region SYSTEM PROPERTIES

				// write the TimeStamp
				xml.WriteStartElement("__TimeStamp");	// custom name so it woun't confict with anything
				xml.WriteAttributeString("Type", typeof(DateTime).ToString());
				xml.WriteAttributeString("Value", TimeStamp.ToString());
				//xml.WriteValue(TimeStamp);
				xml.WriteEndElement();

				#endregion

				#region OBJECT PROPERTIES

				// write the properties
				LoggableProperties.ForEach(prop =>
				{
					xml.WriteStartElement(prop.Name);
					xml.WriteAttributeString("Type", prop.PropInfo.PropertyType.ToString());
					xml.WriteAttributeString("Value", prop.Value);
					//xml.WriteValue(prop.Value);
					xml.WriteEndElement();
				});

				#endregion

				xml.WriteEndElement();		// end objectName
				xml.WriteEndDocument();		// end document
				
				xml.Flush();
				xml.Close();
			}

			strW.Dispose();



			//System.IO.StringReader sr = new System.IO.StringReader(strW.ToString());
			//System.Xml.XmlTextReader xml = new System.Xml.XmlTextReader(sr);
			
			

			return strW.ToString();
		}

		/// <summary>
		/// Inspects the object.
		/// </summary>
		private void InspectObject(Type objType)
		{
			// searches on xml configuration first, if no configuration found for this type, search on code attributes

			if (!InspectObject_UsingXmlConfig(objType))
				InspectObject_UsingAttributesConfig(objType);
		}

		private bool InspectObject_UsingXmlConfig(Type objType)
		{
			bool retval = false;

			XMLTypeConfiguration cfg = Target.XmlConfiguration().GetTypeConfiguration(objType);
			if (cfg == null)
			{
				IsLoggable = false;
			}
			else
			{
				IsLoggable = true;
				retval = true;

				ObjectName = objType.FullName;
				AppendType = AppendTypes.Differential;	// to be defined on xml configuration

				if (!string.IsNullOrEmpty(cfg.KeyProperty))
				{
					KeyPropertyName = cfg.KeyProperty;
					if (!cfg.Properties.Contains(KeyPropertyName))
					{
						cfg.Properties.Add(KeyPropertyName);
					}
				}

				// which properties are to be logged
				for (int i = 0; i < cfg.Properties.Count; i++)
				{
					PropertyInfo prop = objType.GetProperty(cfg.Properties[i]);
					if (prop != null)
					{
						// get property value
						string propValue = string.Empty;
						if (_obj != null)
						{
							object _v = prop.GetValue(_obj, null);
							propValue = _v == null ? null : _v.ToString();
						}

						LoggableProperties.Add(new OHPropertyInfo(prop.Name, propValue, prop, this));
					}
				}
			}

			return retval;
		}

		private bool InspectObject_UsingAttributesConfig(Type objType)
		{
			bool retval = false;

			foreach (var attr in objType.GetCustomAttributes(true))
			{
				if (attr is LogAttribute)	// evaluate if class is to be logged
				{
					retval = true;	// found configuration

					IsLoggable = true;
					ObjectName = ((LogAttribute)attr).MappedName;
					if (string.IsNullOrEmpty(ObjectName))
					{
						ObjectName = objType.FullName;
					}

					AppendType = ((LogAttribute)attr).AppendType;
					
					// evaluate which properties are to be logged
					var props = objType.GetProperties();
					foreach (var prop in props)
					{
						foreach (var pAttr in prop.GetCustomAttributes(true))
						{
							if (pAttr is LogKeyAttribute)
							{
								KeyPropertyName = prop.Name;
							}

							if (pAttr is LogPropertyAttribute)
							{
								// get property name
								string propName = ((LogPropertyAttribute)pAttr).MappedName;
								if (string.IsNullOrEmpty(propName))
								{
									propName = prop.Name;
								}

								// get property value
								string propValue = string.Empty;
								if (_obj != null)
								{
									object _v = prop.GetValue(_obj, null);
									propValue = _v == null ? null : _v.ToString();
								}

								// add the loggable property to the LoggableProperties list
								LoggableProperties.Add(new OHPropertyInfo(propName, propValue, prop, this));
							}
						}
					}

					// At the end we must verify if the object applies to the required rules
					if (string.IsNullOrEmpty(KeyPropertyName))
					{
						throw new MissingLogKeyAttributeException(objType);
					}

				}
			}

			return retval;
		}

	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
Switzerland Switzerland
Senior IT Consultant working in Switzerland as Senior Software Engineer.

Find more at on my blog.

Comments and Discussions