File: System\Diagnostics\TraceInternal.cs
Web Access
Project: src\src\libraries\System.Diagnostics.TraceSource\src\System.Diagnostics.TraceSource.csproj (System.Diagnostics.TraceSource)
// 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.IO;
using System.Reflection;
using System.Threading;
 
namespace System.Diagnostics
{
    internal static class TraceInternal
    {
        private sealed class TraceProvider : DebugProvider
        {
#pragma warning disable CS8770 // Method lacks `[DoesNotReturn]` annotation to match overridden member.
            public override void Fail(string? message, string? detailMessage) { TraceInternal.Fail(message, detailMessage); }
#pragma warning restore CS8770
            public override void OnIndentLevelChanged(int indentLevel)
            {
                lock (TraceInternal.critSec)
                {
                    foreach (TraceListener listener in Listeners)
                    {
                        listener.IndentLevel = indentLevel;
                    }
                }
            }
 
            public override void OnIndentSizeChanged(int indentSize)
            {
                lock (TraceInternal.critSec)
                {
                    foreach (TraceListener listener in Listeners)
                    {
                        listener.IndentSize = indentSize;
                    }
                }
            }
            public override void Write(string? message) { TraceInternal.Write(message); }
            public override void WriteLine(string? message) { TraceInternal.WriteLine(message); }
        }
 
        private static volatile string? s_appName;
        private static volatile TraceListenerCollection? s_listeners;
        private static volatile bool s_autoFlush;
        private static volatile bool s_useGlobalLock;
        private static volatile bool s_settingsInitialized;
        private static volatile bool s_settingsInitializing;
 
 
        // this is internal so TraceSource can use it.  We want to lock on the same object because both TraceInternal and
        // TraceSource could be writing to the same listeners at the same time.
        internal static readonly object critSec = new object();
 
        public static TraceListenerCollection Listeners
        {
            get
            {
                InitializeSettings();
                if (s_listeners == null)
                {
                    lock (critSec)
                    {
                        if (s_listeners == null)
                        {
                            // This is where we override default DebugProvider because we know
                            // for sure that we have some Listeners to write to.
                            Debug.SetProvider(new TraceProvider());
                            // In the absence of config support, the listeners by default add
                            // DefaultTraceListener to the listener collection.
                            s_listeners = new TraceListenerCollection();
                            TraceListener defaultListener = new DefaultTraceListener();
                            defaultListener.IndentLevel = Debug.IndentLevel;
                            defaultListener.IndentSize = Debug.IndentSize;
                            s_listeners.Add(defaultListener);
                        }
                    }
                }
                return s_listeners;
            }
        }
 
        internal static string AppName => s_appName ??= Assembly.GetEntryAssembly()?.GetName().Name ?? string.Empty;
 
        public static bool AutoFlush
        {
            get
            {
                InitializeSettings();
                return s_autoFlush;
            }
 
            set
            {
                InitializeSettings();
                s_autoFlush = value;
            }
        }
 
        public static bool UseGlobalLock
        {
            get
            {
                InitializeSettings();
                return s_useGlobalLock;
            }
 
            set
            {
                InitializeSettings();
                s_useGlobalLock = value;
            }
        }
 
        public static int IndentLevel
        {
            get { return Debug.IndentLevel; }
 
            set
            {
                Debug.IndentLevel = value;
            }
        }
 
        public static int IndentSize
        {
            get
            {
                return Debug.IndentSize;
            }
 
            set
            {
                Debug.IndentSize = value;
            }
        }
 
        public static void Indent()
        {
            Debug.IndentLevel++;
        }
 
        public static void Unindent()
        {
            Debug.IndentLevel--;
        }
 
        public static void Flush()
        {
            if (s_listeners != null)
            {
                if (UseGlobalLock)
                {
                    lock (critSec)
                    {
                        foreach (TraceListener listener in Listeners)
                        {
                            listener.Flush();
                        }
                    }
                }
                else
                {
                    foreach (TraceListener listener in Listeners)
                    {
                        if (!listener.IsThreadSafe)
                        {
                            lock (listener)
                            {
                                listener.Flush();
                            }
                        }
                        else
                        {
                            listener.Flush();
                        }
                    }
                }
            }
        }
 
        public static void Close()
        {
            if (s_listeners != null)
            {
                // Use global lock
                lock (critSec)
                {
                    foreach (TraceListener listener in Listeners)
                    {
                        listener.Close();
                    }
                }
            }
        }
 
        public static void Assert(bool condition)
        {
            if (condition) return;
            Fail(string.Empty);
        }
 
        public static void Assert(bool condition, string? message)
        {
            if (condition) return;
            Fail(message);
        }
 
        public static void Assert(bool condition, string? message, string? detailMessage)
        {
            if (condition) return;
            Fail(message, detailMessage);
        }
 
