Click here to Skip to main content
15,896,111 members
Articles / Web Development / HTML

Transformalizing NorthWind

Rate me:
Please Sign up or sign in to vote.
4.95/5 (29 votes)
24 Jul 2014GPL37 min read 57.8K   341   53  
Combining de-normalization, transformation, replication, and awesome-ness.
#region License
// /*
// See license included in this library folder.
// */
#endregion

using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using System.Xml;
using Transformalize.Libs.NLog.Internal;

#if !SILVERLIGHT

namespace Transformalize.Libs.NLog
{
    /// <summary>
    ///     TraceListener which routes all messages through NLog.
    /// </summary>
    public class NLogTraceListener : TraceListener
    {
        private static readonly Assembly systemAssembly = typeof (Trace).Assembly;
        private LogFactory logFactory;
        private LogLevel defaultLogLevel = LogLevel.Debug;
        private bool attributesLoaded;
#if !NET_CF
        private bool autoLoggerName;
#endif
        private LogLevel forceLogLevel;

        /// <summary>
        ///     Gets or sets the log factory to use when outputting messages (null - use LogManager).
        /// </summary>
        public LogFactory LogFactory
        {
            get
            {
                InitAttributes();
                return logFactory;
            }

            set
            {
                attributesLoaded = true;
                logFactory = value;
            }
        }

        /// <summary>
        ///     Gets or sets the default log level.
        /// </summary>
        public LogLevel DefaultLogLevel
        {
            get
            {
                InitAttributes();
                return defaultLogLevel;
            }

            set
            {
                attributesLoaded = true;
                defaultLogLevel = value;
            }
        }

        /// <summary>
        ///     Gets or sets the log which should be always used regardless of source level.
        /// </summary>
        public LogLevel ForceLogLevel
        {
            get
            {
                InitAttributes();
                return forceLogLevel;
            }

            set
            {
                attributesLoaded = true;
                forceLogLevel = value;
            }
        }

#if !NET_CF
        /// <summary>
        ///     Gets a value indicating whether the trace listener is thread safe.
        /// </summary>
        /// <value></value>
        /// <returns>true if the trace listener is thread safe; otherwise, false. The default is false.</returns>
        public override bool IsThreadSafe
        {
            get { return true; }
        }

        /// <summary>
        ///     Gets or sets a value indicating whether to use auto logger name detected from the stack trace.
        /// </summary>
        public bool AutoLoggerName
        {
            get
            {
                InitAttributes();
                return autoLoggerName;
            }

            set
            {
                attributesLoaded = true;
                autoLoggerName = value;
            }
        }
#endif

        /// <summary>
        ///     When overridden in a derived class, writes the specified message to the listener you create in the derived class.
        /// </summary>
        /// <param name="message">A message to write.</param>
        public override void Write(string message)
        {
            ProcessLogEventInfo(DefaultLogLevel, null, message, null, null);
        }

        /// <summary>
        ///     When overridden in a derived class, writes a message to the listener you create in the derived class, followed by a line terminator.
        /// </summary>
        /// <param name="message">A message to write.</param>
        public override void WriteLine(string message)
        {
            ProcessLogEventInfo(DefaultLogLevel, null, message, null, null);
        }

        /// <summary>
        ///     When overridden in a derived class, closes the output stream so it no longer receives tracing or debugging output.
        /// </summary>
        public override void Close()
        {
        }

        /// <summary>
        ///     Emits an error message.
        /// </summary>
        /// <param name="message">A message to emit.</param>
        public override void Fail(string message)
        {
            ProcessLogEventInfo(LogLevel.Error, null, message, null, null);
        }

        /// <summary>
        ///     Emits an error message and a detailed error message.
        /// </summary>
        /// <param name="message">A message to emit.</param>
        /// <param name="detailMessage">A detailed message to emit.</param>
        public override void Fail(string message, string detailMessage)
        {
            ProcessLogEventInfo(LogLevel.Error, null, message + " " + detailMessage, null, null);
        }

