적절한 애플리케이션 로깅을 위한 10 가지 팁

나는 그들이 매우 흥미로운 발견,따라서 좀 더 컴팩트 한 형식으로 집계하고 당신에게 제시하기로 결정. 그래서,여기에 깨끗하고 유용한 로그에 대한 그의 제안이 있습니다:(참고: 1)작업에 적합한 도구 사용

많은 프로그래머가 응용 프로그램의 동작과 현재 활동을 기록하는 것이 얼마나 중요한지 잊어 버린 것 같습니다. 누군가가 넣을 때:

log.info("Happy and carefree logging");

행복하게 코드의 어딘가에서 그는 유지 보수,튜닝 및 실패 식별 중에 응용 프로그램 로그의 중요성을 깨닫지 못할 것입니다. 좋은 로그의 가치를 과소 평가하는 것은 끔찍한 실수입니다.이는 주로 훌륭한 패턴 대체 지원 때문입니다:

log.debug("Found {} records matching filter: '{}'", records, filter); 

로그 4 에서는 다음을 사용해야 합니다:

log.debug("Found " + records + " records matching filter: '" + filter + "'");

이것은 더 길고 덜 읽을 수있을뿐만 아니라 문자열 연결을 광범위하게 사용하기 때문에 비효율적입니다. 대체 기능을 추가합니다. 또한 로깅 문이 필터링되면 문자열 연결을 피할 수 있고 문자열()이 호출되지 않기 때문에 더 이상 디버깅할 필요가 없습니다(). 필터 문자열 매개 변수 주위에 작은 따옴표를 발견 했습니까?

SLF4J 은 외관. 그것은 많은 흥미로운 기능을 가지고 있으며,반대로 로그 4 제이,적극적으로 개발되고있다.그들의 좌우명을 인용하기 위하여:

이 문제를 해결하는 방법은 무엇입니까?밖으로.나는 무거운 부하에서 하나의 기존 응용 프로그램에 추가 및 몇 가지 다른 행동에 그것을 볼 수 있습니다. 관리자와 비즈니스 사용자 모두이 간단한 유틸리티에 의해 생성 된 좋은 그래프에 감동했다. 또한 우리는 시간에 성능 결함을 발견 할 수 있었다. 그러나 지금은 개발자 가이드를 확인하십시오.

또한,커먼즈 로깅 의존성을 제거하기 위한 간단한 접근방식을 제안하였다(그의 논평 참조).

2)잊지 마세요,로깅 수준이 당신을 위해 있습니다

로깅 문을 만들 때마다 어떤 로깅 수준이 이러한 유형의 이벤트에 적합한 지 열심히 생각합니다. 어떻게 든 프로그래머의 90%는 로깅 수준에주의를 기울이지 않고 일반적으로 정보 또는 디버그와 같은 수준의 모든 것을 로깅합니다. 왜? 로깅 프레임 워크는 시스템에 비해 두 가지 주요 이점이 있습니다.밖으로.,즉 범주와 수준. 둘 다 로깅 문을 영구적으로 또는 진단 시간에만 선택적으로 필터링 할 수 있습니다. 당신이 정말로 차이를 볼 수 없다면,이 테이블을 인쇄하고”로그”를 입력 할 때마다 그것을 살펴보십시오.”당신의 아이디에서:

오류–끔찍한 일이 일어 났고 즉시 조사해야합니다. 어떤 시스템도 이 수준에 로그온된 항목을 허용할 수 없습니다. 예:데이터베이스 사용 불가능,미션 크리티컬 사용 사례를 계속할 수 없습니다.

경고-프로세스가 계속 될 수 있지만 각별히주의하십시오. 실제로 나는 항상 여기에 두 가지 수준을 갖고 싶었습니다:해결 방법이 존재하는 명백한 문제(예:: “캐시 된 값을 사용하여 현재 데이터를 사용할 수 없음”)및 두 번째(이름:주의)잠재적 인 문제 및 제안 사항. 예:”개발 모드에서 실행되는 응용 프로그램”또는”관리 콘솔이 암호로 보호되지 않음”. 응용 프로그램은 경고 메시지를 허용 할 수 있지만 항상 정당화하고 검사해야합니다.

