17

我正在寻找一种为所有现有 TraceSources 添加和删除 TraceListener 的方法。

(我不确定我的方法在这里是否正确,我还可以使用哪些其他方法?基本上我想将所有跟踪输出记录到使用当前项目名称作为文件名的文件中。每当用户创建或重新打开项目时,我想将日志附加到正确的文件。一次只能打开一个项目。)

代码示例:

我在我的应用程序中创建了几个 TraceSource,每个类一个

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");
}

我现在想在运行时向我的所有 traceSources 添加或删除一个 traceListener,如下所示:

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
}

到目前为止,如果不公开我所有的 traceSources(似乎是个坏主意),然后像这样列出我的所有类,我发现没有办法做到这一点:

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

这在几个层面上似乎是一个糟糕的设计选择。

或者

将我所有的 TraceSources 添加到每个类的构造函数中的自定义全局集合中(容易忘记/搞砸;全局变量不好)

有没有更好的办法?基本上我正在寻找一种方法来设置另一个默认监听器

4

4 回答 4

4

基于这个 StackOverflow 答案这个答案

这是一种方法:

    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);
                }
            }
        }
    }
于 2015-07-16T19:21:23.300 回答
1

在 .NET 4 及更高版本中,您可以在配置侦听器后使用Lazy<>延迟加载 TraceSource。请参阅以下工作示例程序:

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();
        }
    }
}
于 2013-03-22T17:41:22.463 回答
1

好的,我参加这个聚会的时间太晚了——很难相信七年后,有人还在为获取系统中的所有跟踪源而苦苦挣扎(并且在任何地方都找不到好的答案)。

这是查找所有跟踪源的反射方法:

    /// <summary>
    /// Get all trace sources instantiated by the current process.
    /// </summary>
    /// <remarks>
    /// This is  reaching into a part of the .Net code that Microsoft haven't given public access to, that's not part of their API.
    /// Found from inspection of the .Net (4.8) reference source that the TraceSource class holds a static cache of all trace sources, and the names of the corresponding members.
    /// </remarks>
    /// <returns>List of all current trace sources</returns>
    public static List<TraceSource> GetAll()
    {
        var result = new List<TraceSource>(); 
        var privateStaticMethods = typeof(TraceSource).GetMethods(BindingFlags.Static | BindingFlags.NonPublic);
        var pruneMethod = privateStaticMethods.FirstOrDefault(m => m.Name == "_pruneCachedTraceSources");
        var privateStaticFields = typeof(TraceSource).GetFields(BindingFlags.Static | BindingFlags.NonPublic);
        var tracesourcesField = privateStaticFields.FirstOrDefault(f => f.Name == "tracesources");
        if (tracesourcesField != null)
        {
            var tracesourceValue = tracesourcesField.GetValue(null);
            var tracesources = tracesourceValue as List<WeakReference>;
            if (tracesources != null)
            {
                lock (tracesources)
                {
                    if (pruneMethod != null)
                    {
                        pruneMethod.Invoke(null, new object[] { });
                    }
                    for (int i = 0; i < tracesources.Count; i++)
                    {
                        var target = tracesources[i].Target;
                        TraceSource tracesource = target as TraceSource;
                        if (tracesource != null)
                        {
                            result.Add(tracesource);
                        }
                    }
                }
            }
        }
        return result;
    }

目前使用 .Net Framework 4.8,它可以工作;我没有用 5 尝试过。也许值得注意的是,如果您管理自己的所有跟踪源列表,那么您将加倍。

使用它来获取所有跟踪源。也许过滤你想要的那些。然后将您的跟踪侦听器添加到所有这些侦听器中。

于 2021-05-29T05:39:20.190 回答
0

我自己也遇到过这个问题。就我而言,我还在每个类中创建了跟踪源。app.config 中的侦听器可以毫无问题地添加到所有源中,但是我需要在运行时添加一个特定的侦听器。这当然只适用于添加监听器的跟踪源。我看到了两个选项 - 创建单个跟踪源并将其传递给应用程序的其余部分(糟糕),或者在其他所有引用的帮助程序类中拥有一个静态跟踪源。所以我有:

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

LogHelper 类还设置了侦听器和在运行时配置的各种其他过滤器。到目前为止,这对我来说效果很好。如果由于某种原因某个特定类需要不同的行为,您仍然可以在该类中创建跟踪源。

于 2014-09-03T01:46:01.257 回答