        /// <summary>
        ///     Flushes the output buffer.
        /// </summary>
        public override void Flush()
        {
            if (LogFactory != null)
            {
                LogFactory.Flush();
            }
            else
            {
                LogManager.Flush();
            }
        }

#if !NET_CF
        /// <summary>
        ///     Writes trace information, a data object and event information to the listener specific output.
        /// </summary>
        /// <param name="eventCache">
        ///     A <see cref="T:System.Diagnostics.TraceEventCache" /> object that contains the current process ID, thread ID, and stack trace information.
        /// </param>
        /// <param name="source">A name used to identify the output, typically the name of the application that generated the trace event.</param>
        /// <param name="eventType">
        ///     One of the <see cref="T:System.Diagnostics.TraceEventType" /> values specifying the type of event that has caused the trace.
        /// </param>
        /// <param name="id">A numeric identifier for the event.</param>
        /// <param name="data">The trace data to emit.</param>
        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
        {
            TraceData(eventCache, source, eventType, id, new[] {data});
        }

        /// <summary>
        ///     Writes trace information, an array of data objects and event information to the listener specific output.
        /// </summary>
        /// <param name="eventCache">
        ///     A <see cref="T:System.Diagnostics.TraceEventCache" /> object that contains the current process ID, thread ID, and stack trace information.
        /// </param>
        /// <param name="source">A name used to identify the output, typically the name of the application that generated the trace event.</param>
        /// <param name="eventType">
        ///     One of the <see cref="T:System.Diagnostics.TraceEventType" /> values specifying the type of event that has caused the trace.
        /// </param>
        /// <param name="id">A numeric identifier for the event.</param>
        /// <param name="data">An array of objects to emit as data.</param>
        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, params object[] data)
        {
            var sb = new StringBuilder();
            for (var i = 0; i < data.Length; ++i)
            {
                if (i > 0)
                {
                    sb.Append(", ");
                }

                sb.Append("{");
                sb.Append(i);
                sb.Append("}");
            }

            ProcessLogEventInfo(TranslateLogLevel(eventType), source, sb.ToString(), data, id);
        }

        /// <summary>
        ///     Writes trace and event information to the listener specific output.
        /// </summary>
        /// <param name="eventCache">
        ///     A <see cref="T:System.Diagnostics.TraceEventCache" /> object that contains the current process ID, thread ID, and stack trace information.
        /// </param>
        /// <param name="source">A name used to identify the output, typically the name of the application that generated the trace event.</param>
        /// <param name="eventType">
        ///     One of the <see cref="T:System.Diagnostics.TraceEventType" /> values specifying the type of event that has caused the trace.
        /// </param>
        /// <param name="id">A numeric identifier for the event.</param>
        public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id)
        {
            ProcessLogEventInfo(TranslateLogLevel(eventType), source, string.Empty, null, id);
        }

        /// <summary>
        ///     Writes trace information, a formatted array of objects and event information to the listener specific output.
        /// </summary>
        /// <param name="eventCache">
        ///     A <see cref="T:System.Diagnostics.TraceEventCache" /> object that contains the current process ID, thread ID, and stack trace information.
        /// </param>
        /// <param name="source">A name used to identify the output, typically the name of the application that generated the trace event.</param>
        /// <param name="eventType">
        ///     One of the <see cref="T:System.Diagnostics.TraceEventType" /> values specifying the type of event that has caused the trace.
        /// </param>
        /// <param name="id">A numeric identifier for the event.</param>
        /// <param name="format">
        ///     A format string that contains zero or more format items, which correspond to objects in the <paramref name="args" /> array.
        /// </param>
        /// <param name="args">An object array containing zero or more objects to format.</param>
        public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string format, params object[] args)
        {
            ProcessLogEventInfo(TranslateLogLevel(eventType), source, format, args, id);
        }

        /// <summary>
        ///     Writes trace information, a message, and event information to the listener specific output.
        /// </summary>
        /// <param name="eventCache">
        ///     A <see cref="T:System.Diagnostics.TraceEventCache" /> object that contains the current process ID, thread ID, and stack trace information.
        /// </param>
        /// <param name="source">A name used to identify the output, typically the name of the application that generated the trace event.</param>
        /// <param name="eventType">
        ///     One of the <see cref="T:System.Diagnostics.TraceEventType" /> values specifying the type of event that has caused the trace.
        /// </param>
        /// <param name="id">A numeric identifier for the event.</param>
        /// <param name="message">A message to write.</param>
        public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
        {
            ProcessLogEventInfo(TranslateLogLevel(eventType), source, message, null, id);
        }

        /// <summary>
        ///     Writes trace information, a message, a related activity identity and event information to the listener specific output.
        /// </summary>
        /// <param name="eventCache">
        ///     A <see cref="T:System.Diagnostics.TraceEventCache" /> object that contains the current process ID, thread ID, and stack trace information.
        /// </param>
        /// <param name="source">A name used to identify the output, typically the name of the application that generated the trace event.</param>
        /// <param name="id">A numeric identifier for the event.</param>
        /// <param name="message">A message to write.</param>
        /// <param name="relatedActivityId">
        ///     A <see cref="T:System.Guid" />  object identifying a related activity.
        /// </param>
        public override void TraceTransfer(TraceEventCache eventCache, string source, int id, string message, Guid relatedActivityId)
        {
            ProcessLogEventInfo(LogLevel.Debug, source, message, null, id);
        }

        /// <summary>
        ///     Gets the custom attributes supported by the trace listener.
        /// </summary>
        /// <returns>
        ///     A string array naming the custom attributes supported by the trace listener, or null if there are no custom attributes.
        /// </returns>
        protected override string[] GetSupportedAttributes()
        {
            return new[] {"defaultLogLevel", "autoLoggerName", "forceLogLevel"};
        }

        /// <summary>
        ///     Translates the event type to level from <see cref="TraceEventType" />.
        /// </summary>
        /// <param name="eventType">Type of the event.</param>
        /// <returns>Translated log level.</returns>
        private static LogLevel TranslateLogLevel(TraceEventType eventType)
        {
            switch (eventType)
            {
                case TraceEventType.Verbose:
                    return LogLevel.Trace;

                case TraceEventType.Information:
                    return LogLevel.Info;

                case TraceEventType.Warning:
                    return LogLevel.Warn;

                case TraceEventType.Error:
                    return LogLevel.Error;

                case TraceEventType.Critical:
                    return LogLevel.Fatal;

                default:
                    return LogLevel.Debug;
            }
        }
