로깅 코드를 비즈니스 로직 외부에서 완전히 유지할 수 있습니까?


12

AOP의 도움으로 비즈니스 로직에서 로깅 코드를 제거 할 수 있습니다. 그러나 나는 간단한 것들 (즉, 로깅 방법 입력 / 종료 및 매개 변수 값)을 기록하는 데만 사용할 수 있다고 생각합니다.

그러나 비즈니스 로직에 무언가를 기록해야하는 경우 어떻게해야합니까? 예 :

public void SomeDomainMethod(string id)
{
   //Get user by Id
   User user = Users.Get(id);
   if (user == null)
   {
      Log.Warn("user is not existed");        //<----------------- Log A
      throw new InvalidOperationException("user is not existed");
   }

   //Step 1 
   while(true)
   {
       //do something
   }
   Log.Info("Step 1 is completed");            //<----------------- Log B

   //Step 2
   while(true)
   {
       //do something
   }
   Log.Info("Step 2 is completed");            //<----------------- Log C

}

위의 샘플 방법은 명확하지 않을 수 있습니다. 여기서 보여 드리고자하는 방법은 도메인 관점에서 가장 작은 단위로 처리해야한다는 것입니다. 더 작은 조각으로 나눠서는 안됩니다.

메소드에서 3 가지 로깅 코드 이상으로 이동할 수 있습니까? 그러한 상황에 대한 모범 사례는 무엇입니까?


귀하의 예제 "Step1"및 "Step2"로그는 비즈니스 로직 감사 추적의 일부이고 첫 번째는 기술 로깅이어야합니다. 내가 먼저 이것을 정렬합니다 ...
— tofro

답변:


1

확실한!

그러나 내 경험상 유용한 로깅 에는 두 가지 일반적인 유형이 있습니다.

모든 로그 : 프로파일 링 API를 통해 작성된 로그. 성능 문제를 식별하고 예외를보고하는 데 좋습니다. 시끄러운.

비즈니스 이벤트 로그 : 비즈니스 로직에서 호출 된 로그. 비즈니스가 관심을 가질만한 모든 것. 최소 소음. 주목할만한 논리적 인 "비즈니스"이벤트. 감사 및 KPI에 적합

그래서 저는 두 가지를 강력히 제안합니다. 먼저 New Relic과 같은 다른 모니터링 도구의 기능을 수행 하고 .NET 프로파일 링 API 1을 사용하십시오 . 둘째, 비즈니스 로직에 논리적 비즈니스 이벤트 를 기록하십시오 . 특정 이벤트의 기록을 유지하는 것은 비즈니스 논리입니다.

그리고 일반적으로 로깅 2에 대해 AOP를 제안하지는 않습니다 . 내 경험상 모든 것을 원합니다. 즉 프로파일 러를 사용하거나 논리 / 비즈니스 이벤트를 원합니다. 후자의 경우 비즈니스 로직에서 로거 를 호출하는 것이 더 간단하다고 생각합니다 .


1. 그러나 진지하게, 수천 시간을 절약하고 기존 프로파일 러 도구를 사용하십시오 ...

2. 물론 이것은 당신이 한 측면 이 비즈니스 규칙을 숨길 수있는 좋은 장소가 아니라는 나의 의견을 공유한다고 가정합니다 !


나는 "비즈니스 이벤트 로그"에 동의하고 다른 사람들의 대답과 마찬가지로 로그 코드를 비즈니스 논리에 유지합니다. "모든 로그"부분에서는 SRP를 따르고 비즈니스 로직을 오염시키지 않기 때문에 AOP 솔루션을 사용하는 것을 선호합니다. 어쨌든 먼저 프로파일 링 API를 살펴 보겠습니다.
— Charlie

10

물론 AOP를 쉽게 사용할 수 있습니다. 단순히 부품을 리팩터링

  • ID로 사용자 확보
  • 1 단계
  • 2 단계

에 별도의 방법 (당신은 당신의 코드 청소기를 만들기 위해 중 하나를 했어야으로). 이제 선택한 메소드 호출을 기록하도록 AOP 프레임 워크를 쉽게 구성 할 수 있습니다 ( 여기 참조 ). 예외는 호출자가 직접 기록 할 수 있으므로이를 비즈니스 로직에서 가져 오기 위해 AOP를 사용할 필요가 없습니다.

편집하려면 :

여기서 보여주고 싶은 것은이 방법이 도메인 관점에서 가장 작은 단위로 취급되어야한다는 것입니다. 더 작은 조각으로 나눌 수 없습니다

왜 안됩니까? "비즈니스 로직 컨텍스트"에서 로깅 할 가치가있는 "무언가"를 기록하려는 경우이 "무언가"에 적절한 이름을 부여 할 수있는 경우 대부분의 경우 코드를 메소드로 리팩토링하는 것이 좋습니다. 자체. AOP를 사용하려면 로깅 요구 사항에 관계없이 코드를 구조화 한 방식으로 코드를 구조화해야합니다. 이를 AOP의 단점으로 해석하거나 코드 구조를 개선 할 수있는 피드백을 제공하므로 이점으로 해석 할 수 있습니다.


