.NET 애플리케이션의 로깅 및 추적

완료됨

애플리케이션 개발을 지속하면서 더 복잡해질 경우 애플리케이션에 추가 디버그 진단을 적용하는 것이 좋습니다.

추적은 애플리케이션이 실행되는 동안 애플리케이션의 실행을 모니터링하는 방법입니다. .NET 애플리케이션을 개발할 때 추적 및 디버깅 계측을 추가할 수 있습니다. 애플리케이션을 개발하는 도중 그리고 개발한 이후 계측을 사용할 수 있습니다.

이 간단한 기술은 놀라울 정도로 강력합니다. 디버거보다 더 많은 것이 필요한 상황에서 사용할 수 있습니다.

  • 오랜 시간 동안 발생하는 문제는 기존 디버거로 디버그하기 어려울 수 있습니다. 로그를 사용하면 오랜 기간 동안 자세한 사후 검토를 할 수 있습니다. 이와 대조적으로 디버거는 실시간 분석으로 제한됩니다.
  • 다중 스레드 애플리케이션과 분산 애플리케이션은 디버그하기 어려운 경우가 많습니다. 디버거를 연결하면 동작이 수정되는 경향이 있습니다. 필요에 따라 자세한 로그를 분석하여 복잡한 시스템을 이해할 수 있습니다.
  • 분산 애플리케이션의 문제는 여러 구성 요소 간 복잡한 상호 작용에서 발생할 수 있습니다. 시스템의 모든 부분에 디버거를 연결하는 것은 적절하지 않을 수 있습니다.
  • 많은 서비스가 중단되지 않아야 합니다. 디버거를 연결하면 시간 제한 오류가 발생하는 경우가 많습니다.
  • 문제는 항상 예측되는 것이 아닙니다. 로깅 및 추적은 문제가 발생하는 경우 프로그램에서 항상 기록할 수 있도록 낮은 오버헤드로 설계되었습니다.

출력 창에 정보 쓰기

이 지점까지 애플리케이션 사용자에게 정보를 표시하기 위해 콘솔을 사용했습니다. 모바일, 웹 및 데스크톱 앱과 같은 사용자 인터페이스가 있는 .NET으로 빌드된 다른 유형의 애플리케이션이 있으며 표시되는 콘솔이 없습니다. 이러한 애플리케이션 System.Console 에서는 "백그라운드에서" 메시지를 기록합니다. 이러한 메시지는 Visual Studio 또는 Visual Studio Code의 출력 창에 표시될 수 있습니다. Android의 logcat과 같이 시스템 로그에 대한 출력일 수도 있습니다. 따라서 콘솔이 아닌 애플리케이션에서 System.Console.WriteLine을 사용하는 경우에는 신중하게 고려해야 합니다.

이때가 System.Diagnostics.Debug 외에 System.Diagnostics.TraceSystem.Console를 사용할 수 있는 경우입니다. DebugTrace는 모두 System.Diagnostics에 속하며 적절한 수신기에 연결된 경우에만 로그를 작성합니다.

사용할 인쇄 스타일 API를 선택하는 것은 사용자에게 달려 있습니다. 주요 차이점은 다음과 같습니다.

  • System.Console
    • 항상 사용하도록 설정되어 있으며 항상 콘솔에 기록됩니다.
    • 고객이 릴리스에서 확인해야 할 수 있는 정보에 유용합니다.
    • 가장 간단한 방법이므로 임시 디버깅에 자주 사용됩니다. 이 디버그 코드는 소스 제어에 체크인되지 않는 경우가 많습니다.
  • System.Diagnostics.Trace
    • TRACE가 정의된 경우에만 사용하도록 설정됩니다.
    • 기본적으로 연결된 수신기인 DefaultTraceListener에 작성합니다.
    • 대부분의 빌드에서 사용되는 로그를 만들 때 이 API를 사용합니다.
  • System.Diagnostics.Debug
    • DEBUG가 정의된 경우(디버그 모드인 경우)에만 활성화됩니다.
    • 연결된 디버거에 씁니다.
    • 디버그 빌드에서만 사용되는 로그를 만들 때 이 API를 사용합니다.
Console.WriteLine("This message is readable by the end user.");
Trace.WriteLine("This is a trace message when tracing the app.");
Debug.WriteLine("This is a debug message just for developers.");

