调试日志弄乱了我的代码

本文关键字:代码 我的 乱了 日志弄 调试 | 更新日期: 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");
    }
}

调试/跟踪逻辑与正常代码分离,当更改构建选项时,它将不会被编译。