매개 변수 목록에서 로거의 위치는 무엇입니까?


12

내 코드에서는 생성자의 매개 변수 목록을 통해 많은 클래스에 로거를 주입합니다.

나는 그것을 무작위로 넣는 것을 알아 차렸다 : 때로는 목록의 첫 번째, 때로는 마지막, 때로는 그 사이에있다

선호 사항이 있습니까? 내 직감에 따르면이 경우 일관성이 도움이되며 개인 선호도를 우선으로 설정하여 누락 된 경우 더 쉽게 알 수 있고있을 때 건너 뛸 수 있습니다.

답변:


31

로거는 우리가 "교차적인 문제"라고 부릅니다. 그들은 Aspect-Oriented Programming과 같은 기술을 낳는다. 속성으로 클래스를 장식하거나 코드 짜기를 수행하는 방법이 있다면 객체와 매개 변수 목록을 "순수"하게 유지하면서 로깅 기능을 얻는 좋은 방법입니다.

로거에 전달하려는 유일한 이유는 다른 로깅 구현을 지정하려는 경우이지만 대부분의 로깅 프레임 워크는 서로 다른 로깅 대상에 대해 구성 할 수있는 유연성이 있습니다. (로그 파일, Windows 이벤트 관리자 등)

이러한 이유로 로깅 목적으로 로거를 모든 클래스에 전달하는 대신 로깅을 시스템의 자연스러운 부분으로 만드는 것을 선호합니다. 그래서 내가 일반적으로하는 일은 적절한 로깅 네임 스페이스를 참조하고 클래스에서 로거를 사용하는 것입니다.

여전히 로거를 전달하려는 경우 선호하는 것은 매개 변수 목록 의 마지막 매개 변수 로 만드는 것입니다 (가능한 경우 선택적 매개 변수로 사용). 첫 번째 매개 변수가되는 것은별로 의미가 없습니다. 첫 번째 매개 변수는 클래스의 작업과 가장 관련이있는 가장 중요한 매개 변수 여야합니다.


11
+! 로거를 생성자 매개 변수에서 제외하십시오. 나는 정적 로거를 선호하므로 모든 사람들이 같은 것을 사용하고 있음을 알 수 있습니다
Steven A. Lowe

1
@ StevenA.Lowe주의하지 않으면 정적 로거를 사용하면 다른 문제가 발생할 수 있습니다 (예 : C ++의 정적 초기화 순서 fiasco). 로거를 전 세계적으로 액세스 할 수있는 엔티티로 갖는 것은 매력이 있지만, 그러한 설계가 전체 아키텍처에 적합한 지 여부와 방법을 신중하게 평가해야합니다.
ComicSansMS

@ComicSansMS : 물론 스레딩 문제 등 개인적 선호도- "가능한 한 단순하지만 단순하지는 않습니다";)
Steven A. Lowe

정적 로거를 갖는 것이 문제가 될 수 있습니다. 의존성 주입이 더 어려워집니다 (DI 컨테이너로 인스턴스화 된 싱글 톤 로거를 의미하지 않는 한) 아키텍처를 변경하려는 경우 고통 스럽습니다. 예를 들어 지금은 각 함수 실행에 매개 변수로 로거를 전달하는 Azure 함수를 사용하고 있으므로 생성자를 통해 로거를 전달하는 것을 후회합니다.
Slothario

@Slothario : 정적 로거의 아름다움입니다. 어떤 종류의 주입도 필요하지 않습니다.
Robert Harvey

7

함수 오버로딩을 사용하는 언어에서는 인수가 선택 사항 일 가능성이 높을수록 더 옳 아야한다고 주장합니다. 이렇게하면 누락 된 오버로드를 만들 때 일관성이 생깁니다.

foo(mandatory);
foo(mandatory, optional);
foo(mandatory, optional, evenMoreOptional);

기능적 언어에서는 그 반대가 더 유용합니다. 기본값을 선택할 가능성이 높을수록 더 왼쪽에 있어야합니다. 이를 통해 단순히 인수를 적용하여 함수를보다 쉽게 ​​전문화 할 수 있습니다.

