Background:

You have a C# application, and you have enabled the tracing feature. Now you want to the tracing log to contain the local system date time along with the log entries.

Analysis:

Enable date time in the tracing is easy by simply add a DateTime option for the traceOutputOptions in your tracing config. But the tricky part is the tracing system always output UTC date time. Because by the implementation of the abstract class TraceListener, it hard coded the way of outputing the datetime value when detected the switch TraceOutputOptions.DateTime is open in its private method WriteFooter(eventCache), which looks like as below:

if (IsEnabled(TraceOptions.DateTime))
	WriteLine("DateTime=" + eventCache.DateTime.ToString("o", CultureInfo.InvariantCulture));

The full code of TraceListener would be listed in the last part of this post.

So here provided a workaround solution by intercepting the message passing to WriteLine() method, and converting the UTC time to local system time if detected.

Solution:

2 steps:

1. In your Tracing config file, add an attribute traceOutputOptions=”DateTime” to your tracing listeners, for example:

<add name="XmlLog" type="System.Diagnostics.XmlWriterTraceListener" initializeData="DebugInfo.xml" traceOutputOptions="DateTime"/>

2. In your own listener implementation code, override the abstract TraceListener’s WriteLine() method as follows:

        public override void WriteLine(string value)
        {
            // The abstract class TraceListener's private method WriteFooter(eventCache)
            // hard coded the way to output the datetime value when detected 
            // the switch TraceOutputOptions.DateTime is open:
            //  if (IsEnabled(TraceOptions.DateTime))
            //      WriteLine("DateTime=" + eventCache.DateTime.ToString("o", CultureInfo.InvariantCulture));
            // So it is hard to customize the datetime kind in the trace log, which is always
            // showing like this: DateTime=2013-07-22T03:34:14.1736743Z
            // Here intercepts the message who meets the above pattern, and converts it to
            // a local datetime fashion.
            string pattern = @"^(DateTime=)(\d{4}-[0-1]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-6]\d.\d{7}Z)$";
            Regex regex = new Regex(pattern, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
            
            value = regex.Replace(value, m =>
            {
                DateTime dt;

                if (DateTime.TryParse(m.Groups[2].Value, out dt))
                {
                    return string.Format("{0}{1}",
                        m.Groups[1].Value,
                        dt.ToLocalTime().ToString(System.Globalization.CultureInfo.InvariantCulture));
                }
                else
                {
                    return m.Value;
                }
            });

            this.traceWriter.WriteLine(value);
        }

Effectiveness & Screen shot:

How to make tracing log local system date time in tracing (C#)

Appendix:

The source code of TraceListener (from http://www.dotnetframework.org/default.aspx/[email protected]/[email protected]/untmp/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/fx/src/CompMod/System/Diagnostics/[email protected]/1305376/[email protected] ):

//------------------------------------------------------------------------------
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//-----------------------------------------------------------------------------
 
/*
 */
namespace System.Diagnostics {
    using System;
    using System.Text;
    using System.Security.Permissions;
    using System.Collections.Specialized;
    using System.Globalization;
    using System.Runtime.InteropServices;
    using System.Collections;
    using System.Configuration;
    using System.Runtime.Versioning;
  
    /// 
    /// Provides the base class for the listeners who
    ///    monitor trace and debug output.
    /// 
    [HostProtection(Synchronization=true)]
    public abstract class TraceListener : MarshalByRefObject, IDisposable {
  
        int indentLevel;
        int indentSize = 4;
        TraceOptions traceOptions = TraceOptions.None;
        bool needIndent = true;
 
        string listenerName;
        TraceFilter filter = null;
        StringDictionary attributes;
        internal string initializeData;
 
        /// 
        /// Initializes a new instance of the  class.
        /// 
        protected TraceListener () {
        }
 
        /// 
        /// Initializes a new instance of the  class using the specified name as the
        ///    listener.
        /// 
        protected TraceListener(string name) {
            this.listenerName = name;
        }
  
        public StringDictionary Attributes {
            get {
                if (attributes == null)
                    attributes = new StringDictionary();
                return attributes;
            }
        }
 
        /// 
        ///  Gets or sets a name for this .
        /// 
        public virtual string Name {
            get { return (listenerName == null) ? "" : listenerName; }
  
            set { listenerName = value; }
        }
 
        public virtual bool IsThreadSafe {
            get { return false; }
        }
  
        /// 
        /// 
        public void Dispose() {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
 
        /// 
        /// 
        protected virtual void Dispose(bool disposing) {
            return;
        }
 
 
        /// 
        ///    When overridden in a derived class, closes the output stream
        ///       so that it no longer receives tracing or debugging output.
        /// 
        public virtual void Close() {
            return;
        }
 
        /// 
        ///    When overridden in a derived class, flushes the output buffer.
        /// 
        public virtual void Flush() {
            return;
        }
  
        /// 
        ///    Gets or sets the indent level.
        /// 
        public int IndentLevel {
            get {
                return indentLevel;
            }
 
            set {
                indentLevel = (value < 0) ? 0 : value;
            }
        }
  
        /// 
        ///    Gets or sets the number of spaces in an indent.
        /// 
        public int IndentSize {
            get {
                return indentSize;
            }
 
            set {
                if (value < 0)
                    throw new ArgumentOutOfRangeException("IndentSize", value, SR.GetString(SR.TraceListenerIndentSize));
                indentSize = value;
            }
        }
 
        [
        ComVisible(false)
        ]
        public TraceFilter Filter {
            get {
                return filter;
            }
            set {
                filter = value;
            }
        }
  
 
        /// 
        ///    Gets or sets a value indicating whether an indent is needed.
        /// 
        protected bool NeedIndent {
            get {
                return needIndent;
            }
  
            set {
                needIndent = value;
            }
        }
  
        [
        ComVisible(false)
        ]
        public TraceOptions TraceOutputOptions {
            get { return traceOptions; }
            set {
                if (( (int) value >> 6) != 0) {
                    throw new ArgumentOutOfRangeException("value");
                }
 
                traceOptions = value;
            }
        }
 
        internal void SetAttributes(Hashtable attribs) {
            TraceUtils.VerifyAttributes(attribs, GetSupportedAttributes(), this);
 
            attributes = new StringDictionary();
            attributes.ReplaceHashtable(attribs);
        }
 
        /// 
        ///    Emits or displays a message for an assertion that always fails.
        /// 
        public virtual void Fail(string message) {
            Fail(message, null);
        }
 
        /// 
        ///    Emits or displays messages for an assertion that always fails.
        /// 
        public virtual void Fail(string message, string detailMessage) {
            StringBuilder failMessage = new StringBuilder();
            failMessage.Append(SR.GetString(SR.TraceListenerFail));
            failMessage.Append(" ");
            failMessage.Append(message);
            if (detailMessage != null) {
                failMessage.Append(" ");
                failMessage.Append(detailMessage);
            }
 
            WriteLine(failMessage.ToString());
        }
 
        virtual protected internal string[] GetSupportedAttributes() {
            return null;
        }
 
        /// 
        ///    When overridden in a derived class, writes the specified
        ///       message to the listener you specify in the derived class.
        /// 
        public abstract void Write(string message);
  
        /// 
        /// Writes the name of the  parameter to the listener you specify when you inherit from the 
        /// class.
        /// 
        public virtual void Write(object o) {
            if (Filter != null && !Filter.ShouldTrace(null, "", TraceEventType.Verbose, 0, null, null, o))
                return;
 
            if (o == null) return;
            Write(o.ToString());
        }
 
        /// 
        ///    Writes a category name and a message to the listener you specify when you
        ///       inherit from the 
        ///       class.
        /// 
        public virtual void Write(string message, string category) {
            if (Filter != null && !Filter.ShouldTrace(null, "", TraceEventType.Verbose, 0, message))
                return;
 
            if (category == null)
                Write(message);
            else
                Write(category + ": " + ((message == null) ? string.Empty : message));
        }
  
        /// 
        /// Writes a category name and the name of the  parameter to the listener you
        ///    specify when you inherit from the 
        ///    class.
        /// 
        public virtual void Write(object o, string category) {
            if (Filter != null && !Filter.ShouldTrace(null, "", TraceEventType.Verbose, 0, category, null, o))
                return;
  
            if (category == null)
                Write(o);
            else
                Write(o == null ? "" : o.ToString(), category);
        }
  
        /// 
        ///    Writes the indent to the listener you specify when you
        ///       inherit from the 
        ///       class, and resets the  property to .
        /// 
        protected virtual void WriteIndent() {
            NeedIndent = false;
            for (int i = 0; i < indentLevel; i++) {
                if (indentSize == 4)
                    Write("    ");
                else {
                    for (int j = 0; j < indentSize; j++) {
                        Write(" ");
                    }
                }
           }
        }
  
        /// 
        ///    When overridden in a derived class, writes a message to the listener you specify in
        ///       the derived class, followed by a line terminator. The default line terminator is a carriage return followed
        ///       by a line feed (\r\n).
        /// 
        public abstract void WriteLine(string message);
  
        /// 
        /// Writes the name of the  parameter to the listener you specify when you inherit from the  class, followed by a line terminator. The default line terminator is a
        ///    carriage return followed by a line feed
        ///    (\r\n).
        /// 
        public virtual void WriteLine(object o) {
            if (Filter != null && !Filter.ShouldTrace(null, "", TraceEventType.Verbose, 0, null, null, o))
                return;
  
            WriteLine(o == null ? "" : o.ToString());
        }
  
        /// 
        ///    Writes a category name and a message to the listener you specify when you
        ///       inherit from the  class,
        ///       followed by a line terminator. The default line terminator is a carriage return followed by a line feed (\r\n).
        /// 
        public virtual void WriteLine(string message, string category) {
            if (Filter != null && !Filter.ShouldTrace(null, "", TraceEventType.Verbose, 0, message))
                return;
            if (category == null)
                WriteLine(message);
            else
                WriteLine(category + ": " + ((message == null) ? string.Empty : message));
        }
 
        /// 
        ///    Writes a category
        ///       name and the name of the parameter to the listener you
        ///       specify when you inherit from the 
        ///       class, followed by a line terminator. The default line terminator is a carriage
        ///       return followed by a line feed (\r\n).
        /// 
        public virtual void WriteLine(object o, string category) {
            if (Filter != null && !Filter.ShouldTrace(null, "", TraceEventType.Verbose, 0, category, null, o))
                return;
 
            WriteLine(o == null ? "" : o.ToString(), category);
        }
 
  
        // new write methods used by TraceSource
 
        [
        ComVisible(false)
        ]
        public virtual void TraceData(TraceEventCache eventCache, String source, TraceEventType eventType, int id, object data) {
            if (Filter != null && !Filter.ShouldTrace(eventCache, source, eventType, id, null, null, data))
                return;
  
            WriteHeader(source, eventType, id);
            string datastring = String.Empty;
            if (data != null)
                datastring = data.ToString();
 
            WriteLine(datastring);
            WriteFooter(eventCache);
        }
  
        [
        ComVisible(false)
        ]
        public virtual void TraceData(TraceEventCache eventCache, String source, TraceEventType eventType, int id, params object[] data) {
            if (Filter != null && !Filter.ShouldTrace(eventCache, source, eventType, id, null, null, null, data))
                return;
  
            WriteHeader(source, eventType, id);
  
            StringBuilder sb = new StringBuilder();
            if (data != null) {
                for (int i=0; i< data.Length; i++) {
                    if (i != 0)
                        sb.Append(", ");
  
                    if (data[i] != null)
                        sb.Append(data[i].ToString());
                }
            }
            WriteLine(sb.ToString());
 
            WriteFooter(eventCache);
        }
  
        [
        ComVisible(false)
        ]
        public virtual void TraceEvent(TraceEventCache eventCache, String source, TraceEventType eventType, int id) {
            TraceEvent(eventCache, source, eventType, id, String.Empty);
        }
  
        // All other TraceEvent methods come through this one.
        [
        ComVisible(false)
        ]
        public virtual void TraceEvent(TraceEventCache eventCache, String source, TraceEventType eventType, int id, string message) {
            if (Filter != null && !Filter.ShouldTrace(eventCache, source, eventType, id, message))
                return;
 
            WriteHeader(source, eventType, id);
            WriteLine(message);
  
            WriteFooter(eventCache);
        }
  
        [
        ComVisible(false)
        ]
        public virtual void TraceEvent(TraceEventCache eventCache, String source, TraceEventType eventType, int id, string format, params object[] args) {
            if (Filter != null && !Filter.ShouldTrace(eventCache, source, eventType, id, format, args))
                return;
  
            WriteHeader(source, eventType, id);
            if (args != null)
                WriteLine(String.Format(CultureInfo.InvariantCulture, format, args));
            else
                WriteLine(format);
  
            WriteFooter(eventCache);
        }
  
        [
        ComVisible(false)
        ]
        public virtual void TraceTransfer(TraceEventCache eventCache, String source, int id, string message, Guid relatedActivityId) {
            TraceEvent(eventCache, source, TraceEventType.Transfer, id, message + ", relatedActivityId=" + relatedActivityId.ToString());
        }
 
        private void WriteHeader(String source, TraceEventType eventType, int id) {
            Write(String.Format(CultureInfo.InvariantCulture, "{0} {1}: {2} : ", source, eventType.ToString(), id.ToString(CultureInfo.InvariantCulture)));
        }
  
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private void WriteFooter(TraceEventCache eventCache) {
            if (eventCache == null)
                return;
  
            indentLevel++;
            if (IsEnabled(TraceOptions.ProcessId))
                WriteLine("ProcessId=" + eventCache.ProcessId);
 
            if (IsEnabled(TraceOptions.LogicalOperationStack)) {
                Write("LogicalOperationStack=");
                Stack operationStack = eventCache.LogicalOperationStack;
                bool first = true;
                foreach (Object obj in operationStack) {
                    if (!first)
                        Write(", ");
                    else
                        first = false;
 
                    Write(obj.ToString());
                }
                WriteLine(String.Empty);
            }
  
            if (IsEnabled(TraceOptions.ThreadId))
                WriteLine("ThreadId=" + eventCache.ThreadId);
 
            if (IsEnabled(TraceOptions.DateTime))
                WriteLine("DateTime=" + eventCache.DateTime.ToString("o", CultureInfo.InvariantCulture));
  
            if (IsEnabled(TraceOptions.Timestamp))
                WriteLine("Timestamp=" + eventCache.Timestamp);
  
            if (IsEnabled(TraceOptions.Callstack))
                WriteLine("Callstack=" + eventCache.Callstack);
            indentLevel--;
        }
 
        internal bool IsEnabled(TraceOptions opts) {
            return (opts & TraceOutputOptions) != 0;
        }
    }
}
 
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.