추적 및 디버깅 전략을 설계하는 경우 출력을 어떻게 표시할지 생각해야 합니다. 관련 없는 정보로 채워진 여러 Write 문은 읽기 어려운 로그를 만듭니다. 다른 한편으로는 WriteLine을 사용하여 별도의 줄에 관련된 문을 넣으면 함께 속한 정보를 구분하기 어렵게 될 수도 있습니다. 일반적으로 여러 소스의 정보를 결합하여 단일 정보 메시지를 생성하려는 경우 여러 Write 문을 사용합니다. 완전한 단일 메시지를 생성하려는 경우 WriteLine 문을 사용합니다.

Debug.Write("Debug - ");
Debug.WriteLine("This is a full line.");
Debug.WriteLine("This is another full line.");

다음 출력은 Debug를 사용한 앞선 로깅에서 가져온 것입니다.

Debug - This is a full line.
This is another full line.

TRACE 및 DEBUG 상수 정의

기본적으로 디버그에 따라 애플리케이션을 실행하면 DEBUG 상수가 정의됩니다. 이는 속성 그룹의 프로젝트 파일에 DefineConstants 항목을 추가하면 제어할 수 있습니다. 다음은 TRACE 구성에 대한 Debug 외에도 ReleaseDEBUG 구성 모두에 대해 Debug를 활성화하는 예입니다.

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
    <DefineConstants>TRACE</DefineConstants>
</PropertyGroup>

디버거에 연결되지 않은 경우 Trace를 사용할 때 dotnet-trace와 같은 추적 수신기를 구성해야 합니다.

조건부 추적

간단한 WriteWriteLine 메서드 외에 WriteIfWriteLineIf로 조건을 추가하는 기능도 있습니다. 예를 들어 다음 논리는 개수가 0인지 확인한 다음 디버그 메시지를 씁니다.

if(count == 0)
{
    Debug.WriteLine("The count is 0 and this may cause an exception.");
}

이를 한 줄의 코드로 다시 작성할 수 있습니다.

Debug.WriteLineIf(count == 0, "The count is 0 and this may cause an exception.");

Trace와 애플리케이션에서 정의하는 플래그로도 이러한 조건을 사용할 수 있습니다.

bool errorFlag = false;  
System.Diagnostics.Trace.WriteIf(errorFlag, "Error in AppendData procedure.");  
System.Diagnostics.Debug.WriteIf(errorFlag, "Transaction abandoned.");  
System.Diagnostics.Trace.Write("Invalid value for data request");

특정 조건이 존재하는지 확인

어설션 또는 Assert 구문은 Assert 구문에 대한 인수로 지정한 조건을 테스트합니다. 조건이 평가 true되면 아무 작업도 수행되지 않습니다. 조건이 false로 평가되면 어설션이 실패합니다. 디버그 빌드를 사용하여 이를 실행하는 경우 프로그램은 중단 모드로 전환됩니다.

Assert 또는 DebugTrace 메서드를 사용할 수 있으며 둘 다 System.Diagnostics 네임스페이스에 있습니다. Debug 클래스 메서드는 프로그램의 릴리스 버전에 포함되지 않으므로 릴리스 코드의 크기를 증가시키거나 속도를 떨어뜨리지 않습니다.

코드가 정확한 경우 true로 유지되어야 하는 조건을 테스트하려면 System.Diagnostics.Debug.Assert 메서드를 사용합니다. 예를 들어 정수 나누기 함수를 작성한다고 가정합니다. 수학의 규칙에 따라 0으로는 나눌 수 없습니다. 어설션을 사용하여 이 조건을 테스트할 수 있습니다.

int IntegerDivide(int dividend, int divisor)
{
    Debug.Assert(divisor != 0, $"{nameof(divisor)} is 0 and will cause an exception.");

    return dividend / divisor;
}

디버거에서 이 코드를 실행하면 어설션 문이 평가됩니다. 그러나 릴리스 버전에서는 비교되지 않으므로 추가 오버헤드가 없습니다.

비고

System.Diagnostics.Debug.Assert를 사용할 때 어설션이 삭제될 경우 Assert 내의 모든 코드로 인해 프로그램의 결과가 변경되지 않도록 합니다. 그러지 않으면 프로그램의 릴리스 버전에만 표시되는 버그를 실수로 도입할 수 있습니다. 함수 또는 프로시저 호출이 포함된 어설션에 특히 주의해야 합니다.

네임스페이스 Debug 사용 TraceSystem.Diagnostics 사용은 애플리케이션을 실행하고 디버그할 때 추가 컨텍스트를 제공하는 좋은 방법입니다.