정보–중요한 비즈니스 프로세스가 완료되었습니다. 이상적인 세계에서,관리자 또는 고급 사용자는 정보 메시지를 이해하고 신속하게 응용 프로그램이 무엇을하고 있는지 알 수 있어야한다. 예를 들어 응용 프로그램이 비행기 티켓 예약에 관한 모든 경우 각 티켓 당”예약 된 티켓”이라는 정보 문이 하나 있어야합니다. 정보 메시지의 다른 정의:응용 프로그램의 상태를 크게 변경하는 각 작업(데이터베이스 업데이트,외부 시스템 요청).

디버그 개발자 물건. 나중에 어떤 종류의 정보를 기록 할 가치가 있는지 논의 할 것입니다.

추적–개발 전용 매우 자세한 정보입니다. 프로덕션 환경에 배포한 후 짧은 기간 동안 추적 메시지를 유지할 수 있지만 이러한 로그 문을 임시 문으로 처리하면 결국 해제되거나 해제될 수 있습니다. 디버그와 추적을 구분하는 것이 가장 어렵지만 로깅 문을 넣고 기능을 개발 및 테스트한 후에 제거하면 추적 수준이어야 합니다.

위의 목록은 당신이 따라야 할 지침의 자신의 세트를 만들 수 있습니다,단지 제안이지만,몇 가지를 가지고하는 것이 중요합니다. 내 경험은 항상 모든 것이 필터링없이(적어도 응용 프로그램 코드에서)기록되지만 로그를 신속하게 필터링하고 적절한 세부 수준으로 정보를 추출 할 수있는 기능을 갖는 것이 생명을 구할 수 있다는 것입니다.

마지막으로 언급 할 가치가있는 것은 악명 높은*사용()조건입니다. 일부는 모든 로깅 문 앞에 넣습니다:

if(log.isDebugEnabled()) log.debug("Place for your commercial");

개인적으로,나는이 관용구가 피해야 할 단지 혼란 스럽다는 것을 알게됩니다. 성능 향상(특히 이전에 논의 된 패턴 대체를 사용할 때)은 관련이없는 것처럼 보이며 조기 최적화처럼 냄새가납니다. 또한 중복을 발견 할 수 있습니까? 명시 적 조건을 갖는 것이 정당화 될 때 매우 드문 경우가 있습니다–로깅 메시지를 구성하는 데 비용이 많이 든다는 것을 증명할 수 있습니다. 다른 상황에서는 로깅 작업을 수행하고 로깅 프레임 워크가 작업(필터링)을 수행하도록하십시오.

3)당신은 당신이 로깅하는 것을 알고 계십니까?

로깅 문을 발행 할 때마다 잠시 시간을내어 로그 파일에 정확히 무엇이 들어 있는지 살펴보십시오. 나중에 로그를 읽고 잘못된 문장을 찾으십시오. 우선,다음과 같은 것을 피하십시오:

log.debug("Processing request with id: {}", request.getId());

당신은 요청이 여기에 널 아니라는 것을 절대적으로 확신?

또 다른 함정은 로깅 컬렉션입니다. 최대 절전 모드를 사용하여 데이터베이스에서 도메인 개체 컬렉션을 가져온 경우 여기와 같이 부주의하게 기록합니다:

log.debug("Returning users: {}", users);

이 문장이 실제로 인쇄 된 경우에만 호출됩니다. 메모리 부족 오류,엔+1 선택 문제,스레드 기아(로깅은 동기식입니다!),지연 초기화 예외,로그 저장소가 완전히 채워짐-각각 발생할 수 있습니다.

예를 들어 도메인 개체의 아이디(또는 컬렉션의 크기 만)만 기록하는 것이 훨씬 좋습니다. 자바에서는 믿을 수 없을 정도로 어렵고 번거롭다. 이 라이브러리는 그러한 문제를 표준화된 프로그래밍 인터페이스를 제공함으로써 해결합니다.:

log.debug("Returning user ids: {}", collect(users, "id"));

여기서 수집()방법은 다음과 같이 구현 될 수있다:

public static Collection collect(Collection collection, String propertyName) { return CollectionUtils.collect(collection, new BeanToPropertyValueTransformer(propertyName));}

마지막으로 언급 할 것은 부적절한 구현 또는 사용입니다. 먼저 로깅 명령문의 아무 곳이나 나타나는 각 클래스에 대해 문자열()을 만듭니다. 둘째,배열 및 비 일반적인 컬렉션을 조심하십시오. 배열과 일부 이상한 컬렉션은 각 항목의 문자열()을 호출하는 문자열()을 구현하지 않을 수 있습니다. 배열의 사용 방법은 다음과 같습니다. 그리고 잘못 형식의 메시지를 발견하기 위해 자주 로그를 읽어 보시기 바랍니다.