addThreeToList = map (+3)

그러나 다른 답변에서 언급했듯이 시스템의 모든 클래스의 인수 목록에서 로거를 명시 적으로 전달하고 싶지 않을 것입니다.


3

이 문제에 대해 많은 시간을 할애 할 수 있습니다.

표준 로깅 구현을 사용하는 언어의 경우 모든 클래스에서 직접 표준 로거를 인스턴스화하십시오.

표준 구현이없는 언어의 경우 로깅 파사드 프레임 워크를 찾아이를 사용하십시오. Java에서는 slf4j 를 선택하는 것이 좋습니다.

개인적으로 하나의 구체적인 로깅 구현을 고수하고 모든 것을 syslog에 보냅니다. 모든 우수한 로그 분석 도구는 여러 앱 서버의 sysout 로그를 포괄적 인 보고서로 결합 할 수 있습니다.

함수 시그니처에 하나 또는 두 개의 종속성 서비스와 일부 "실제"인수가 포함 된 경우 종속성을 마지막에 배치합니다.

int calculateFooBarSum(int foo, int bar, IntegerSummationService svc)

내 시스템에는 그러한 서비스가 5 개 이하인 경향이 있기 때문에 항상 모든 기능 서명에서 서비스가 동일한 순서로 포함되도록해야합니다. 알파벳 순서는 다른 것만 큼 좋습니다. (외부 : 뮤텍스 처리를 위해이 방법 론적 접근 방식을 유지하면 교착 상태가 발생할 가능성이 줄어 듭니다.)

앱에서 수십 가지 이상의 종속성을 주입하는 경우 시스템을 별도의 하위 시스템으로 분할해야 할 수도 있습니다 (마이크로 서비스라고합니까?).


calculateFooBarSum을 호출하기 위해 속성 주입을 사용하는 것이 어색한 것 같습니다.
phu

2

로거는 문자 그대로 어디에서나 사용할 수 있어야하기 때문에 특별한 경우입니다.

로거를 모든 클래스의 생성자에 전달하기로 결정한 경우,이를 수행하는 방법에 대해 일관된 규칙을 설정해야합니다 (예 : 항상 첫 번째 매개 변수, 항상 참조로 전달됨). m_logger (TheLogger) 등). 전체 코드베이스에서 사용될 모든 것은 언젠가 일관성의 이점을 얻을 것입니다.

또는 전달할 항목없이 모든 클래스가 자신의 로거 객체를 인스턴스화 할 수 있습니다. 로거는 작동하기 위해 "마술로"몇 가지를 알아야 할 수도 있지만 클래스 정의에서 파일 경로를 하드 코딩하는 것은 잠재적으로 수백 가지의 다른 클래스에 올바르게 전달하는 것보다 유지 관리가 쉽고 지루하지 않으며 전역 변수를 사용하여 tedium을 우회하는 것보다 훨씬 덜 악합니다. (로거는 전역 변수에 대한 합법적 인 사용 사례는 거의 없습니다.)


1

나는 로거가 클래스에 전달되지 않고 정적으로 액세스되어야한다고 제안하는 사람들에 동의합니다. 당신이 그것을 전달하려는 강한 이유가 경우 (아마도 다른 인스턴스가 다른 위치 또는 무언가에 로그인 할) 나는 당신이 생성자를 사용하여 통과하지 못한 제안이 아니라, 그렇게 예를 수행하기 위해 별도의 전화를 Class* C = new C(); C->SetLogger(logger);대신 ...보다Class* C = new C(logger);

이 방법을 선호하는 이유는 로거가 본질적으로 클래스의 일부가 아니라 다른 목적으로 사용되는 주입 된 기능이기 때문입니다. 생성자 목록에 배치하면 클래스의 요구 사항이되고 클래스의 실제 논리적 상태에 속합니다. 기대하는 것이 합리적이다 (예를 들어, 대부분의 클래스 모든 비록 포함), 그 경우 X != Y다음 C(X) != C(Y)하지만 당신이 너무 같은 클래스의 인스턴스를 비교하는 경우 로거 불평등을 테스트 할 것 같지는 않다.


