Logging (23.09.01)

·2023년 9월 1일
0

Spring

목록 보기
30/36
post-thumbnail

🌷 Logging

🌼 로그 (log)

  • 사전적 의미
    통나무, 향해 일지, 배의 속력이나 항주한 거리를 계측하는 장치의 총칭

  • 실질적 의미
    기록을 남기는 것

🌱 로그 사용 이유

  • 애플리케이션 운영 시 로그의 효율적인 관리가 가능함 (콘솔 또는 특정 파일)
  • 콘솔 로그를 위해 System.out.print를 사용하는 건 성능 저하를 야기함

👍 장점

  • 프로그램의 문제 파악에 용이함
  • 빠르고 효율적인 디버깅이 가능함
  • 수행 내역 파악이 쉬움
  • 로그 이력을 파일, DB 등으로 남길 수 있음

👎 단점

  • 로그에 대한 디바이스(파일) 입출력으로 인해 런타임 오버헤드가 발생함
  • 로깅을 위한 추가 코드로 인해 전체 코드 사이즈가 증가함
  • 심하게 생성되는 로그는 혼란을 야기하거나 어플리케이션 성능에 영향을 미침
  • 개발 중간에 로깅 코드를 추가하기 어려움

🌱 로그 레벨

(가장 경미) DEBUG < INFO < WARN < ERROR < FATAL (가장 심각)


🌼 Logging Framework

log4j / logback / slf4j

  • 스프링 프로젝트에는 기본적으로 log4j 라이브러리가 추가되어 있고,
    log4j와 관련된 설정을 담는 log4j.xml 파일을 서버 구동과 동시에 로딩하게 되어 있음
  • 하지만 log4j를 이용하여 많은 양의 로그를 출력하는 경우 성능 저하가 심해
    최근에는 logback 이라는 라이브러리를 사용함 (10배 빠름)
  • 스프링 프로젝트에서 기존 사용하던 log4j를 새로운 logback로 변경을 해야 하는데,
    이를 일일이 변경하기에는 비효율적이므로 log4j <-> logback 서로 변환을 해줄 수 있는 slf4j 라이브러리가 존재
    -> STS 사용 시 pom.xml에 자동으로 slf4h 라이브러리가 추가되어 있음
    (pom.xml에 <!—Logging 부분)

자동으로 slf4h가 추가되어 있다니...!
진짜로 확인해 보자!

🔎 pom.xml

...
		<!-- Log4j (Logging) -->
		<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-core</artifactId>
			<version>2.17.1</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${org.slf4j-version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
...

이처럼 pom.xml에 log4j 관련 라이브러리가 추가되어 있는 것을 확인할 수 있다.

🌱 slf4j

(Simple logging Facade for Java)

  • Facade(추상체) 역할
  • Logging framework 간 호환성을 보장하는 역할
    (log4j <-> logback)

🌼 log4j.xml 구조

(로그 관련 설정 파일)

🌱 Appender

전달된 로그를 어디에 출력할지를 결정함
(콘솔 출력, 파일 기록, DB 저장)

💭 Appender의 종류

  • ConsoleAppender : 로그를 콘솔에 출력하기 위한 Appender

  • JDBCAppender : 로그를 RDB에 출력하기 위한 Appender

  • FileAppender : 로그를 파일에 출력하기 위한 Appender
    -> 단, 지정한 파일에 로그가 계속 남기때문에 크기가 지나치게 커질 수 있고 지속적인 로그 관리가 불가능해짐

  • RollingFileAppender : FileAppender를 보완한 개념
    -> 일정한 조건 후에 기존 파일을 백업 파일로 바꾸고 다시 처음부터 로깅을 시작함
    (대표적 예시 : DailyRollingFileAppender)

🌱 Logger/root

출력할 메세지를 Appender에 전달
(로그 주체)

💡 name 속성

로그 주체, 패키지 작성

💡 additivity 속성

로그가 상위로 전달할지 여부 (기본값 true)

💡 appender-ref 자식 태그

ref 속성값으로 appender 태그 name 값을 지정

💡 level 자식 태그

로그 레벨을 설정함
-> 설정된 값 이상의 priority일 경우, 로깅 출력


이를 직접 확인해 보기 위해 현재 내 프로젝트 내 log4j.xml을 살펴보자.

log4j.xml는 사진 속 위치에 있다.
log4j의 DTD 파일 경로가 변경되어 2번째 줄 코드를 변경해 보았다.

log4j.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM 
"http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

	<!-- Appenders -->
	<appender name="console" class="org.apache.log4j.ConsoleAppender">
		<param name="Target" value="System.out" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%p] %m%n" />
		</layout>
	</appender>

	<!-- //날짜별 로그 파일 생성 하기 -->
	<appender name="dailyRollingFile" class="org.apache.log4j.DailyRollingFileAppender">
		<param name="File" value="/logs/runtime.log" />
		<param name="Append" value="true" />
		<param name="encoding" value="UTF-8" />
		<param name="DatePattern" value="'.'yyyy-MM-dd" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%p] %m%n" />
		</layout>
	</appender>
	
	<!-- log level
	trace -> debug -> info -> warn -> error -> fatal
	 -->
	
	<!-- Application Loggers -->
	<logger name="edu.kh.project">
		<level value="debug" />
		<appender-ref ref="dailyRollingFile" />
	</logger>
	
	<!-- 3rdparty Loggers -->
	<logger name="org.springframework.core">
		<level value="info" />
	</logger>
	
	<logger name="org.springframework.beans">
		<level value="info" />
	</logger>
	
	<logger name="org.springframework.context">
		<level value="info" />
	</logger>

	<logger name="org.springframework.web">
		<level value="info" />
	</logger>

	<!-- Root Logger -->
	<root>
		<priority value="debug" />
		<appender-ref ref="console" />
	</root>
	