4)부작용 방지

로깅 문은 응용 프로그램의 동작에 거의 영향을 미치지 않아야 합니다. 최근에 내 친구가 일부 특정 환경에서 실행할 때만 최대 절전 모드의 게으른 초기화를 던진 시스템의 예를 제시했습니다. 컨텍스트에서 짐작할 수 있듯이 일부 로깅 문으로 인해 세션이 첨부 될 때 지연 초기화 된 컬렉션이로드되었습니다. 이 환경에서는 로깅 수준이 증가하고 컬렉션이 더 이상 초기화되지 않았습니다. 이 컨텍스트를 모른 채 버그를 찾는 데 얼마나 걸릴지 생각해보십시오.

또 다른 부작용은 응용 프로그램 속도를 늦추는 것입니다. 빠른 답변:로그가 너무 많거나 잘못 사용 된 경우 문자열()및/또는 문자열 연결,로깅은 성능 부작용이 있습니다. 얼마나 큰? 음,과도한 로깅으로 인한 스레드 기아 때문에 15 분마다 서버가 다시 시작되는 것을 보았습니다. 지금 이것은 부작용 이다! 내 경험에서,미립자의 몇 수백 아마 당신이 시간 당 디스크에 로그인 할 수 있습니다 얼마나의 상한이다.

물론 로깅 문 자체가 실패하고 예외로 인해 비즈니스 프로세스가 종료되는 경우 이는 또한 큰 부작용입니다. 나는 이것을 피하기 위해 그러한 구조를 보았다:

try { log.trace("Id=" + request.getUser().getId() + " accesses " + manager.getPage().getUrl().toString())} catch(NullPointerException e) {}

이것은 실제 코드이지만,세상을 조금 더 나은 곳으로 만들고 그렇게하지 마십시오.

5)간결하고 설명적인

각 로깅 문에는 데이터와 설명이 모두 포함되어야합니다. 다음 예를 고려하십시오:

log.debug("Message processed");log.debug(message.getJMSMessageID());log.debug("Message with id '{}' processed", message.getJMSMessageID());

알 수없는 응용 프로그램에서 오류를 진단하는 동안 어떤 로그를 확인 하시겠습니까? 저를 믿으십시오,위의 모든 예는 거의 똑같이 일반적입니다. 또 다른 안티 패턴:

if(message instanceof TextMessage) //...else log.warn("Unknown message type");

실제 메시지 유형,메시지 아이디 등을 포함하기가 너무 어려웠습니까? 경고 문자열에? 뭔가 잘못 된 거 알아,하지만 뭐? 문맥은 무엇 이었습니까?

세 번째 안티 패턴은”매직 로그”입니다. 실제 예:팀의 대부분의 프로그래머는 3 앰퍼샌드 다음에 느낌표,해시,의사 난수 영숫자 문자열 로그가”수신 된 메시지”를 의미한다는 것을 알고있었습니다. 아무도 로그를 변경 귀찮게,단순히 누군가가 키보드를 치고 몇 가지 독특한 선택”&&&!#”문자열,그것은 쉽게 자신에 의해 발견 될 수 있도록.

결과적으로 전체 로그 파일은 임의의 문자 시퀀스처럼 보입니다. 누군가는 그 파일을 유효한 펄 프로그램이라고 생각할 수도 있습니다. 대신 로그 파일은 읽기 쉽고 깨끗하며 설명이 가능해야 합니다. 매직 넘버,로그 값,숫자,신분증을 사용하고 컨텍스트를 포함하지 마십시오. 처리 중인 데이터를 표시하고 그 의미를 표시합니다. 프로그램이 실제로 무엇을하고 있는지 보여줍니다. 좋은 로그는 응용 프로그램 코드 자체에 대한 훌륭한 문서 역할을 할 수 있습니다.

암호 및 개인 정보를 기록하지 말라고 언급 했습니까? 하지 마!

6)패턴 조정