1
물론 이것은 로거를 생성자가 사용할 수 없다는 단점이 있습니다.
Ben Voigt

1
나는이 답변을 정말로 좋아한다. 그것은 당신이 그것을 사용하는 것과 별도로 신경 써야 할 클래스의 측면에 대한 로깅을 만듭니다. 많은 클래스의 생성자에 로거를 추가하는 경우 종속성 삽입을 사용하고있을 가능성이 있습니다. 모든 언어를 말할 수는 없지만 C #에서는 일부 DI 구현도 속성 (getter / setter)에 직접 주입됩니다 .
jpmc26

@ BenVoigt : 그것은 사실이며, 이런 식으로하지 않는 킬러 이유 일 수 있지만 일반적으로 로거가 설정되면 응답으로 생성자에서 수행하는 로깅을 수행 할 수 있습니다.
Jack Aidley

0

언급할만한 가치가 있습니다. 여기서 다른 대답을 보지 못했습니다. 로거를 속성이나 정적을 통해 주입하면 클래스를 단위 테스트하기가 어렵습니다. 예를 들어, 속성을 통해 로거를 주입하면 이제 로거를 사용하는 메소드를 테스트 할 때마다 해당 로거를 주입해야합니다. 이것은 클래스에 필요하기 때문에 생성자 종속성으로 설정할 수도 있음을 의미 합니다.

정적은 동일한 문제에 적합합니다. 로거가 작동하지 않으면 로거가 클래스 책임의 '일부'일 필요는 없지만 클래스가 로거를 사용하는 경우 전체 클래스가 실패합니다. 적어도 로거가 어떤 의미에서 "있다"는 것을 알고 있어야합니다.

특히 TDD를 사용하는 경우 생각할만한 음식. 내 생각에 로거는 실제로 클래스의 테스트 가능한 부분이 아니어야합니다 (클래스를 테스트 할 때 로깅도 테스트해서는 안됩니다).


1
hmmm ... 클래스에서 로깅을 수행하려고하지만 (로깅은 사양에 있어야 함) 로거를 사용하여 테스트하고 싶지 않습니다. 이것이 가능한가? 나는 당신의 요점이 아닌 것 같아요. 테스트 도구가 고장난 경우 테스트 할 수 없습니다. 테스트 도구에 의존하지 않는 방식으로 설계하는 것은 약간 과도하게 엔지니어링 된 IMHO
Hogan

1
내 요점은 생성자를 사용하고 클래스에서 메서드를 호출했는데 속성을 설정하지 않아 여전히 실패하면 클래스 디자이너가 생성자 뒤에있는 개념을 잘못 이해했다는 것입니다. 클래스에 로거가 필요한 경우 생성자에 로거를 삽입해야합니다. 즉 생성자가있는 것입니다.
Dan Pantry

음 .. 진짜 아니에요 "프레임 워크"의 로깅 시스템 부분을 고려하면 생성자의 일부로 의미가 없습니다. 그러나 이것은 다른 답변에서 명확하게 언급되었습니다.
Hogan

1
나는 재산 주입에 반대하고있다. 반드시 생성자에 주입하는 것을 옹호하는 것은 아닙니다. 제 생각에는 재산 주입보다 선호되는 것입니다.
Dan Pantry

"이것이 가능한가?" 또한, 예, IL 직조는 존재하고 최고의 답변 에서 언급 된 것입니다 ... mono-project.com/docs/tools+libraries/libraries/libraries/Mono.Cecil
Dan Pantry

0

로거 객체를 각 클래스 인스턴스로 전달하기에는 너무 게으르다. 그래서 내 코드에서 이러한 종류의 것들은 정적 필드에 있거나 정적 필드의 스레드 로컬 변수에 있습니다. 후자는 훌륭하고 각 스레드마다 다른 로거를 사용할 수 있으며 다중 스레드 응용 프로그램에서 의미 있고 예상되는 작업을 수행하는 로깅을 설정 및 해제하는 방법을 추가 할 수 있습니다.

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