Добавить/удалить TraceListener во все TraceSources

Я ищу способ добавления и удаления TraceListener для всех существующих TraceSources.

(Я не уверен, что мой подход здесь правильный, какие другие способы я могу использовать? В основном я хочу записать весь вывод трассировки в файл, который использует текущее имя проекта как имя файла. Всякий раз, когда пользователь создает или возобновляет проект, я хочу добавить журналы в правильный файл. Одновременно может быть открыт только один проект.)

Пример кода:

Я создаю несколько TraceSources в моем приложении, по одному для каждого класса

public class Class1
{
    private static readonly System.Diagnostics.TraceSource trace = 
            new System.Diagnostics.TraceSource("Class1");
}

public class Class2
{
    private static readonly System.Diagnostics.TraceSource trace = 
            new System.Diagnostics.TraceSource("Class2");
}

Теперь я хочу добавить или удалить traceListener во все мои трассировки в Runtime, например:

private System.Diagnostics.TextWriterTraceListener myListener;

private onProjectOpen()
{
    // user created a new project or opened an existing one
    myListener = new System.Diagnostics.TextWriterTraceListener("log-"+projectname+".log");
    ALL_TRACESOURCES.Add ( myListener) ; // <-- how to do this?
}

private onProjectClose()
{
    // user closed a project
    ALL_TRACESOURCES.Remove( myListener) ; // <-- how to do this?
    myListener.Flush();
    myListener.Close();
    myListener.Dispose(); // <-- not sure if all this is neccessary
}

До сих пор я не нашел способа сделать это, не открыв все мои трассировочные источники (кажется плохая идея), а затем перечисляю все мои классы следующим образом:

Class1.Trace.Add( myListener );
Class2.Trace.Add( myListener );
...

который кажется плохим выбором дизайна на нескольких уровнях.

или

Добавьте все мои TraceSources в пользовательскую глобальную коллекцию в конструкторе каждого класса (легко забыть/испортить, а глобальные переменные - плохие)

Есть ли лучший способ? В принципе, я ищу способ установить другой приемник по умолчанию

Ответы

Ответ 1

Основываясь на fooobar.com/questions/5850/... и этот ответ

Вот один из способов сделать это:

    private static void AttachToAllTraceSources(TraceListener yourListener)
    {
        TraceSource ts = new TraceSource("foo");
        List<WeakReference> list = (List<WeakReference>)GetInstanceField(typeof(TraceSource), ts, "tracesources");
        foreach(var weakReference in list)
        {
            if(weakReference.IsAlive)
            {
                TraceSource source = (weakReference.Target as TraceSource);
                if(source != null && source.Name != "foo")
                {
                    source.Listeners.Add(yourListener);
                }
            }
        }
    }

Ответ 2

В .NET 4 и выше вы можете использовать Lazy < > для ленивой загрузки ваших TraceSources после настройки слушателей. См. Следующую рабочую примерную программу:

public static class TraceSources
{
    public static TraceSource Create(string sourceName)
    {
        var source = new TraceSource(sourceName);
        source.Listeners.AddRange(Trace.Listeners);
        source.Switch.Level = SourceLevels.All;
        return source;
    }
}

public class Class1
{
    private static readonly Lazy<TraceSource> trace = new 
            Lazy<TraceSource>(() => TraceSources.Create("Class1"));

    public void DoSomething()
    {
        trace.Value.TraceEvent(TraceEventType.Information, 1, "Class1 speaking up");
    }
}

public class Class2
{
    private static readonly Lazy<TraceSource> trace = new
            Lazy<TraceSource>(() => TraceSources.Create("Class2"));

    public void DoSomethingElse()
    {
        trace.Value.TraceEvent(TraceEventType.Information, 2, "Class2 speaking out");
    }
}

public class Program
{
    static void Main(string[] args)
    {
        try
        {
            var listener = new TextWriterTraceListener(@"C:\trace.txt");
            Trace.Listeners.Add(listener);

            var classOne = new Class1();
            var classTwo = new Class2();
            classOne.DoSomething();
            classTwo.DoSomethingElse();
        }
        finally
        {
            Trace.Close();
        }
    }
}

Ответ 3

Я сам столкнулся с этой проблемой. В моем случае у меня также есть источники трассировки, созданные в каждом классе. Слушатели, которые находятся в app.config, не добавляются ко всем источникам без проблем, но есть некоторый слушатель, который мне нужно добавить во время выполнения. Это, конечно, работает только с источником трассировки, к которому добавляется слушатель. Я вижу два варианта: создайте один источник трассировки и передайте его всем остальным приложениям (yuck) или используйте один статический источник трассировки в вспомогательном классе, который ссылается на все остальные. Поэтому у меня есть:

public class LogHelper { /// /// The trace source logger for the application. /// public static readonly TraceSource Logger = new TraceSource("FruityBlergs", SourceLevels.All); }

Класс LogHelper также устанавливает слушателя и различные другие фильтры, которые настраиваются во время выполнения. До сих пор это работало хорошо. Если по какой-то причине конкретный класс нуждается в другом поведении, вы все равно можете создать источник трассировки в этом классе.