로깅 패턴은 만드는 모든 로깅 문에 의미있는 컨텍스트를 투명하게 추가하는 훌륭한 도구입니다. 그러나 너는 너의 본안에 포함할 것이다 정보를 아주 주의깊게 사려해야 한다. 예를 들어,날짜가 이미 로그 파일 이름에 포함되어 있으므로 로그가 매 시간마다 롤백되는 날짜를 로깅하는 것은 의미가 없습니다. 반대로 스레드 이름을 기록하지 않으면 두 스레드가 동시에 작동 할 때 로그를 사용하여 프로세스를 추적 할 수 없습니다. 요즘 거의 죽은 단일 스레드 응용 프로그램에서 잘 될 수 있습니다.

내 경험에 비추어 볼 때 이상적인 로깅 패턴에는 현재 시간(날짜,밀리 초 정밀도 제외),로깅 수준,스레드 이름,간단한 로거 이름(정규화되지 않음)및 메시지가 포함되어야합니다. 로그백에서 그것은 다음과 같은 것입니다:

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} %-5level %m%n</pattern> </encoder></appender>

파일 이름,클래스 이름 및 줄 번호를 포함해서는 안되지만 매우 유혹적입니다. 나는 심지어 코드에서 발행 된 빈 로그 문을 보았다:

log.info("");

프로그래머는 줄 번호가 로깅 패턴의 일부가 될 것이라고 가정하고”파일의 67 번째 줄에 빈 로깅 메시지가 나타나면(인증()방법),사용자가 인증되었음을 의미합니다.” 또한 클래스 이름,메소드 이름 및/또는 줄 번호를 로깅하면 성능에 심각한 영향을 미칩니다.

로깅 프레임워크의 좀 더 고급 기능은 매핑된 진단 컨텍스트의 개념입니다. 맵은 단순히 스레드 로컬 단위로 관리되는 맵입니다. 이 맵에 키-값 쌍을 넣을 수 있으며 그 이후로이 스레드에서 발행 된 모든 로깅 문은이 값을 패턴의 일부로 첨부 할 것입니다.

7)로그 메서드 인수 및 반환 값

개발 중에 버그를 발견하면 일반적으로 잠재적 원인을 추적하려고 디버거를 실행합니다. 이제 잠시 동안 디버거를 사용할 수 없다고 상상해보십시오. 예를 들어,버그는 몇 일 전에 고객 환경에 자신을 나타 내기 때문에 당신이 가진 모든 로그입니다. 당신은 그(것)들에서 아무거나를 찾아낼 수 있을는가 것입니다?

각 메소드 입력 및 출력(인수 및 반환 값)을 로깅하는 간단한 규칙을 따르는 경우 디버거가 더 이상 필요하지 않습니다. 물론 합리적이어야하지만 외부 시스템(데이터베이스 포함),블록,대기 등에 액세스하는 모든 방법이 있어야합니다. 고려되어야한다. 이 패턴을 따르십시오:

public String printDocument(Document doc, Mode mode) { log.debug("Entering printDocument(doc={}, mode={})", doc, mode); String id = //Lengthy printing operation log.debug("Leaving printDocument(): {}", id); return id;}

메소드 호출의 시작과 끝을 모두 로깅하기 때문에 비효율적 인 코드를 수동으로 검색하고 교착 상태 및 기아의 가능한 원인을 검색 할 수 있습니다. 당신의 방법이 의미있는 이름을 가지고 있다면,로그를 읽는 것은 즐거움이 될 것입니다. 또한 각 단계에서 처리 된 내용을 정확히 알고 있기 때문에 무엇이 잘못되었는지 분석하는 것이 훨씬 간단합니다. 간단한 작업 측면을 사용하여 코드에 다양한 메서드를 기록할 수도 있습니다. 이 코드 중복을 줄일 수 있습니다,하지만 조심,그것은 거대한 로그의 엄청난 양이 발생할 수 있기 때문에.

이러한 유형의 로그에 가장 적합한 디버그 또는 추적 수준을 고려해야 합니다. 그리고 일부 메소드가 너무 자주 호출되고 로깅이 성능에 해를 끼칠 수 있음을 발견하면 해당 클래스의 로깅 수준을 줄이거 나 로그를 완전히 제거하십시오(전체 메소드 호출에 대해 하나만 남겨 둘 수 있습니까?)그러나 로깅 문이 너무 적기보다는 너무 많이 갖는 것이 항상 좋습니다. 단위 테스트와 동일한 방식으로 로깅 문을 처리합니다. 시스템의 어떤 부분도 로그 없이 유지해서는 안 됩니다. 때로는 로그 롤링을 관찰하는 것이 응용 프로그램이 제대로 작동하는지 아니면 영원히 중단되는지를 알 수 있는 유일한 방법입니다.

8)외부 시스템 주의