내 예가 충분히 명확하지 않은 것은 나쁘다. 이 예제에서 실제로 보여주고 싶은 것은 방법이 도메인 관점에서 가장 작은 단위이며 더 작은 조각으로 나눌 수 없다는 것입니다.
— Charlie

@Charlie : 예제는 완벽합니다. 여기서 오해는 아마도 단계보다 더 큰 방법을 사용하는 것이 좋습니다. 그리고 그것은 IMHO 잘못입니다, 그것은 좋은 생각이 아닙니다. 로깅 할 가치가있는 다른 단계가 있다는 것은 명백한 신호입니다.이 단계에는 추상화, 자체 이름, 따라서 자체 메소드가 있어야합니다.
— Doc Brown

@Charlie는 당신의 유닛이나 작업에 의해 호출되는 3 개의 개인 메소드를 막을 수있는 것이 없습니다. 외부 에서이 방법은 동일하게 유지되었지만 이제 로깅에 필요한 추상화가 있습니다.
— Rémi

우려 사항을 기록하여 코드 구조를 구동하려는 경우이 방법이 좋습니다. 때로는 다른 무언가로 운전하고 싶을 수도 있습니다.
— John Wu

@ JohnWu : 코드 구조는 로깅 요구 사항에 관계없이 다른 우려 / 단계를 반영해야합니다. 이것이 코드 구조를 이끄는 것입니다. 이 문제가 해결되면 AOP를 통해 로깅을 수행 할 수 있습니다. 이는 코드에 더 나은 구조를 제공하는 "부수 효과"입니다. 따라서 코드 구조를 구동하는 것은 로깅 문제가 아니라고 생각합니다. 로깅에 AOP를 사용해야하는 요구 사항이 있으면 코드에 필요한 구조가 누락 된 것보다 더 투명 해집니다.
— Doc Brown

3

로깅은 비즈니스 요구 사항의 일부가 아니라면 코드에서 완전히 벗어나는 것이 가장 좋습니다.

즉, "1 단계 완료"와 같은 항목을 기록하지 않으려는 것입니다. 처음에는 디버깅에 유용 할 수 있지만 프로덕션 환경에서는 절대로 볼 수없는 기가 바이트의 쓰레기가 생성됩니다.

Step1Complete가 추가 조치가 필요한 일종의 비즈니스 이벤트 인 경우 클래스에 ILogger 또는 이와 유사한 것을 주입하지 않고도 구식 이벤트를 통해 노출 될 수 있습니다


그것이 내가 생각한 것입니다. 도메인 / 비즈니스 모델 POCO 내에서 로깅에 대한 합리적인 사례를 제시 할 수 없습니다. 로깅은 핵심 비즈니스 모델 인 IMO 외부에 자연스럽게 들어가는 경향이 있습니다.
— jleach

2

몇 가지 일반적인 패턴을 사용하여 비즈니스 로직에서 로깅 코드를 가져올 수 있습니다. 그러나 그렇게 할 가치가 없을 수도 있습니다

예를 들어 리스너 (수공예 하나 또는 이벤트 버스 등)를 사용하면 코드가 다음과 같이 표시됩니다.

public void SomeDomainMethod(string id)
{
   //Get user by Id
   User user = Users.Get(id);
   if (user == null)
   {
      listener.OnUserNotFound(userId);
      throw new InvalidOperationException("user is not existed");
   }

   //Step 1 
   while(true)
   {
       //do something
   }
   listener.OnStep1Finished(......);

   ...

}

리스너에서 로깅을 구현함으로써 비즈니스 로직에는 더 이상 로깅 로직이 없습니다.

그러나 논리의 의미있는 이벤트를 항상 정의 할 수있는 것은 아니기 때문에 이것이 항상 현실적이지는 않을 수 있습니다.

