The Logging Behaviour

Many people have been blogging about the new abilities and functionality of Expression Blend 3. Having had a look at this new product I tried writing some Behaviors for myself.

Behaviors are verbs in their simplest form. Dropped onto a UIElement or such like, these verbs add extensibility to the selected element. So, a verb might be “Shimmer” or “Fade” thus allowing the element to “Shimmer”, “Fade”, etc.

So my simplest start, and being a developer that just loves filling logs full of messages during debug sessions, I decided on building a logger that could be attached to any event and hence log a entry in the event log.

For this adventure I decided on using the Enterprise Library 4.1, but the example could be changed for the logger of choice. Hence the following:

Blog2

The configuration within the App.Config for the logging is:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </configSections>
    <loggingConfiguration name="Logging Application Block" tracingEnabled="true"
        defaultCategory="General" logWarningsWhenNoCategoriesMatch="true">
        <listeners>
            <add source="Enterprise Library Logging" formatter="Text Formatter"
                log="Application" machineName="" listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FormattedEventLogTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                traceOutputOptions="None" filter="All" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FormattedEventLogTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                name="Formatted EventLog TraceListener" />
        </listeners>
        <formatters>
            <add template="Timestamp: {timestamp}&#xD;&#xA;Message: {message}&#xD;&#xA;Category: {category}&#xD;&#xA;Priority: {priority}&#xD;&#xA;EventId: {eventid}&#xD;&#xA;Severity: {severity}&#xD;&#xA;Title:{title}&#xD;&#xA;Machine: {machine}&#xD;&#xA;Application Domain: {appDomain}&#xD;&#xA;Process Id: {processId}&#xD;&#xA;Process Name: {processName}&#xD;&#xA;Win32 Thread Id: {win32ThreadId}&#xD;&#xA;Thread Name: {threadName}&#xD;&#xA;Extended Properties: {dictionary({key} - {value}&#xD;&#xA;)}"
                type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                name="Text Formatter" />
        </formatters>
        <categorySources>
            <add switchValue="All" name="General">
                <listeners>
                    <add name="Formatted EventLog TraceListener" />
                </listeners>
            </add>
        </categorySources>
        <specialSources>
            <allEvents switchValue="All" name="All Events" />
            <notProcessed switchValue="All" name="Unprocessed Category" />
            <errors switchValue="All" name="Logging Errors &amp; Warnings">
                <listeners>
                    <add name="Formatted EventLog TraceListener" />
                </listeners>
            </errors>
        </specialSources>
    </loggingConfiguration>
</configuration>

 

The complete code is here:

using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Interactivity;
using Microsoft.Practices.EnterpriseLibrary.Logging;

namespace Behaviors
{
    /// <summary>
    /// The <see cref="LoggingBehaviour"/> class.
    /// </summary>
    public class LoggingBehaviour : TriggerAction<UIElement>
    {
        private LogEntry logEntry;

        #region Dependency Properties

        public static readonly DependencyProperty EventIdProperty;

        public static readonly DependencyProperty MessageProperty;

        public static readonly DependencyProperty PriorityProperty;

        public static readonly DependencyProperty SeverityProperty;

        public static readonly DependencyProperty TitleProperty;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes the <see cref="LoggingBehaviour"/> class.
        /// </summary>
        static LoggingBehaviour()
        {
            EventIdProperty =
                DependencyProperty.Register("EventId",
                                    typeof (int),
                                    typeof (LoggingBehaviour),
                                    new UIPropertyMetadata(0));

            MessageProperty =
                DependencyProperty.Register("Message",
                                      typeof (string),
                                      typeof (LoggingBehaviour),
                                      new  UIPropertyMetadata(string.Empty));

            PriorityProperty =
                DependencyProperty.Register("Priority",
                                    typeof (int),
                                    typeof (LoggingBehaviour),
                                    new UIPropertyMetadata(0));

            SeverityProperty =
                DependencyProperty.Register("Severity",
                                    typeof (TraceEventType),
                                    typeof (LoggingBehaviour),
                                    new UIPropertyMetadata(
                                    TraceEventType.Information));

            TitleProperty =
                DependencyProperty.Register("Title",
                                    typeof (string),
                                    typeof (LoggingBehaviour),
                                    new UIPropertyMetadata(string.Empty));
        }