이전 팁의 특별한 경우는 다음과 같습니다. 기간. 통합은 힘든 일이며 두 응용 프로그램(두 개의 서로 다른 공급 업체,환경,기술 스택 및 팀 생각)간의 문제를 진단하는 것은 특히 어렵습니다. 예를 들어,최근에,우리는 전체 메시지 내용을 기록 하는 것을 발견 했습니다.,아파치 웹 서비스에서 비누와 헤더를 포함 하 여 통합 및 시스템 테스트 하는 동안 매우 유용.

이는 큰 오버헤드이며 성능이 문제라면 항상 로깅을 사용하지 않도록 설정할 수 있습니다. 그러나 아무도 고칠 수없는 빠르고 깨진 응용 프로그램을 갖는 점은 무엇입니까? 외부 시스템과 통합 할 때 특별히 조심하고 그 비용을 지불 할 준비를하십시오. 만약 당신이 운이 좋다면,모든 통합이 에스크로 비에 의해 처리된다면,버스는 아마도 들어오는 모든 요청과 응답을 기록 할 수있는 가장 좋은 장소 일 것입니다. 예를 들어 노새의 로그 구성 요소를 참조하십시오.

때로는 외부 시스템과 교환되는 정보의 양이 모든 것을 기록하는 것을 용납 할 수 없게 만듭니다. 반면에 테스트 중 및 짧은 시간 동안 생산(예:잘못된 일이 발생하는 경우)동안 모든 것을 로그에 저장하고 성능 비용을 지불 할 준비가되어 있습니다. 이는 로깅 수준을 신중하게 사용하여 달성 할 수 있습니다. 다만 뒤에 오는 관용구에 보십시오:

Collection<Integer> requestIds = //...if(log.isDebugEnabled()) log.debug("Processing ids: {}", requestIds);else log.info("Processing ids size: {}", requestIds.size());

이 특정 로거가 디버그 메시지를 기록하도록 구성된 경우 전체 요청 수집 내용을 인쇄합니다. 그러나 정보 메시지를 인쇄하도록 구성된 경우 컬렉션 크기 만 출력됩니다. 왜 내가 잊어 버렸는지 궁금하다면 팁#2 로 돌아가십시오. 언급 할 가치가있는 한 가지는 이 경우 요청자 컬렉션이 무효가되어서는 안된다는 것입니다. 하지만 디버그가 활성화되면 널로 올바르게 기록되지만 로거가 정보로 구성된 경우 큰 지방 널 포인터 예외가 발생합니다. 끝#4 에 있는 부작용에 관하여 나의 학습을 기억하는가?

9)예외를 제대로 기록하십시오

우선,예외 로깅을 피하고 프레임 워크 또는 컨테이너(그것이 무엇이든)가 당신을 위해 그것을 할 수 있도록하십시오. 이 규칙에 예외가 하나 있습니다: 일부 원격 서비스(원격 세션 빈 등)에서 예외를 던지면예외를 직렬화할 수 있는 경우 클라이언트에서 예외를 모두 사용할 수 있는지 확인합니다. 그렇지 않으면 클라이언트가”참”오류 대신 일부 예외를 받게 됩니다.

예외 로깅은 로깅의 가장 중요한 역할 중 하나이지만 많은 프로그래머는 로깅을 예외를 처리하는 방법으로 취급하는 경향이 있습니다. 때로는 기본값(일반적으로 널,0 또는 빈 문자열)을 반환하고 아무 일도 일어나지 않은 척합니다. 다른 경우에는 먼저 예외를 기록한 다음 래핑하고 다시 던집니다:

log.error("IO exception", e);throw new MyCustomException(e);

이 구문은 거의 항상 동일한 스택 추적을 두 번 인쇄합니다. 그렇지 않으면 로그가 혼란 스러울 것입니다.

그러나 우리가 정말로 예외를 기록하고 싶다면? 어떤 이유로(우리는 아피스 및 문서를 읽지 않기 때문에?),내가 보는 로깅 명령문의 약 절반이 잘못되었습니다. 빠른 퀴즈,다음 로그 문 중 어느 것이 제대로 로그됩니까?