또 다른 접근 방식은 실행중인 프로세스에 주입 할 수있는 Solaris의 Dtrace와 같은 메커니즘을 사용하는 것입니다 (C #에서 비슷한 일을 할 수 있다고 생각합니까?) 로깅 및 통계 수집을 런타임에 정의 할 수 있습니다. 여전히 다른 결점이 있습니다.


AOP가 해결하려고하는 한 가지 문제는 "비즈니스 코드"와 섞여있는 비 업무용 코드 (예 : 로깅 호출)를 읽을 수 없게되는 문제입니다. "청취자"로 "로거"를 교체해도 문제가 해결되지 않습니다. 코드의 가독성이 변경되지 않았습니다.
— Doc Brown

2

또 다른 방법은 비즈니스 로깅과 기술 로깅을 분리하는 것입니다. 그런 다음 비즈니스 로깅을 "감사"라고하고 스토리지 기간과 같은 특정 비즈니스 규칙 세트와 비즈니스 활동 모니터링과 같은 처리 규칙을 적용 할 수 있습니다.

반면에, 기술 로깅 또는 간단히 "로깅"은 기술적 문제의 흔적을 남기는 최후의 수단입니다. 로그 메시지를 유지하지 못하면 비동기적이고 빠르며 견딜 수 있어야합니다. 또한 로그 메시지는 문제의 원인에 근접 할 수있는 가장 적은 수의 프록시를 통과해야합니다.

로깅의 논리는 매우 가변적이며 구현과 밀접하게 연결되어 있으므로 실제로 코드와 분리해야합니까?

감사의 논리는 도메인 논리로 간주되고 그에 따라 처리되어야합니다.

예를 들어, 6 각형 아키텍처에는 클라이언트, 스토리지 및 MQ (및 가능하면 메트릭 및 제어) 포트와 함께 감사 포트가있을 수 있습니다. 이 포트는 2 차 포트입니다. 즉,이 포트의 활동은 외부 시스템이 아닌 비즈니스 코어에 의해 트리거됩니다.


나는 두 종류의 벌목이 있다는 것에 매우 동의했다. 그러나 나는 로깅의 논리가 상당히 변하지 않으며 구현과 밀접하게 결합되어 있습니다. 기술 로깅을 의미합니까? 기술 로깅의 경우 메소드 입력 / 종료 및 메소드 외부에 더 나은 매개 변수 값을 기록하는 데 사용됩니다.
— Charlie

@Charlie 예, "로깅"이란 기술 로깅을 의미합니다. 순수한 기능의 경우 시작 / 종료 / 매개 변수 값 로깅으로 충분합니다. 그런 다음 코스 또는 측면을 사용하거나 Logger 모나드를 사용할 수 있습니다. 그러나 순수한 기능은 테스트 가능하다는 점에서 뛰어납니다. 따라서 로거가 추적 해야하는 문제는 dev / debug 중에 해결 될 것입니다. 기술 로깅이 가장 많이 사용되는 불순한 기능을 사용하면 모든 부수적 인 호출 매개 변수 / 결과, 모든 예외를 기록하려고합니다.
— iTollu

1

클래스 나 메소드에 직접 로깅을 피하는 방법 :

  1. 예외를 던지고 콜 트리에서 멀어지면 catch 블록에 로깅하십시오. 로그 수준을 캡처해야하는 경우 사용자 지정 예외를 throw 할 수 있습니다.

  2. 로깅을 위해 이미 계측 된 메소드를 호출하십시오.


1
로깅이 문제가있는 위치에 있고 "고정"가치가있는 것입니까?
— whatsisname

1

로깅과 비즈니스 로직을 분리해야합니까? 수행되는 로깅은 작성된 비즈니스 로직과 일치하므로 동일한 클래스 / 기능에있는 것이 좋습니다. 더 중요한 것은 코드의 가독성을 향상시키는 데 도움이됩니다.

그러나 비즈니스 로직에서 로깅을 분리하려는 경우 사용자 정의 예외를 발생시키고 해당 예외를 로깅에 처리해야합니다.


0

아니요, C #에는 없습니다.

OP, 특정 질문에 대한 답변은 아니오, C #이 아닙니다. 다른 더 원시적 인 AOP 언어가있을 수 있지만, 내가 본 C #의 AOP에 대한 모든 접근 방식은 결합 지점 의 컨텍스트에서만 종횡비 동작을 적용 할 수 있습니다 . 즉, 하나의 코드 블록과 다른. 다른 동작을 호출하는 것 외에는, 특정 동작이 메소드 중간에 실행되지 않습니다.

특정 로깅 비트를 "Apsect-ize"할 수 있습니다

즉, 로그 쓰기가 아니라 로깅과 관련된 특정 문제를 추출 할 수 있습니다. 예를 들어, 메소드 시작시 실행 된 컷 포인트는 로깅 컨텍스트를 설정하고 모든 입력 매개 변수를 출력 할 수 있으며 종료시 예외를 포착하거나 로그를 영구 스토리지에 커밋 할 수 있습니다.

어쨌든 로그 작성 은 측면이 아닙니다.

어쨌든 로그 작성은 실제로 교차 문제가 아니라고 덧붙입니다. 최소한 디버그 로깅은 아닙니다. 이것에 대한 나의 증거는 당신이이 측면이 무엇을하는지 완전히 설명하는 교차 절단 요구 사항을 작성할 수 없다는 것입니다. 로그를 작성하는 목적은 논리와 각 메소드의 논리는 합리적으로 고유해야합니다 ( DRY 참조 ).

다시 말해서, 로그 기록과 기록되는 내용 사이에는 불가분의 논리적 의존성이 있습니다. 일반화 할 수 없습니다.

그러나 감사는

일종의 기능적 로깅 요구 사항 (예 : 부인 방지 요구 사항 을 지원하는 감사 로깅 )이있는 경우 일부는 메소드 중간에 이러한 로그 쓰기를 실행 해야하는 경우 측면 지향적 사고와 일치하는 방식으로 코드를 구성하지 않았습니다. 이 경우 필요한 세부 수준을 얻을 때까지 코드를 별도의 메서드로 추출해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.