본문 바로가기
프로그래밍/스프링프레임워크

전자정부 표준프레임워크 - Portal Site 로깅(log4j 2) 설정하기

by pentode 2018. 4. 8.

테스트용 프로젝트의 설치는 "전자정부 표준프레임워크 - Portal Site(Oracle) 템플릿 프로젝트 설치" 를 참조 하세요.

 

로깅은 애플리케이션의 디버깅과 문제해결을 위한 필수적인 도구라고 생각됩니다. 물론 개발중의 디버깅에는 System.out.println이 더 효과적인 때도 있지만, 운영중인 애플리케이션의 문제점을 찾기위해서는 반드시 필요한 도구 입니다.

 

전자정부 표준프레임워크의 Portal Site 템플릿을 설치해서 이것 저것 테스트 해보고 있는 중인데, 이번에는 로깅 설정 부분을 보도록 하겠습니다. 전자정부 표준프레임워크 3.6  Portal Site 템플릿에는 log4j 2 버전과 SQL문장 로깅을 위한 log4jdbc가 들어 있습니다. 여기서 알아볼 설정 방법은 일반적인 스프링 프레임웍에서도 동일하게 적용될 수 있습니다.

 

 

1. 로그 레벨

 

Log4j 2는 org.apache.logging.log4j.Level 클래스에서 여덟가지로 로그 레벨을 정의하고 있습니다.

 

- OFF : 아무것도 로깅하지 않음을 뜻합니다.

- FATAL : 치명적인 에러

- ERROR : 에러

- WARN : 경고

- INFO : 정보

- DEBUG : 디버깅

- TRACE : 추적

- ALL : 모든 것을 로깅함을 뜻합니다.

 

OFF와 ALL은 정해진 의미가 있지만, 다른 여섯가지 레벨은 자신이 편한대로 정의해서 사용하면 됩니다. 자신이 로그를 보고자 하는것을 레벨을 이용해서 분류하면 됩니다. 이러한 로깅 프레임웍의 장점이 레벨별로 출력을 제어할 수 있다는 것이 되겠습니다.

 

로그 레벨의 적용예입니다. 이 사례는 개인적인 생각일뿐 절대적인게 아닙니다. 자신의 상황에 맞게 얼마든지 바꿀 수 있을 것입니다.

 

- ERROR : 작업을 실행 완료할 수 없을 때 로깅합니다.(예, 작업을 멈추는 Exception 이 발생했을 때)

- WARN : 애플리케이션의 외부적 요인에 의해 발생되는 프로그램이 종료되지 않는 문제점이 발생하였을때 로깅합니다.(사용자의 입력으로 인한 누락되거나 일치하는 않는 매개변수, 값이 맞지 않아 기본값이 사용되거나 하는 경우)

- INFO : 정상적이고 중요한 메세지를 로깅합니다. (로그인 성공, 로그인 실패, 로그아웃, 메일발송, SMS 발송 등)

- DEBUG : 정상적이고 중요하지 않은 메세지를 로깅합니다.(메소드의 시작, 종료 등)

- TRACE : 잘 사용하지 않습니다.(조회된 자료 전체 출력이나, 루프 추적 등)

 

로깅 포함 관계는 다음과 같습니다.

 

ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF

 

즉, DEBUG 로 레벨을 설정하면 DEBUG, INFO, WARN, ERROR, FATAL 이 로깅 됩니다. 개발시에 주로 이걸로 설정합니다. 운영시에는 로깅양이 많을 경우 성능에 문제가 될 수 있으므로, ERROR 로 설정을 해 둘 수 있는데 ERROR, FATAL이 로깅 됩니다.

 

 

2. 로거의 계층구조

 

다음은 전자정부 표준프레임워크 Portal  Site 템플릿을 설치시 기본 로그 설정 파일입니다. 설정내에 Appenders와 Loggers가 보입니다. Appenders 는 로그를 출력한 곳과 출력 포맷을 지정하는 곳입니다. 로그를 파일로 출력하거나, 콘솔로 출력하는 여러개의 Appender를 둘 수 있습니다. 기본 설정에서는 Console로 출력하는 Appender 가 하나 지정되어 있습니다.

 

Loggers에는 Root 로거와 이름(java.sql, egovframework 등) 지어진 로그들이 있습니다. 각각의 로거는 또한 AppenderRef 로 어떤 Appener를 사용할지 지정되어 있습니다. 로거에 붙여진 이름은 실제 Java 코드 내에서 로그를 남기기 위해  Logger객체를 생성할때 사용되어 집니다. 즉, 이름을로 로거를 분류할 수 있는 것입니다.

 

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
	<Appenders>
		<Console name="console" target="SYSTEM_OUT">
			<PatternLayout pattern="%d %5p [%c] %m%n" />
		</Console>
	</Appenders>
	<Loggers>
		<Logger name="java.sql" level="INFO" additivity="false">
			<AppenderRef ref="console" />
		</Logger>
		<Logger name="egovframework" level="DEBUG" additivity="false">
			<AppenderRef ref="console" />
		</Logger>
		<!-- log SQL with timing information, post execution -->
		<Logger name="jdbc.sqltiming" level="INFO" additivity="false">
			<AppenderRef ref="console" />
		</Logger>
		<Logger name="org.springframework" level="INFO" additivity="false">
			<AppenderRef ref="console" />
		</Logger>
		<Root level="ERROR">
			<AppenderRef ref="console" />
		</Root>
	</Loggers>
</Configuration>

 

만약, Java 코드 내에서 사용된 로거의 이름에 이 설정 파일에 없다면, Root 로거가 사용되어 집니다. 이름 붙여진 로그에서 로그 이름은 자바 패키지 이름처럼 마침표(.) 로 계층 지어 집니다.

 

이름이 "egovframework" 이면 "egovframework.let" 나 "egovframework.let.sym" 의 부모가 됩니다. 루트 로거는 모든 로거의 최상위 부모가 됩니다. 로그의 출력은 부모에게도 전달이 되는데 출력방향이 같다면 여러번 출력되게 됩니다. 이것을 막는것지 로거의 additivity="false" 입니다.

 

 

3. Java 코드 내에서 루트 로거를 얻는 방법입니다.

 

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
...
Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
//or
Logger logger = LogManager.getRootLogger();

 

명명된 로그를 얻기 위해서는 다음과 같이 로거 이름을 직접 사용하거나 클래스를 인자로 주어서 클래스의 완전한 클래스명(fully qualified name)이 로거 이름으로  사용되도록 할 수 있습니다.(예로 String클래스의 완전한 이름은 java.lang.String 입니다.)

 

Logger logger = LogManager.getLogger("egovframework"); // 로그명 직접 사용
//or
Logger logger = LogManager.getLogger(ManagerController.class); // 클래스 사용

 

 

위의 방법은 log4j 2 를 직접 사용하여 로거를 얻는 방법입니다. 전자정부표준프레임워크에서는 로깅 퍼사드로 SLF4J 를 사용하고 있습니다. slf4j에 대한 자세한 정보는 https://www.slf4j.org/ 에서 얻을 수 있습니다.

 

SLF4J 는 Java를 위한 심플한 로깅 퍼사드(Facade) 라는 이름을 가지고 있습니다. 이것은 서로 다른 로깅 프레임웍에 대한 공통 인터페이스를 제공하여 나중에 로깅 라이브러리를 교체하기 쉽게 하기 위해서 사용됩니다.

 

sfl4j를 사용해서 로거를 얻는 방법 입니다.

 

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld {
	public static void main(String[] args) {
		Logger logger = LoggerFactory.getLogger(HelloWorld.class);
		logger.info("Hello World");
	}
}

 

 

4. log4jdbc를 사용해서 데이터베이스 호출시 SQL이 콘솔에 출력되도록 설정하기

 

- globals.properties 파일에 oracle  드라이브 설정 부분을 다음과 같이 수정합니다.

 

jdbc드라이버는 net.sf.log4jdbc.DriverSpy 가 사용됩니다. 접속용 드라이버 URL은 jdbc:log4jdbc:oracle:thin:@127.0.0.1:1521:XE 가 사용됩니다.

 

#oracle-spy
Globals.DriverClassName=net.sf.log4jdbc.DriverSpy
Globals.Url=jdbc:log4jdbc:oracle:thin:@127.0.0.1:1521:XE

#oracle
#Globals.DriverClassName=oracle.jdbc.driver.OracleDriver
#Globals.Url=jdbc:oracle:thin:@127.0.0.1:1521:XE

 

이제 콘솔로  SQL 문장이 출력되는것을 알 수 있습니다. log4j2.xml 에 root 로그의 level이 현재 ERROR 인데 이것을 DEBUG로 바꾸시 실행을 해보면 더욱 많은 로그가 쏟아지는것을 볼 수 있습니다. log4jdbc에는 "jdbc.resultset", "jdbc.sqlonly", "jdbc.audit", "jdbc.sqltiming" 등의 로그를 내고 있습니다. 기본 설정에서는 "jdbc.sqltiming" 이름의 로그만 나온 것입니다. 위에서 설명한 로그 레벨 포함 관계와 로거명의 계층구조에 대한 내용을 바탕으로 각자 원하는 로그가 출력되도록 설정해 보시기 바랍니다.

 

로그 출력 결과

 

 

5. 로그 포맷 문자 입니다.

 

- %p : DEBUG, INFO, WARN, ERROR, FATAL 등의 로깅 이벤트의 레벨을 출력합니다.

- %m : 로로그문에서 전달된 메시지를 출력합니다.

- %d : 로깅 이벤트의 일자와 시간을 출력합니다. 포맷은 %d{HH:mm:ss, SSS}, %d{yyyy MMM dd HH:mm:ss, SSS}같은 형태로 사용하며 SimpleDateFormat에 따른 포맷팅을 하면 된다

- %t : 로깅 이벤트가 발생한 스레드명을 출력합니다.

- %% : %를 출력하기 위해 사용하는 패턴입니다.

- %n : 플랫폼 종속적인 개행문자를 출력합니다. \r\n 또는 \n 일것이다.

- %c : 로깅 이벤트를 발생시키기 위해 선택한 로거의 이름을 출력합니다.

- %C : 로깅 이벤트가 발생한 클래스의 풀네임명을 출력합니다.

- %F : 로깅 이벤트가 발생한 클래스의 파일명을 출력합니다.

- %l : 로로깅 이벤트가 발생한 클래스의 풀네임명.메서드명(파일명:라인번호)를 출력합니다.

- %L : 로깅 이벤트가 발생한 라인 번호를 출력합니다.

- %M : 로깅 이벤트가 발생한 메서드명을 출력합니다.

- %r : 로그 처리시간 (milliseconds)을 출력합니다.

반응형