File: System\Diagnostics\DelimitedListTraceListener.cs
Web Access
Project: src\src\libraries\System.Diagnostics.TextWriterTraceListener\src\System.Diagnostics.TextWriterTraceListener.csproj (System.Diagnostics.TextWriterTraceListener)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Text;
 
namespace System.Diagnostics
{
    public class DelimitedListTraceListener : TextWriterTraceListener
    {
        private const string DelimiterKey = "delimiter";
 
        private string _delimiter = ";";
        private string _secondaryDelim = ",";
        private bool _initializedDelim;
 
        public DelimitedListTraceListener(Stream stream) : base(stream)
        {
        }
 
        public DelimitedListTraceListener(Stream stream, string? name) : base(stream, name)
        {
        }
 
        public DelimitedListTraceListener(TextWriter writer) : base(writer)
        {
        }
 
        public DelimitedListTraceListener(TextWriter writer, string? name) : base(writer, name)
        {
        }
 
        public DelimitedListTraceListener(string? fileName) : base(fileName)
        {
        }
 
        public DelimitedListTraceListener(string? fileName, string? name) : base(fileName, name)
        {
        }
 
        public string Delimiter
        {
            get
            {
                lock (this)
                {
                    if (!_initializedDelim)
                    {
                        if (Attributes.ContainsKey(DelimiterKey))
                        {
                            string? delimiter = Attributes[DelimiterKey];
                            if (!string.IsNullOrEmpty(delimiter))
                            {
                                _delimiter = delimiter;
                            }
                        }
                        _initializedDelim = true;
                    }
                }
                return _delimiter;
            }
            set
            {
                ArgumentException.ThrowIfNullOrEmpty(value, nameof(Delimiter));
 
                lock (this)
                {
                    _delimiter = value;
                    _initializedDelim = true;
                }
 
                if (_delimiter == ",")
                    _secondaryDelim = ";";
                else
                    _secondaryDelim = ",";
            }
        }
 
        // base class method is protected internal but since its base class is in another assembly can't override it as protected internal because a CS0507
        // warning would be hitted.
        protected override string[] GetSupportedAttributes() => new string[] { DelimiterKey };
 
        public override void TraceEvent(TraceEventCache? eventCache, string source, TraceEventType eventType, int id, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? format, params object?[]? args)
        {
            if (Filter != null && !Filter.ShouldTrace(eventCache, source, eventType, id, format, args, null, null))
                return;
 
            WriteHeader(source, eventType, id);
 
            if (args != null)
                WriteEscaped(string.Format(CultureInfo.InvariantCulture, format!, args));
            else
                WriteEscaped(format);
            Write(Delimiter); // Use get_Delimiter
 
            // one more delimiter for the data object
            Write(Delimiter); // Use get_Delimiter
 
            WriteFooter(eventCache);
        }
 
        public override void TraceEvent(TraceEventCache? eventCache, string source, TraceEventType eventType, int id, string? message)
        {
            if (Filter != null && !Filter.ShouldTrace(eventCache, source, eventType, id, message, null, null, null))
                return;
 
            WriteHeader(source, eventType, id);
 
            WriteEscaped(message);
            Write(Delimiter); // Use get_Delimiter
 
            // one more delimiter for the data object
            Write(Delimiter); // Use get_Delimiter
 
            WriteFooter(eventCache);
        }
 
        public override void TraceData(TraceEventCache? eventCache, string source, TraceEventType eventType, int id, object? data)
        {
            if (Filter != null && !Filter.ShouldTrace(eventCache, source, eventType, id, null, null, data, null))
                return;
 
            WriteHeader(source, eventType, id);
 
            // first a delimiter for the message
            Write(Delimiter); // Use get_Delimiter
 
            WriteEscaped(data?.ToString());
            Write(Delimiter); // Use get_Delimiter
 
            WriteFooter(eventCache);
        }
 
        public override 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);
 
            // first a delimiter for the message
            Write(Delimiter); // Use get_Delimiter
 
            if (data != null)
            {
                for (int i = 0; i < data.Length; i++)
                {
                    if (i != 0)
                        Write(_secondaryDelim);
                    WriteEscaped(data[i]?.ToString());
                }
            }
            Write(Delimiter); // Use get_Delimiter
 
            WriteFooter(eventCache);
        }
 
        private void WriteHeader(string source, TraceEventType eventType, int id)
        {
            WriteEscaped(source);
            Write(Delimiter); // Use get_Delimiter
 
            Write(eventType.ToString());
            Write(Delimiter); // Use get_Delimiter
 
            Write(id.ToString(CultureInfo.InvariantCulture));
            Write(Delimiter); // Use get_Delimiter
        }
 
        private void WriteFooter(TraceEventCache? eventCache)
        {
            if (eventCache != null)
            {
                if (IsEnabled(TraceOptions.ProcessId))
                    Write(eventCache.ProcessId.ToString(CultureInfo.InvariantCulture));
                Write(Delimiter); // Use get_Delimiter
 
                if (IsEnabled(TraceOptions.LogicalOperationStack))
                    WriteStackEscaped(eventCache.LogicalOperationStack);
                Write(Delimiter); // Use get_Delimiter
 
                if (IsEnabled(TraceOptions.ThreadId))
                    WriteEscaped(eventCache.ThreadId);
                Write(Delimiter); // Use get_Delimiter
 
                if (IsEnabled(TraceOptions.DateTime))
                    WriteEscaped(eventCache.DateTime.ToString("o", CultureInfo.InvariantCulture));
                Write(Delimiter); // Use get_Delimiter
 
                if (IsEnabled(TraceOptions.Timestamp))
                    Write(eventCache.Timestamp.ToString(CultureInfo.InvariantCulture));
                Write(Delimiter); // Use get_Delimiter
 
                if (IsEnabled(TraceOptions.Callstack))
                    WriteEscaped(eventCache.Callstack);
            }
            else
            {
                for (int i = 0; i < 5; i++)
                    Write(Delimiter); // Use get_Delimiter
            }
 
            WriteLine("");
        }
 
        private void WriteEscaped(string? message)
        {
            if (!string.IsNullOrEmpty(message))
            {
                StringBuilder sb = new StringBuilder("\"");
                EscapeMessage(message, sb);
                sb.Append('"');
                Write(sb.ToString());
            }
        }
 
        private void WriteStackEscaped(Stack stack)
        {
            StringBuilder sb = new StringBuilder("\"");
            bool first = true;
            foreach (object? obj in stack)
            {
                if (!first)
                {
                    sb.Append(", ");
                }
                else
                {
                    first = false;
                }
 
                string? operation = obj?.ToString();
                EscapeMessage(operation, sb);
            }
 
            sb.Append('"');
            Write(sb.ToString());
        }
 
        private static void EscapeMessage(string? message, StringBuilder sb)
        {
            if (!string.IsNullOrEmpty(message))
            {
                int index;
                int lastindex = 0;
                while ((index = message.IndexOf('"', lastindex)) != -1)
                {
                    sb.Append(message, lastindex, index - lastindex);
                    sb.Append("\"\"");
                    lastindex = index + 1;
                }
 
                sb.Append(message, lastindex, message.Length - lastindex);
            }
        }
    }
}