</log4j:configuration>

🌼 Layout

로그를 어떤 형식으로 출력할지 결정

🌱 Layout의 종류

  • PatternLayout (가장 디버깅에 적합)
  • DateLayout
  • HTMLLayout
  • SimpleLayout
  • XMLLayout

🌱 PatternLayout 패턴 지정 방법

💡 %p

debug, info, warn, error, fatal 등의 priority 출력

💡 %m

로그 내용 출력

💡 %d

로깅 이벤트가 발생한 시간 출력
-> 포맷을 %d{HH:mm:ss}, %d{yyyy-MM-dd HH:mm:ss} 같은 형태로 사용하면 SimpleDateFormat에 따른 포맷팅이 진행됨

💡 %t

로그 이벤트가 발생된 스레드의 이름 출력

💡 %%

% 표시 출력

💡 %n

플랫폼 종속적인 개행 문자 출력
(rn 또는 n 출력)

💡 %c

카테고리 표시
(예시 : 카테고리가 a.b.c 처럼 되어있다면 %c{2}는 b.c가 출력됨)

💡 %C

클래스명 표시
(예시 : 클래스 구조가 org.apache.xyz.SomeClass처럼 되어 있다면
%C{2}는 xyz.SomeClass가 출력됨)

💡 %F

로깅이 발생한 프로그램 파일명 출력

💡 %l

로깅이 발생한 caller의 정보 출력

💡 %L

로깅이 발생한 caller의 라인 수 출력

💡 %M

로깅이 발생한 method 이름 출력

💡 %r

어플리케이션 시작 이후부터 로깅이 발생한 시점의 시간(milliseconds) 출력

💡 %x

로깅이 발생한 thread와 관련된 NDC(nested diagnostic context) 출력


이제 로그를 한번 출력해 보자.
login() 메소드가 실행될 때 로그를 출력하기 위해 기존에 만들어 놓은 MemberServiceImpl 클래스로 가서 코드를 일부 추가해 볼 것이다.

👀 코드로 살펴보기

🌱 MemberServiceImpl.java

package edu.kh.project.member.model.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import edu.kh.project.member.model.dao.MemberDAO;
import edu.kh.project.member.model.dto.Member;

@Service
public class MemberServiceImpl implements MemberService {
	
	// org.slf4j.Logger : 로그를 작성할 수 있는 객체
	// org.slf4j.LoggerFactory
	private Logger logger = LoggerFactory.getLogger(MemberServiceImpl.class);

	@Autowired
	private MemberDAO dao;

	@Autowired // bean으로 등록된 객체 중 타입이 일치하는 객체를 DI (의존성 주입)
	private BCryptPasswordEncoder bcrypt;
	
	@Override
	public Member login(Member inputMember) {

		// 로그 출력
		logger.info("MemberService.login() 실행"); // 정보 출력
		logger.debug("memberEmail : " + inputMember.getMemberEmail());
		logger.warn("이건 경고 용도");
		logger.error("이건 오류 발생 시");

		Member loginMember = dao.login(inputMember);
		
		if(loginMember != null) { // 아이디가 일치하는 회원이 조회된 경우
			
			// 입력한 pw, 암호화된 pw 같은지 확인
			
			// 같을 경우
			if(bcrypt.matches(inputMember.getMemberPw(),
							  loginMember.getMemberPw())) {
				
				// 비밀번호를 유지하지 않기 위해서 로그인 정보에서 제거
				loginMember.setMemberPw(null);
				
			} else { // 다를 경우
				loginMember = null; // 로그인 실패처럼 만듦
			}
		}
		
		return loginMember;
	}
...

이렇게 작성한 뒤 서버를 실행해 보았다.
이렇게 작성된 로그는 어디서 확인할 수 있을까?

해당 위치에 logs 폴더가 생겼다.

폴더 안으로 들어가 보니 runtime이라는 파일이 생긴 것을 볼 수 있다.
이 파일을 열어 보면

이렇게 로그들이 기록되어 있는 것을 볼 수 있다. 😉

profile
풀스택 개발자 기록집 📁

0개의 댓글