#endif

        private void ProcessLogEventInfo(LogLevel logLevel, string loggerName, [Localizable(false)] string message, object[] arguments, int? eventId)
        {
            var ev = new LogEventInfo();

            ev.LoggerName = (loggerName ?? Name) ?? string.Empty;

#if !NET_CF
            if (AutoLoggerName)
            {
                var stack = new StackTrace();
                var userFrameIndex = -1;
                MethodBase userMethod = null;

                for (var i = 0; i < stack.FrameCount; ++i)
                {
                    var frame = stack.GetFrame(i);
                    var method = frame.GetMethod();

                    if (method.DeclaringType == GetType())
                    {
                        // skip all methods of this type
                        continue;
                    }

                    if (method.DeclaringType.Assembly == systemAssembly)
                    {
                        // skip all methods from System.dll
                        continue;
                    }

                    userFrameIndex = i;
                    userMethod = method;
                    break;
                }

                if (userFrameIndex >= 0)
                {
                    ev.SetStackTrace(stack, userFrameIndex);
                    if (userMethod.DeclaringType != null)
                    {
                        ev.LoggerName = userMethod.DeclaringType.FullName;
                    }
                }
            }
#endif

            ev.TimeStamp = CurrentTimeGetter.Now;
            ev.Message = message;
            ev.Parameters = arguments;
            ev.Level = forceLogLevel ?? logLevel;

            if (eventId.HasValue)
            {
                ev.Properties.Add("EventID", eventId.Value);
            }

            var logger = LogManager.GetLogger(ev.LoggerName);
            logger.Log(ev);
        }

        private void InitAttributes()
        {
            if (!attributesLoaded)
            {
                attributesLoaded = true;
#if !NET_CF
                foreach (DictionaryEntry de in Attributes)
                {
                    var key = (string) de.Key;
                    var value = (string) de.Value;

                    switch (key.ToUpperInvariant())
                    {
                        case "DEFAULTLOGLEVEL":
                            defaultLogLevel = LogLevel.FromString(value);
                            break;

                        case "FORCELOGLEVEL":
                            forceLogLevel = LogLevel.FromString(value);
                            break;

                        case "AUTOLOGGERNAME":
                            AutoLoggerName = XmlConvert.ToBoolean(value);
                            break;
                    }
                }
#endif
            }
        }
    }
}

#endif

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 GNU General Public License (GPLv3)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions