[JAVA] System.out.println()이 아닌 LOG로 출력해야 하는 이유

Kevin·2024년 4월 21일
0

JAVA

목록 보기
10/16
post-thumbnail

🧐 서론

나는 지금까지 어떠한 프로그래밍의 로그를 남길 때 늘 System 표준 입출력 라이브러리를 사용했었다. 여러 교육들에서 Log 라이브러리를 사용해서 로그를 남겨야 한다는 이야기들은 자주 들었었지만, 내 스스로 필요성을 느끼지 못했다.

그러나 개발을 진행하면서 특정 로직에서 실행 시간이 느려지는 것을 경험하면서, 로그를 남길 때 System 표준 입출력 라이브러리보다 Log 라이브러리등을 사용해야 한다는 생각이 들었는데 함께 왜 Log 라이브러리를 사용 해야 하는지에 대해서 알아보자.


🫠 Log 라이브러리를 사용해 Log를 작성한 나의 코드 설명


import org.apache.log4j.Logger;

public class TargetJob extends QuartzJobBean {
			
	private Logger log = Logger.getLogger(TargetJob.class);
	
	 protected void executeInternal(JobExecutionContext context)
			 throws JobExecutionException {
		 
		 try{			 
			 LocalDateTime startTime = checkStartTime();
			 		 
			 List<HashMap<String, Object>> targets = systemDAO.getTarget();
			
			 if (existsTarget(targets.size())) {
				 doWork(targets, systemDAO);	            
			 }  
			 
			 checkEndTime(startTime);

		 } catch(Exception e){
			 e.printStackTrace();
		 }
	 }
	 
	 /* 스케쥴러 실행 시간을 기록 */
	 private LocalDateTime checkStartTime() {
		
		 log.info("TargetJob이 돌아가는 중입니다. : " + LocalDateTime.now());
		 
		 return LocalDateTime.now();
		 
	 }
	 
	 /* 스케쥴러 종료 시간을 기록 */
	 private void checkEndTime(LocalDateTime startTime) {
		 
		 LocalDateTime endTime = LocalDateTime.now();
		 
		 log.info("TargetJob이 모두 수행되었습니다. : " + endTime);
		 
		 log.info("총 소모 시간입니다. : " + Duration.between(startTime, endTime));
		 
	 }
	 	 	 
	 /* DB에 예약된 Target이 있다면 true 반환 */
	 private boolean existsTarget(int size) {
		 if (size > 0) {
			 return true;
		 }		 
		 return false;
	 }
	 
	 
	 /* DB에 예약된 Target이 있다면 true 반환 */
	 private void doWork(List<HashMap<String, Object>> targets, SystemDAO systemDAO) {
		 	

         for (HashMap<String, Object> target : targets) {
             executor.execute(() -> {
             	TargetService targetService = new TargetService();
             	targetService.getInfoOID(target, systemDAO);
             });
         }

         // 모든 작업이 완료될 때까지 대기
         executor.shutdown();
         while (!executor.isTerminated()) {}
	 }

}

위 코드는 DB로부터 특정 데이터를 조회하고, 해당 데이터의 존재 여부에 따라 분기 처리하는 멀티 쓰레드 코드이다.

위 코드에서 분기를 어떻게 타고 있는지와 코드의 실행 시간등을 확인 하기 위해 로그를 작성하였다.

기존에는 System 표준 입출력 라이브러리를 사용 하였는데, 지금은 Log 라이브러리로 로그들을 작성하였다.

이 때의 장점은 무엇인지에 대해서 같이 이야기 해보자.


🤭 Log 라이브러리를 사용해야 하는 이유

1. System.out.println()은 휘발된다.

System.out.println()은 로그가 표준 출력으로 출력된다.

이는 파일로 저장되지 않고, 단순히 출력으로 휘발된다는 뜻이다.

로그는 에러가 발생한 상황을 기록하고, 추후 확인하여 문제를 진단하고 고치기 위해 사용되는데 이러한 부분에서 사용될 수 없다.


2. 로그 출력 레벨을 사용할 수 없다.

로컬에서 개발할 때는 디버깅을 위해 아주 상세한 정보가 출력되어 확인할 수 있어야 한다.

그러나 실 프로덕션에서는 로그는 에러 / 장애가 발생할 때 문제를 진단할 수 있는 정보만을 남겨두어야 한다.

개발시에만 사용되는 정보와 문제 상황에 대한 정보가 함께 로깅된다면 문제 해결을 위한 정작 중요한 정보를 얻기 힘들 뿐더러, 민감한 정보를 로그로 남길 수도 있기 떄문이다.

이를 위해 LOG 라이브러리에서는 TRACE, DEBUG, INFO 등의 로그 출력 레벨이 사용된다.

그러나 System.out.println()(이하 print라 칭함)은 이러한 단계가 존재하지 않는다.


3. 성능 저하의 원인이 될 수 있다.

print는 newLine()이라는 메서드를 호춯하는데, newLine()에는 synchronized 키워드가 붙어 있다.

해당 키워드를 통해서 newLine() 메서드는 임계영역이 된다. 이로써 멀티 쓰레드 환경에서 A 쓰레드가 newLine() 메서드를 실행하면 다른 메서드는 A 쓰레드가 임계 영역을 풀어줄 때까지 newLine() 메서드를 실행하지 못하고 기다리게 되어 오버헤드가 발생하게 된다.

이러한 이유들로 print를 절대 사용하면 안된다.

한 번 요청 시 5000명의 사용자를 요청하고, 처리 과정에서 응답시간이 20초 걸리는 사이트가 있는데, 원인을 알아보니 5000명의 정보를 다 System.out.println()으로 처리하고있던 것이다. 이는 System.out.println()을 줄임으로써 응답시간이 6초까지 줄었다. - 이상민, 자바 성능 튜닝이야기, 인사이트, 2013


🤔 Log 라이브러리의 출력 단계 설정 방법

프로젝트 폴더/src/main/resource 경로 아래에 log4.xml이 존재할 것이다.

해당 xml에서 root Logger의 레벨을 원하는 출력 레벨로 설정해주면 된다.


레퍼런스

로깅을 System.out.println() 로 하면 안되는 이유텍스트

profile
Hello, World! \n

0개의 댓글

관련 채용 정보