try { Integer x = null; ++x;} catch (Exception e) { log.error(e); //A log.error(e, e); //B log.error("" + e); //C log.error(e.toString()); //D log.error(e.getMessage()); //E log.error(null, e); //F log.error("", e); //G log.error("{}", e); //H log.error("{}", e.getMessage()); //I log.error("Error reading configuration file: " + e); //J log.error("Error reading configuration file: " + e.getMessage()); //K log.error("Error reading configuration file", e); //L}

놀랍게도 지 및 바람직하게는 엘만이 정확합니다! 다른 사람들은 스택 추적을 버리거나 부적절한 메시지를 인쇄합니다. 일반적으로 예외 메시지를 제공하지 않으며 스택 추적도 인쇄되지 않습니다. 첫 번째 인수는 항상 문자 메시지,기억 문제의 본질에 대해 뭔가를 작성합니다. 예외 메시지는 스택 추적 앞의 로그 문 뒤에 자동으로 인쇄되므로 포함하지 마십시오. 그러나 이렇게 하려면 예외 자체를 두 번째 인수로 전달해야 합니다.

10)로그 읽기 쉽고 구문 분석하기 쉬운

응용 프로그램 로그에 특히 관심이있는 두 그룹의 수신자가 있습니다:인간(동의하지 않을 수도 있지만 프로그래머는이 그룹에 속함)과 컴퓨터(일반적으로 시스템 관리자가 작성한 쉘 스크립트). 로그는 이 두 그룹에 모두 적합해야 합니다. 누군가가 당신의 응용 프로그램 로그를 뒤에서 바라 보는 경우(소스 위키 백과):

그럼 당신은 아마 내 팁을 따라하지 않은. 로그는 코드처럼 읽기 쉽고 이해하기 쉬워야 합니다.

반면에,응용 프로그램이 매 시간 0.5 기가바이트의 로그를 생성한다면,어떤 사람도 그래픽 텍스트 편집기도이를 완전히 읽을 수 없습니다. 이 곳은 오래된 학교 그렙,세드 및 아크가 편리합니다. 가능한 경우 로깅 메시지를 인간과 컴퓨터 모두에 의해 이해 될 수있는 방식으로 작성하십시오. 숫자의 서식을 피하고 정규 표현식으로 쉽게 인식 할 수있는 패턴 등을 사용하십시오. 가능하지 않은 경우 두 가지 형식으로 데이터를 인쇄합니다:

log.debug("Request TTL set to: {} ({})", new Date(ttl), ttl);// Request TTL set to: Wed Apr 28 20:14:12 CEST 2010 (1272478452437)final String duration = DurationFormatUtils.formatDurationWords(durationMillis, true, true);log.info("Importing took: {}ms ({})", durationMillis, duration);//Importing took: 123456789ms (1 day 10 hours 17 minutes 36 seconds)

사람들은”1 일 10 시간 17 분 36 초”텍스트를보고 기뻐할 것입니다 동안 컴퓨터는 시간 형식”1970 시대 이후 밀리”감사합니다. 시간 형식,좋은 도구를 살펴보십시오.이 웹 사이트는 귀하가 웹 사이트를 탐색하는 동안 귀하의 경험을 향상시키기 위해 쿠키를 사용합니다.이 쿠키들 중에서 필요에 따라 분류 된 쿠키는 웹 사이트의 기본적인 기능을 수행하는 데 필수적이므로 브라우저에 저장됩니다.또한이 웹 사이트의 사용 방식을 분석하고 이해하는 데 도움이되는 제 3 자 쿠키를 사용합니다.이 쿠키는 귀하의 동의하에 만 브라우저에 저장됩니다.이러한 쿠키를 거부 할 수도 있습니다. 공유하는 것을 잊지 마세요!

관련 기사 :
  • 로깅 안티 패턴
  • 모든 프로그래머가 알아야 할 것들
  • 소프트웨어 설계 법칙
  • 자바 모범 사례–벡터 대 배열 목록 대 해시 세트
  • 자바 모범 사례–멀티 스레딩 환경에서 날짜 형식
  • 자바 모범 사례–고성능 직렬화
  • 자바 모범 사례–문자열 성능 및 정확한 문자열 일치
관련 스니펫 :
  • 로거 핸들러에 대한 사용자 지정 포맷터 생성
  • 로거 핸들러에 대한 필터 설정
  • 로거 핸들러에 대한 포맷터 설정
  • 로깅 측면 예제
  • 로그 4 제이 메이븐 예제
  • 최대 절전 모드 로그백 구성–로그백 구성