답변:
로거는 우리가 "교차적인 문제"라고 부릅니다. 그들은 Aspect-Oriented Programming과 같은 기술을 낳는다. 속성으로 클래스를 장식하거나 코드 짜기를 수행하는 방법이 있다면 객체와 매개 변수 목록을 "순수"하게 유지하면서 로깅 기능을 얻는 좋은 방법입니다.
로거에 전달하려는 유일한 이유는 다른 로깅 구현을 지정하려는 경우이지만 대부분의 로깅 프레임 워크는 서로 다른 로깅 대상에 대해 구성 할 수있는 유연성이 있습니다. (로그 파일, Windows 이벤트 관리자 등)
이러한 이유로 로깅 목적으로 로거를 모든 클래스에 전달하는 대신 로깅을 시스템의 자연스러운 부분으로 만드는 것을 선호합니다. 그래서 내가 일반적으로하는 일은 적절한 로깅 네임 스페이스를 참조하고 클래스에서 로거를 사용하는 것입니다.
여전히 로거를 전달하려는 경우 선호하는 것은 매개 변수 목록 의 마지막 매개 변수 로 만드는 것입니다 (가능한 경우 선택적 매개 변수로 사용). 첫 번째 매개 변수가되는 것은별로 의미가 없습니다. 첫 번째 매개 변수는 클래스의 작업과 가장 관련이있는 가장 중요한 매개 변수 여야합니다.
함수 오버로딩을 사용하는 언어에서는 인수가 선택 사항 일 가능성이 높을수록 더 옳 아야한다고 주장합니다. 이렇게하면 누락 된 오버로드를 만들 때 일관성이 생깁니다.
foo(mandatory);
foo(mandatory, optional);
foo(mandatory, optional, evenMoreOptional);
기능적 언어에서는 그 반대가 더 유용합니다. 기본값을 선택할 가능성이 높을수록 더 왼쪽에 있어야합니다. 이를 통해 단순히 인수를 적용하여 함수를보다 쉽게 전문화 할 수 있습니다.
addThreeToList = map (+3)
그러나 다른 답변에서 언급했듯이 시스템의 모든 클래스의 인수 목록에서 로거를 명시 적으로 전달하고 싶지 않을 것입니다.
이 문제에 대해 많은 시간을 할애 할 수 있습니다.
표준 로깅 구현을 사용하는 언어의 경우 모든 클래스에서 직접 표준 로거를 인스턴스화하십시오.
표준 구현이없는 언어의 경우 로깅 파사드 프레임 워크를 찾아이를 사용하십시오. Java에서는 slf4j 를 선택하는 것이 좋습니다.
개인적으로 하나의 구체적인 로깅 구현을 고수하고 모든 것을 syslog에 보냅니다. 모든 우수한 로그 분석 도구는 여러 앱 서버의 sysout 로그를 포괄적 인 보고서로 결합 할 수 있습니다.
함수 시그니처에 하나 또는 두 개의 종속성 서비스와 일부 "실제"인수가 포함 된 경우 종속성을 마지막에 배치합니다.
int calculateFooBarSum(int foo, int bar, IntegerSummationService svc)
내 시스템에는 그러한 서비스가 5 개 이하인 경향이 있기 때문에 항상 모든 기능 서명에서 서비스가 동일한 순서로 포함되도록해야합니다. 알파벳 순서는 다른 것만 큼 좋습니다. (외부 : 뮤텍스 처리를 위해이 방법 론적 접근 방식을 유지하면 교착 상태가 발생할 가능성이 줄어 듭니다.)
앱에서 수십 가지 이상의 종속성을 주입하는 경우 시스템을 별도의 하위 시스템으로 분할해야 할 수도 있습니다 (마이크로 서비스라고합니까?).
로거는 문자 그대로 어디에서나 사용할 수 있어야하기 때문에 특별한 경우입니다.
로거를 모든 클래스의 생성자에 전달하기로 결정한 경우,이를 수행하는 방법에 대해 일관된 규칙을 설정해야합니다 (예 : 항상 첫 번째 매개 변수, 항상 참조로 전달됨). m_logger (TheLogger) 등). 전체 코드베이스에서 사용될 모든 것은 언젠가 일관성의 이점을 얻을 것입니다.
또는 전달할 항목없이 모든 클래스가 자신의 로거 객체를 인스턴스화 할 수 있습니다. 로거는 작동하기 위해 "마술로"몇 가지를 알아야 할 수도 있지만 클래스 정의에서 파일 경로를 하드 코딩하는 것은 잠재적으로 수백 가지의 다른 클래스에 올바르게 전달하는 것보다 유지 관리가 쉽고 지루하지 않으며 전역 변수를 사용하여 tedium을 우회하는 것보다 훨씬 덜 악합니다. (로거는 전역 변수에 대한 합법적 인 사용 사례는 거의 없습니다.)
나는 로거가 클래스에 전달되지 않고 정적으로 액세스되어야한다고 제안하는 사람들에 동의합니다. 당신이 그것을 전달하려는 강한 이유가 경우 (아마도 다른 인스턴스가 다른 위치 또는 무언가에 로그인 할) 나는 당신이 생성자를 사용하여 통과하지 못한 제안이 아니라, 그렇게 예를 수행하기 위해 별도의 전화를 Class* C = new C(); C->SetLogger(logger);
대신 ...보다Class* C = new C(logger);
이 방법을 선호하는 이유는 로거가 본질적으로 클래스의 일부가 아니라 다른 목적으로 사용되는 주입 된 기능이기 때문입니다. 생성자 목록에 배치하면 클래스의 요구 사항이되고 클래스의 실제 논리적 상태에 속합니다. 기대하는 것이 합리적이다 (예를 들어, 대부분의 클래스 모든 비록 포함), 그 경우 X != Y
다음 C(X) != C(Y)
하지만 당신이 너무 같은 클래스의 인스턴스를 비교하는 경우 로거 불평등을 테스트 할 것 같지는 않다.
언급할만한 가치가 있습니다. 여기서 다른 대답을 보지 못했습니다. 로거를 속성이나 정적을 통해 주입하면 클래스를 단위 테스트하기가 어렵습니다. 예를 들어, 속성을 통해 로거를 주입하면 이제 로거를 사용하는 메소드를 테스트 할 때마다 해당 로거를 주입해야합니다. 이것은 클래스에 필요하기 때문에 생성자 종속성으로 설정할 수도 있음을 의미 합니다.
정적은 동일한 문제에 적합합니다. 로거가 작동하지 않으면 로거가 클래스 책임의 '일부'일 필요는 없지만 클래스가 로거를 사용하는 경우 전체 클래스가 실패합니다. 적어도 로거가 어떤 의미에서 "있다"는 것을 알고 있어야합니다.
특히 TDD를 사용하는 경우 생각할만한 음식. 내 생각에 로거는 실제로 클래스의 테스트 가능한 부분이 아니어야합니다 (클래스를 테스트 할 때 로깅도 테스트해서는 안됩니다).