C#条件记录/跟踪
我想将日志记录或跟踪添加到我的C#应用程序中,但如果日志详细程度设置得太低以至于消息不会被记录,我不想记录格式化字符串或计算值的开销 在C++中,可以使用预处理器定义宏,这样就可以防止代码被这样执行:C#条件记录/跟踪,c#,logging,trace,C#,Logging,Trace,我想将日志记录或跟踪添加到我的C#应用程序中,但如果日志详细程度设置得太低以至于消息不会被记录,我不想记录格式化字符串或计算值的开销 在C++中,可以使用预处理器定义宏,这样就可以防止代码被这样执行: #define VLOG(level,expr) if (level >= g_log.verbosity) { g_log.output << expr; } VLOG(5,"Expensive function call returns " << Expensi
#define VLOG(level,expr) if (level >= g_log.verbosity) { g_log.output << expr; }
VLOG(5,"Expensive function call returns " << ExpensiveFunctionCall());
if (Component1TraceSource.ShouldTrace(TraceEventType.Verbose))
OutputExpensiveTraceInformation()
如果我取消定义跟踪,它不会调用昂贵的函数?还是打电话,然后决定它不会追踪任何东西
无论如何,即使它确实删除了它,也不如C++宏,因为我不能让那个丑陋的调用看起来像C++中的简单VLoW()调用,并且仍然避免评估参数,我能吗?我也不能通过定义运行时的冗长来避免开销,比如我可以在C++中,对?< /p> 你的评论
“因为我不能让那个大丑陋的调用看起来像我的C++中简单的VLoW()调用——你可以在下面添加一个使用语句。
using System.Diagnostics;
....
Trace.WriteLineIf(.....)
使用系统诊断;
....
Trace.WriteLineIf(…)
据我所知,如果您取消定义跟踪符号,它将删除包含跟踪的行。我不确定,但您可以自己找到答案 让它成为一个非常昂贵的函数(比如
Thread.Sleep(10000)
),并为调用计时。如果它需要很长时间,那么它无论如何都会调用您的函数
(您可以使用
#if Trace
和#endif
包装Trace.WriteLineIf()
调用,并再次测试它以进行基本比较。)要回答您的一个问题,所有必须求值才能调用Trace.WriteLine的方法调用(或其同级/同级)在编译Trace.WriteLine时不会被调用。因此,请继续,直接将昂贵的方法调用作为跟踪调用的参数,如果不定义跟踪符号,它将在编译时被删除
现在是关于在运行时更改详细信息的另一个问题。这里的技巧是Trace.WriteLine和类似的方法将“params object[]args”作为字符串格式参数。只有当字符串实际发出时(当详细度设置得足够高时),该方法才会调用这些对象上的ToString以从中获取字符串。所以我经常玩的一个技巧是将对象而不是完全组装的字符串传递给这些方法,并将字符串创建留在我传递的对象的ToString中。这样一来,运行时性能税只在实际发生日志记录时支付,它让您可以自由更改详细信息,而无需重新编译应用程序。是您最好的朋友。当未设置#define时,调用将被完全删除(就像调用站点被#if'd)
编辑:有人在评论中写了这句话(谢谢!),但在主要答案正文中值得注意:
跟踪类的所有方法都用条件(“跟踪”)修饰。刚才用反光镜看到的
这意味着如果未定义跟踪,Trace.Blah(…昂贵…)将完全消失。它将调用昂贵的调用,因为它可能会产生预期的副作用
您可以做的是使用[Conditional(“TRACE”)或[Conditional(“DEBUG”)属性来修饰昂贵的方法。如果未定义调试或跟踪常量,则该方法将不会编译到最终可执行文件中,执行昂贵方法的任何调用也不会编译到最终可执行文件中。对我有效的解决方案是使用类。它可以公开您的日志功能,您可以有效地控制其行为。让我们称这个类为“apploger”。她就是一个例子
public class AppLogger
{
public void WriteLine(String format, params object[] args)
{
if ( LoggingEnabled )
{
Console.WriteLine( format, args );
}
}
}
注意,上面的例子中没有提到单例。有很多很好的例子。现在有趣的是如何支持多线程。我是这样做的:(简称为简洁,哈哈哈)
通过这种方式,任何线程都可以进行日志记录,并在原始创建线程上处理消息。或者,您可以创建一个专门的线程来处理日志输出 您是否尝试过类似log4net()的复杂日志api?其中两个答案(Andrew Arnott和Brian的)确实回答了我的部分问题。如果跟踪或调试未定义,则应用于跟踪和调试类方法的ConditionalAttribute会导致删除对方法的所有调用,包括昂贵的参数计算。谢谢
对于第二部分,是否可以在运行时而不是编译时完全删除所有调用,我在log4net中找到了答案。根据他们的说法,如果在启动时设置只读属性,运行时将编译掉所有未通过测试的调用!这不允许您在启动后对其进行更改,但这很好,比在编译时删除它们要好。所有关于条件(跟踪)的信息都很好-但我假设您真正的问题是希望在生产代码中保留跟踪调用,但(通常)在运行时禁用它们,除非遇到问题 如果您正在使用TraceSource(我认为您应该这样做,而不是直接调用Trace,因为它在运行时为您提供了对组件级别跟踪的更细粒度控制),您可以执行以下操作:
#define VLOG(level,expr) if (level >= g_log.verbosity) { g_log.output << expr; }
VLOG(5,"Expensive function call returns " << ExpensiveFunctionCall());
if (Component1TraceSource.ShouldTrace(TraceEventType.Verbose))
OutputExpensiveTraceInformation()
这假设您能够在另一个函数中隔离跟踪参数(即,它们主要依赖于当前类的成员,而不是对该代码所在函数的参数执行昂贵的操作)
这种方法的优点是,JITer根据需要逐个函数进行编译,如果“if”的计算结果为false,那么函数不仅不会被调用,甚至不会被JIT。缺点是(a)您已经将跟踪级别的知识在这个调用和函数OutputExpensiveTraceInformation之间分离了(例如,如果您将TraceEventType更改为TraceEventType.Information,它将不起作用,因为除非启用TraceSource,否则您甚至不会调用它