How to modify trace format for System Diagnostics Trace Listener?

Mar 30, 2012 at 11:15 PM

I'm using System Diagnostics Trace Listener to log trace messages. I only select DateTime in "Trace Output Options" when I configured it. However, LAB logged the DateTime info + all the default Text Formatter info. How do I modify the Text Formatter to just print my message?

Here is the snippet for System Diagnostics Trace Listener configuration:

      <add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.SystemDiagnosticsTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        type="System.Diagnostics.DefaultTraceListener, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
        traceOutputOptions="DateTime" name="System Diagnostics Trace Listener" />

Here is the trace output snippet:

[8600] DateTime=2012-03-30T22:53:09.9271826Z 
[8600] General Information: 100 : 
[8600] Timestamp: 3/30/2012 10:53:09 PM 
[8600] Message:  Calling GetClientId with package id(0140VI11874510Z): 
[8600] Category: General 
[8600] Priority: -1 
[8600] EventId: 100 
[8600] Severity: Information 
[8600] Title: 
[8600] Machine: FHRDEV949 
[8600] App Domain: /LM/W3SVC/1/ROOT/ShipSystemService-1-129776214738920948 
[8600] ProcessId: 8600 
[8600] Process Name: c:\windows\system32\inetsrv\w3wp.exe 
[8600] Thread Name:  
[8600] Win32 ThreadId:6340 
[8600] Extended Properties:  

Thanks in advance!

Cindy

Mar 31, 2012 at 1:14 AM
Edited Mar 31, 2012 at 1:44 AM

There are 2 things happening here: the first is that the LogEntry uses the default ToString() TextFormatter implementation to format the output to the DefaultTraceListener and the second is that Enterprise Library calls the TraceData method on the DefaultTraceListener which writes out header information (e.g. "General Information: 100 :").

See this thread for an explanation.  If all you want to see in DebugView is your message then you will need to follow the steps outlined in that thread.  So, in your instance you would need to extend LogEntry and override ToString() and extend the DefaultTraceListener and have the TraceData method call Write:

    public class MyLogEntry : LogEntry
    {
        private static readonly TextFormatter toStringFormatter = new TextFormatter("{message}");

        public override string ToString()
        {
            return toStringFormatter.Format(this);
        }
    }

    public class MyDefaultTraceListener : DefaultTraceListener
    {
        public MyDefaultTraceListener () : base()
        {
        }

        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
        {
            base.Write(data);
        }
    }

Also, the configuration would have to be changed to use the new MyDefaultTraceListener:

        <listeners>
            <add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.SystemDiagnosticsTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                type="LoggingOutputDebugTraceListener.MyDefaultTraceListener, LoggingOutputDebugTraceListener"
                name="System Diagnostics Trace Listener" />
        </listeners>

Then you could invoke the method as per usual:

static void Main(string[] args)
{
    var logger = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();

    var le = new MyLogEntry();
    le.Categories.Add("General");
    le.Message = "My Custom Message";
    logger.Write(le);
}

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com 

Apr 5, 2012 at 6:32 PM

Hi Randy,

To use TextFormatter class, which dll I need to add in my reference? I have the reference Microsoft.Practices.EnterpriseLibrary.Logging.dll in my project, but it is not good for Microsoft.Practices.EnterpriseLibrary.Logging.Properties.Formatters namespace.

Thanks,

Cindy

Apr 5, 2012 at 6:48 PM

I solved my problem. I should use the namespace -

Microsoft.Practices.EnterpriseLibrary.Logging.Formatters

Cindy

Aug 15, 2013 at 5:45 PM
Edited Aug 15, 2013 at 5:50 PM
I had this same issue. I ended up creating a fully custom trace listener following the example of FlatFileTraceListener which outputs to the attached debugger (e.g. VisualStudio or DebugView). This listener supports a custom formatter and header/footer options.

With the assembly containing this listener in the same folder as EntLibConfig.exe, the listener should automatically show up in the list as "Debugger Trace Listener".

Any suggestions are welcome.

-- Terrence Jones
[Description("A logging target listener that sends log messages an attached debugger (e.g. Visual Studio, DebugView)")]
[DisplayName("Debugger Trace Listener")]
public class DebuggerTraceListenerData : TraceListenerData
{
    private const string footerProperty = "footer";
    private const string formatterProperty = "formatter";
    private const string headerProperty = "header";

    public DebuggerTraceListenerData()
        : base(typeof(DebuggerTraceListener))
    {
        base.ListenerDataType = typeof(DebuggerTraceListenerData);
    }

    public DebuggerTraceListenerData(string formatterName)
        : this("unnamed", formatterName)
    {
    }

    public DebuggerTraceListenerData(string name, string formatterName)
        : this(name, typeof(DebuggerTraceListener), formatterName)
    {
    }

    public DebuggerTraceListenerData(string name, Type listenerType, string formatterName)
        : this(name, listenerType, formatterName, TraceOptions.None)
    {
    }

    public DebuggerTraceListenerData(string name, string header, string footer, string formatterName)
        : this(name, header, footer, formatterName, TraceOptions.None)
    {
    }

    public DebuggerTraceListenerData(string name, Type listenerType, string formatterName, TraceOptions traceOutputOptions)
        : base(name, listenerType, traceOutputOptions)
    {
        this.Formatter = formatterName;
    }

    public DebuggerTraceListenerData(string name, string header, string footer, string formatterName, TraceOptions traceOutputOptions)
        : this(name, typeof(DebuggerTraceListener), formatterName, traceOutputOptions)
    {
        this.Header = header;
        this.Footer = footer;
    }

    /// <summary>
    /// Builds the System.Diagnostics.TraceListener object represented by this configuration object.
    /// </summary>
    /// <param name="settings">The logging configuration settings.</param>
    /// <returns>A trace listener.</returns>
    protected override TraceListener CoreBuildTraceListener(LoggingSettings settings)
    {
        return new DebuggerTraceListener(this.Header, this.Footer, base.BuildFormatterSafe(settings, this.Formatter));
    }

    [Description("The name of the Log Message Formatter to use to format log messages written.")]
    [ConfigurationProperty(formatterProperty, IsRequired = false)]
    [Reference(typeof(NameTypeConfigurationElementCollection<FormatterData, CustomFormatterData>), typeof(FormatterData))]
    public string Formatter
    {
        get
        {
            return (string)base[formatterProperty];
        }
        set
        {
            base[formatterProperty] = value;
        }
    }

    [DisplayName("Message Footer")]
    [Description("The text of the footer to add to the log message.")]
    [ConfigurationProperty(footerProperty, IsRequired = false)]
    public string Footer
    {
        get
        {
            return (string)base[footerProperty];
        }
        set
        {
            base[footerProperty] = value;
        }
    }

    [DisplayName("Message Header")]
    [Description("The text of the header to add to the log message.")]
    [ConfigurationProperty(headerProperty, IsRequired = false)]
    public string Header
    {
        get
        {
            return (string)base[headerProperty];
        }
        set
        {
            base[headerProperty] = value;
        }
    }
}


/// <summary>
/// Implements a TraceListener which outputs to the attached debugger (e.g. VisualStudio, DebugView)
/// </summary>
/// <remarks>To obtain DebugView see: http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx </remarks>
[ConfigurationElementType(typeof(DebuggerTraceListenerData))]
public class DebuggerTraceListener : TraceListener
{
    public DebuggerTraceListener()
        : this(null)
    { }

    public DebuggerTraceListener(ILogFormatter formatter)
        : this(string.Empty, string.Empty, formatter)
    { }

    public DebuggerTraceListener(string header, string footer, ILogFormatter formatter)
    {
        Header = header ?? string.Empty;
        Footer = footer ?? string.Empty;
        Formatter = formatter;
    }

    /// <summary>
    /// Declares "formatter", "header", and "footer" as a supported attribute names.
    /// </summary>
    protected override string[] GetSupportedAttributes()
    {
        return new string[] { "formatter", "header", "footer" };
    }

    /// <summary>
    /// Gets the Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.ILogFormatter
    ///     used to format the trace messages.
    /// </summary>
    public ILogFormatter Formatter { get; set; }

    //The text of the Header to add to the log message.
    private string Header { get; set; }

    /// <summary>
    /// The text of the footer to add to the log message.
    /// </summary>
    private string Footer { get; set; }

    /// <summary>
    /// Delivers the trace data to attached debugger.
    /// </summary>
    /// <param name="eventCache">The context information provided by System.Diagnostics.</param>
    /// <param name="source">The name of the trace source that delivered the trace data.</param>
    /// <param name="eventType">The type of event.</param>
    /// <param name="id">The id of the event.</param>
    /// <param name="data">The data to trace.</param>
    public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
    {
        if ((base.Filter == null) || base.Filter.ShouldTrace(eventCache, source, eventType, id, null, null, data, null))
        {
            if (this.Header.Length > 0)
                WriteLine(this.Header);

            if (data is LogEntry)
            {
                if (Formatter != null)
                    WriteLine(Formatter.Format(data as LogEntry));
                else
                    base.TraceData(eventCache, source, eventType, id, data);
            }
            else
                base.TraceData(eventCache, source, eventType, id, data);

            if (this.Footer.Length > 0)
                WriteLine(this.Footer);
        }
    }

    /// <summary>
    /// Writes a message to this instance's System.Diagnostics.TextWriterTraceListener.Writer.
    /// </summary>
    /// <param name="message">A message to write.</param>
    [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
    public override void Write(string message)
    {
        if (base.NeedIndent)
        {
            this.WriteIndent();
        }
        if ((message == null) || (message.Length <= 0x4000))
        {
            this.InternalWrite(message);
        }
        else
        {
            int startIndex = 0;
            while (startIndex < (message.Length - 0x4000))
            {
                this.InternalWrite(message.Substring(startIndex, 0x4000));
                startIndex += 0x4000;
            }
            this.InternalWrite(message.Substring(startIndex));
        }
    }

    /// <summary>
    /// Writes a message to this instance's System.Diagnostics.TextWriterTraceListener.Writer
    /// followed by a line terminator. The default line terminator is a carriage
    /// return followed by a line feed (\r\n).
    /// </summary>
    /// <param name="message">A message to write.</param>
    [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
    public override void WriteLine(string message)
    {
        if (base.NeedIndent)
        {
            this.WriteIndent();
        }
        this.Write(message + Environment.NewLine);
        base.NeedIndent = true;
    }

    private void InternalWrite(string message)
    {
        // Either send the message to the Debugger or to Debug Output
        // NOTE: We use OutputDebugString here rather than Trace() because it is accessible in Release builds
        //
        if (Debugger.IsLogging())
            Debugger.Log(0, null, message);
        else
            OutputDebugString(message ?? string.Empty);
    }

    [DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, BestFitMapping = true)]
    [ResourceExposure(ResourceScope.None)]
    private static extern void OutputDebugString(String message);
}