        [DoesNotReturn]
        public static void Fail(string? message)
        {
            if (UseGlobalLock)
            {
                lock (critSec)
                {
                    foreach (TraceListener listener in Listeners)
                    {
                        listener.Fail(message);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
            else
            {
                foreach (TraceListener listener in Listeners)
                {
                    if (!listener.IsThreadSafe)
                    {
                        lock (listener)
                        {
                            listener.Fail(message);
                            if (AutoFlush) listener.Flush();
                        }
                    }
                    else
                    {
                        listener.Fail(message);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
#pragma warning disable 8763 // "A method marked [DoesNotReturn] should not return."
        }
#pragma warning restore 8763
 
        [DoesNotReturn]
        public static void Fail(string? message, string? detailMessage)
        {
            if (UseGlobalLock)
            {
                lock (critSec)
                {
                    foreach (TraceListener listener in Listeners)
                    {
                        listener.Fail(message, detailMessage);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
            else
            {
                foreach (TraceListener listener in Listeners)
                {
                    if (!listener.IsThreadSafe)
                    {
                        lock (listener)
                        {
                            listener.Fail(message, detailMessage);
                            if (AutoFlush) listener.Flush();
                        }
                    }
                    else
                    {
                        listener.Fail(message, detailMessage);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
#pragma warning disable 8763 // "A method marked [DoesNotReturn] should not return."
        }
#pragma warning restore 8763
 
        private static void InitializeSettings()
        {
            if (!s_settingsInitialized)
            {
                // We should avoid 2 threads altering the state concurrently for predictable behavior
                // though it may not be strictly necessary at present.
                lock (critSec)
                {
                    // Prevent re-entrance.
                    if (s_settingsInitializing)
                    {
                        return;
                    }
 
                    s_settingsInitializing = true;
 
                    if (!s_settingsInitialized)
                    {
                        s_autoFlush = DiagnosticsConfiguration.AutoFlush;
                        s_useGlobalLock = DiagnosticsConfiguration.UseGlobalLock;
                        s_settingsInitialized = true;
                        s_settingsInitializing = false;
                    }
                }
            }
        }
 
        // This method refreshes all the data from the configuration file, so that updated to the configuration file are mirrored
        // in the System.Diagnostics.Trace class
        internal static void Refresh()
        {
            lock (critSec)
            {
                s_settingsInitialized = false;
                s_listeners = null;
                Debug.IndentSize = DiagnosticsConfiguration.IndentSize;
            }
            InitializeSettings();
        }
 
        public static void TraceEvent(TraceEventType eventType, int id, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string? format, params object?[]? args)
        {
            TraceEventCache EventCache = new TraceEventCache();
 
            if (UseGlobalLock)
            {
                lock (critSec)
                {
                    if (args == null)
                    {
                        foreach (TraceListener listener in Listeners)
                        {
                            listener.TraceEvent(EventCache, AppName, eventType, id, format);
                            if (AutoFlush) listener.Flush();
                        }
                    }
                    else
                    {
                        foreach (TraceListener listener in Listeners)
                        {
                            listener.TraceEvent(EventCache, AppName, eventType, id, format!, args);
                            if (AutoFlush) listener.Flush();
                        }
                    }
                }
            }
            else
            {
                if (args == null)
                {
                    foreach (TraceListener listener in Listeners)
                    {
                        if (!listener.IsThreadSafe)
                        {
                            lock (listener)
                            {
                                listener.TraceEvent(EventCache, AppName, eventType, id, format);
                                if (AutoFlush) listener.Flush();
                            }
                        }
                        else
                        {
                            listener.TraceEvent(EventCache, AppName, eventType, id, format);
                            if (AutoFlush) listener.Flush();
                        }
                    }
                }
                else
                {
                    foreach (TraceListener listener in Listeners)
                    {
                        if (!listener.IsThreadSafe)
                        {
                            lock (listener)
                            {
                                listener.TraceEvent(EventCache, AppName, eventType, id, format!, args);
                                if (AutoFlush) listener.Flush();
                            }
                        }
                        else
                        {
                            listener.TraceEvent(EventCache, AppName, eventType, id, format!, args);
                            if (AutoFlush) listener.Flush();
                        }
                    }
                }
            }
        }
 
 
        public static void Write(string? message)
        {
            if (UseGlobalLock)
            {
                lock (critSec)
                {
                    foreach (TraceListener listener in Listeners)
                    {
                        listener.Write(message);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
            else
            {
                foreach (TraceListener listener in Listeners)
                {
                    if (!listener.IsThreadSafe)
                    {
                        lock (listener)
                        {
                            listener.Write(message);
                            if (AutoFlush) listener.Flush();
                        }
                    }
                    else
                    {
                        listener.Write(message);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
        }
 
        public static void Write(object? value)
        {
            if (UseGlobalLock)
            {
                lock (critSec)
                {
                    foreach (TraceListener listener in Listeners)
                    {
                        listener.Write(value);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
            else
            {
                foreach (TraceListener listener in Listeners)
                {
                    if (!listener.IsThreadSafe)
                    {
                        lock (listener)
                        {
                            listener.Write(value);
                            if (AutoFlush) listener.Flush();
                        }
                    }
                    else
                    {
                        listener.Write(value);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
        }
 
        public static void Write(string? message, string? category)
        {
            if (UseGlobalLock)
            {
                lock (critSec)
                {
                    foreach (TraceListener listener in Listeners)
                    {
                        listener.Write(message, category);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
            else
            {
                foreach (TraceListener listener in Listeners)
                {
                    if (!listener.IsThreadSafe)
                    {
                        lock (listener)
                        {
                            listener.Write(message, category);
                            if (AutoFlush) listener.Flush();
                        }
                    }
                    else
                    {
                        listener.Write(message, category);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
        }
 
        public static void Write(object? value, string? category)
        {
            if (UseGlobalLock)
            {
                lock (critSec)
                {
                    foreach (TraceListener listener in Listeners)
                    {
                        listener.Write(value, category);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
            else
            {
                foreach (TraceListener listener in Listeners)
                {
                    if (!listener.IsThreadSafe)
                    {
                        lock (listener)
                        {
                            listener.Write(value, category);
                            if (AutoFlush) listener.Flush();
                        }
                    }
                    else
                    {
                        listener.Write(value, category);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
        }
 
        public static void WriteLine(string? message)
        {
            if (UseGlobalLock)
            {
                lock (critSec)
                {
                    foreach (TraceListener listener in Listeners)
                    {
                        listener.WriteLine(message);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
            else
            {
                foreach (TraceListener listener in Listeners)
                {
                    if (!listener.IsThreadSafe)
                    {
                        lock (listener)
                        {
                            listener.WriteLine(message);
                            if (AutoFlush) listener.Flush();
                        }
                    }
                    else
                    {
                        listener.WriteLine(message);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
        }
 
        public static void WriteLine(object? value)
        {
            if (UseGlobalLock)
            {
                lock (critSec)
                {
                    foreach (TraceListener listener in Listeners)
                    {
                        listener.WriteLine(value);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
            else
            {
                foreach (TraceListener listener in Listeners)
                {
                    if (!listener.IsThreadSafe)
                    {
                        lock (listener)
                        {
                            listener.WriteLine(value);
                            if (AutoFlush) listener.Flush();
                        }
                    }
                    else
                    {
                        listener.WriteLine(value);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
        }
 
        public static void WriteLine(string? message, string? category)
        {
            if (UseGlobalLock)
            {
                lock (critSec)
                {
                    foreach (TraceListener listener in Listeners)
                    {
                        listener.WriteLine(message, category);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
            else
            {
                foreach (TraceListener listener in Listeners)
                {
                    if (!listener.IsThreadSafe)
                    {
                        lock (listener)
                        {
                            listener.WriteLine(message, category);
                            if (AutoFlush) listener.Flush();
                        }
                    }
                    else
                    {
                        listener.WriteLine(message, category);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
        }
 
        public static void WriteLine(object? value, string? category)
        {
            if (UseGlobalLock)
            {
                lock (critSec)
                {
                    foreach (TraceListener listener in Listeners)
                    {
                        listener.WriteLine(value, category);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
            else
            {
                foreach (TraceListener listener in Listeners)
                {
                    if (!listener.IsThreadSafe)
                    {
                        lock (listener)
                        {
                            listener.WriteLine(value, category);
                            if (AutoFlush) listener.Flush();
                        }
                    }
                    else
                    {
                        listener.WriteLine(value, category);
                        if (AutoFlush) listener.Flush();
                    }
                }
            }
        }
 
        public static void WriteIf(bool condition, string? message)
        {
            if (condition)
                Write(message);
        }
 
        public static void WriteIf(bool condition, object? value)
        {
            if (condition)
                Write(value);
        }
 
        public static void WriteIf(bool condition, string? message, string? category)
        {
            if (condition)
                Write(message, category);
        }
 
        public static void WriteIf(bool condition, object? value, string? category)
        {
            if (condition)
                Write(value, category);
        }
 
        public static void WriteLineIf(bool condition, string? message)
        {
            if (condition)
                WriteLine(message);
        }
 
        public static void WriteLineIf(bool condition, object? value)
        {
            if (condition)
                WriteLine(value);
        }
 
        public static void WriteLineIf(bool condition, string? message, string? category)
        {
            if (condition)
                WriteLine(message, category);
        }
 
        public static void WriteLineIf(bool condition, object? value, string? category)
        {
            if (condition)
                WriteLine(value, category);
        }
    }
}