        #endregion

        #region Properties

        /// <summary>
        /// Used to filter Log Entries, only those above the
        /// "Minimum Priority" are processd
        /// (defaults to -1 which indicates that the Minimum
        /// Priority should be used).
        /// </summary>
        /// <value>The priority.</value>
        public int Priority
        {
            get { return (int) GetValue(PriorityProperty); }
            set { SetValue(PriorityProperty, value); }
        }

        /// <summary>
        /// A value you can use to further categorise Log Entries
        /// (defaults to 0 for a LogEntry and to 1
        /// for a LogEntry implicitly created by Logger.Write).
        /// </summary>
        /// <value>The event id.</value>
        public int EventId
        {
            get { return (int) GetValue(EventIdProperty); }
            set { SetValue(EventIdProperty, value); }
        }

        /// <summary>
        /// Indicates the severity of the Log Entry, e.g.,
        /// Information, Warning, Error etc.
        /// </summary>
        /// <value>The severity.</value>
        public TraceEventType Severity
        {
            get { return (TraceEventType) GetValue(SeverityProperty); }
            set { SetValue(SeverityProperty, value); }
        }

        /// <summary>
        /// A summary of the LogEntry.Message.
        /// </summary>
        /// <value>The title.</value>
        public string Title
        {
            get { return (string) GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }

        /// <summary>
        /// The message to write to the log file.
        /// </summary>
        /// <value>The message.</value>
        public string Message
        {
            get { return (string) GetValue(MessageProperty); }
            set { SetValue(MessageProperty, value); }
        }

        #endregion

        #region TriggerAction overrides

        /// <summary>
        /// Invokes the action.
        /// </summary>
        /// <param name="parameter">The parameter to the action.
        /// If the Action does not require a parameter, the
        /// parameter may be set to a null reference.
        /// </param>
        protected override void Invoke(object parameter)
        {
            try
            {
                logEntry = new LogEntry
                {
                    EventId = EventId,
                    Priority = Priority,
                    Title = Title,
                    Severity = Severity,
                    Message = Message
                };
                Logger.Write(logEntry);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Called after the action is attached to an AssociatedObject.
        /// </summary>
        protected override void OnAttached()
        {
            // here you could setup the logger
            base.OnAttached();
        }

        /// <summary>
        /// Called when the action is being detached from its
        /// AssociatedObject, but before it has actually occurred.
        /// </summary>
        protected override void OnDetaching()
        {
            // tear down what may have been setup in OnAttached
            base.OnDetaching();
        }

        #endregion
    }
}

Within Expression Blend 3.0 you would use the normal “drag and drop” method to attach the Behaviour.

Blog2

Configuring the Logging Behaviour in Expression Blend is achived through the properties panel as shown below.

Blog3

Blog4

The markup within the XAML is as follows:

<Button x:Name="button" HorizontalAlignment="Left"
       Margin="44,17,0,26"
       Width="83"
       Content="Button"
       Grid.Column="1"
       Grid.Row="16" >
      <i:Interaction.Triggers>
           <i:EventTrigger EventName="Click">
                <Behaviors:LoggingBehaviour Message="my message" Priority="100" Title="Title"/>
           </i:EventTrigger>
      </i:Interaction.Triggers>
</Button>

The output in the event log when the button has been clicked is:

Blog5

As you can see it is pretty simple stuff in this case. However, the complexity of a Behavior is only limited by your imagination and the Blend infrastructure.

Advertisements

~ by Intelligence4 on August 29, 2009.

 
%d bloggers like this: