调试日志弄乱了我的代码
本文关键字:代码 我的 乱了 日志弄 调试 | 更新日期: 2023-09-27 17:50:08
我正在尝试添加调试日志到我的c# .net代码。但它打乱了我的代码,看起来像个地狱。是否有任何东西可以自动记录每个代码及其值?现在看起来像这样
#if DEBUG
debuglogger("The functionstarted");
#endif
someCode1();
#if DEBUG
debuglogger("SomeCode1 Finished");
#endif
someCode2();
#if DEBUG
debuglogger("SomeCode2 Finished");
#endif
someCode3();
#if DEBUG
debuglogger("SomeCode3 Finished");
#endif
#if DEBUG
debuglogger("Function End");
#endif
您应该使用ConditionalAttribute
。它允许您定义条件方法,如果条件不匹配,这些方法将在构建时被删除:
[Conditional("DEBUG")]
void DebugLog(string message)
{
// Do some logging...
}
在没有定义DEBUG
的构建中,对该方法的调用将被剥离。就像
#if DEBUG
DebugLog("Testing...");
#endif
与将#if
移动到方法内部相比,这有一个额外的好处,即不会使堆栈跟踪变得混乱。
回答得太晚了,但我留作将来参考。在我看来,对于这样的任务,您应该考虑面向方面编程。也就是说,如果您不需要为一个小任务增加这样的复杂性,您可以将预处理器条件移到您的log方法中:
public static void Log(string message)
{
#if DEBUG
// Do logging
#endif
}
不要担心留下一个空方法,JIT会优化它,它甚至不会被调用。几乎等同于:
[Condition("DEBUG")]
public static void Log(string message)
警告:我说几乎,因为使用[Condition]
属性的方法参数甚至不会被评估,然后在发布版中给出此代码:
Log(String.Format("Index: {0}", index++));
index
变量永远不会递增,因为JIT编译器不会发出对Log
的调用,甚至它的参数也不会被求值。这是不正确的,如果你保持你的方法体空#if
指令里面。调用不会被触发(因为函数体是空的),但是它的参数会被计算。
这个解决方案的问题是它会使正常的程序流程变得混乱。有日志调用,参数转储之类的。你能做什么?
重构日志
如果您多次调用someecode1()方法,您不应该在每个调用站点进行日志记录,最好是将日志记录移到被调用的方法内部。只在每个函数的开始和结束记录日志,日志仍然会在你的代码中,但它会跨越多个函数。
void SomeCode1() {
Log("Starting SomeCode1");
// Do something
Log("SomeCode1 completed");
}
你的呼叫站点将是干净的:
SomeCode1();
SomeCode2();
SomeCode3();
表达式如果性能不是问题(度量,而不是猜测),您可以使用表达式为您完成任务。您也可以记录参数(或字段、对象状态、诊断信息、不变量和任何您可能需要的东西),所有由诊断开关控制的内容(仅在需要时启用它们)。在LOB类中没有日志代码,但代价是执行速度(和LoggedOperation
函数的复杂性)。
这段代码(礼貌地对自己说)非常幼稚,一个体面的实现会复杂得多,所以把它看作一个想法而不是一个实现。
static void LoggedOperation(Expression<Action> expression)
{
MethodCallExpression methodCall = expression.Body as MethodCallExpression;
if (methodCall != null)
Log("Calling {0}", methodCall.Method.Name);
expression.Compile()();
if (methodCall != null)
Log("{0} completed", methodCall.Method.Name);
}
然后像这样使用:
LoggedOperation(() => SomeCode1());
LoggedOperation(() => SomeCode2());
LoggedOperation(() => SomeCode3());
你会得到:
<>之前调用SomeCode1SomeCode1完成调用SomeCode2SomeCode2完成调用SomeCode3SomeCode3完成之前AOP会给你一个更清晰的代码,但这在很多情况下可能已经足够了。
您可以将预处理器指令移动到debuglogger
函数中,或者使用一个可配置的日志框架,该框架允许您配置何时进行日志记录,而不是在构建时依赖于预处理器语句。这样,您就可以"打开"日志记录,而不必重新构建应用程序。
您可以使用AOP,尽管我自己还没有尝试过。
或者,您可以做一些事情来帮助提高代码的可读性,例如使用Conditional
属性
用Conditional
属性标记debuglogger
方法消除了对#if DEBUG
代码的需要
[Conditional("DEBUG")]
public void debuglogger(string message)
{
// Logging code goes here
}
debuglogger("The functionstarted");
someCode1();
debuglogger("SomeCode1 Finished");
someCode2();
debuglogger("SomeCode2 Finished");
someCode3();
debuglogger("SomeCode3 Finished");
debuglogger("Function End");
我个人也会有someCodeN
方法记录"SomeCodeN Finished"消息,这进一步简化了你的代码
debuglogger("The functionstarted");
someCode1();
someCode2();
someCode3();
debuglogger("Function End");
public void someCode1()
{
// Do something
debuglogger("someCode1 Finished");
}
Mark Gravell最近发表了一个利用分部类的有趣想法。链接到他的博客
项目结构:
-Foo.cs
-Foo.debug.cs
这里是类:
// Foo.debug.cs
#if DEBUG
partial class Foo
{
partial void Trace(string value)
{
Console.WriteLine("The value is: {0}", value);
}
}
#endif
// Foo.cs
partial class Foo
{
partial void Trace(string value);
public void MethodWithTracing()
{
Trace("This is traced");
}
}
调试/跟踪逻辑与正常代码分离,当更改构建选项时,它将不会被编译。