[SpringBoot] log4j, tibero 연동(로컬,빌드,서버 배포)
본문은 egovframework 4.1 (springboot 2.7.0), tibero6 으로 구성되어 있는 서비스에 log4j를 적용한 내용을 기술한 글입니다.
블로그로 글을 남기게 된 이유는 ... log4j가 기본적으로 제공하는 dbDriver에 tibero가 없기 때문이다!
구글링을 해보니 -Dlog4jdbc.drivers=com.tmax.tibero.jdbc.TbDriver (vm argument)를 작성하고 ... log4j.log4j2.properties를 만들고 등등의 방법이 많았다.
하지만 필자는 파일을 새롭게 만들고 싶지 않고, log4j2를 추가하기도 싫어서 새로운 방법을 긴 시간의 삽질을 하며 성공했다 ㅎㅎ
환경구성
JDK: 1.8
Build Tool : mavne
DataBase: tibero6
JDBC 위치 설정
아마 이 글을 읽는 분들은 아래와 같이 tibero jdbc 파일을 설정했을 것이다.
<dependency>
<groupId>com.tmax.tibero</groupId>
<artifactId>tibero-jdbc</artifactId>
<version>3.0</version>
<groupId>jdbc.tibero</groupId>
<artifactId>tibero</artifactId>
<version>6</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/tibero-6.jar</systemPath>
</dependency>
하지만 로컬에서는 JDBC 커넥션도 잘 맺어지고, 수행도 잘 되는데 우리는 로컬에서 개발하고 끝이 아닌 서버에 배포도 해야하고, log4j도 적용을 해야한다.
필자가 가지고 있는 jdbc는 tibero-6.jar 이름으로 되어있다.
설정해야 하는 jdbc 위치가 이름에 따라 달라짐으로 필자의 jdbc 이름에 맞춰도 좋고, 환경에 따라 수정하여 사용하길 바란다.
경로 생성
프로젝트에 아래 경로를 생성해준다.
src/main/resources/lib/com/tmax/tibero/6/tibero-6.jar
생성한 후 jdbc를 위치해준다.
tibero-jdbc의 내부를 보게되면 Driver가 com/tmax/tibero/jdbc/TbDriver.class에 위치해 있다.
해당 구조를 참조하여 경로를 생성해준다.
의존성 및 repository 추가
로컬 환경과 서버 환경에서 동일하게 빌드를 진행하려면 아래와 같은 설정을 해주어야한다.
기존 방식과 동일하게 dependency에 scope, systempath 설정을 진행해도 되지만 이와 같은 방식은 로컬에서 eclipse나 intelij 같은 IDE를 사용할때만 해당 JDBC를 참조할 수 있다.
<repositories>
<repository>
<id>local-repo</id>
<name>local-repository</name>
<url>file://${project.basedir}/src/main/resources/lib</url>
</repository>
</repositories>
.
.
.
<dependencies>
<dependency>
<groupId>com.tmax</groupId>
<artifactId>tibero</artifactId>
<version>6</version>
</dependency>
</dependencies>
.
.
.
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
</plugins>
</build>
설정을 보면 ${project.basedir} 환경 변수에서 프로젝트 루트 경로를 읽어준 뒤에 lib 경로를 설정한것을 확인할 수 있다.
jenkins나 gitlab 같은 CI/CD를 이용해 배포를 진행하는 시스템의 경우 형상관리(예를 들어 Git)에서 빌드툴을 이용해 빌드를 진행하기 때문에 pom.xml에서 프로젝트 루트 경로의 lib 폴더를 바라볼 수 있도록 하는 설정이 매우 중요하다.
Data Source Configuration
tibero db를 이용하기 위해 tibero db가 설치되어 있는 URL 주소와 username, password 정보를 기입한다.
(필자는 properties 확장자로 application.properties에 설정함)
#tibero
Globals.tibero.DriverClassName=net.sf.log4jdbc.DriverSpy
Globals.tibero.Url=jdbc:log4jdbc:tibero:thin:@your_tibero_ip:8629:tibero
Globals.tibero.UserName=your-userName
Globals.tibero.Password=your_password
여기서 주의할 점!!!
log4j는 google, bgee 등 maven에서 배포되고 있는 종류가 있다...!
종류에 따라서 DriverClassName 경로가 다를수도 있으니 유의해야한다.
필자는 google log4j이다. 아래 그림을 보면 알겠지만 DriverClassName을 명확하게 확인하고 기입해주어야 class not found exception을 만나지 않는다!
해당하는 log4j 라이브러리를 직접 압축을 풀고 경로를 확인해보자!
net.sf.log4jdbc.sql.jdbcapi.DriverSpy 일수도 있다.
logback-spring.xml
Spring에서 서버가 실행될 때 기본으로 읽어들이는 설정 파일이다.
egovframework 4.1을 사용중이신 분들이라면 logback-spring.xml이 아니라 logback.xml 이라는 이름으로 되어 있을텐데, https://github.com/eGovFramework/egovframe-template-simple-backend
GitHub - eGovFramework/egovframe-template-simple-backend
Contribute to eGovFramework/egovframe-template-simple-backend development by creating an account on GitHub.
github.com
최신 전자정부 프레임워크를 보면, 파일 이름이 변경된걸 볼 수 있다.
예측하건데, logback.xml으로 이름이 되어있으면 springProfile을 사용하지 못해서 (설정 파일이 너무 빨리 로드됨으로 springProfile이 로드되기 전에 로드됨) 인거 같다...
아래 설정은 필자의 설정임으로 log4j 문법에 맞게 사용하면 되겠다.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- application-${activ}.properties 변수를 해당 xml에 할당-->
<springProperty scope="context" name="LOG_LEVEL" source="logging.level.root" /> <!-- 로그 레벨 -->
<springProperty scope="context" name="LOG_LEVEL_QUERY" source="logging.level.query" /> <!-- 로그 레밸(쿼리) -->
<springProperty scope="context" name="LOG_FILEPATH" source="logging.filepath" /> <!-- 로그 저장 경로 -->
<springProperty scope="context" name="LOG_FILENAME_EXTENSION" source="logging.filename.extension" /> <!-- 로그 저장 확장자 -->
<springProperty scope="context" name="LOG_APP_FILE_NAME" source="logging.app.filename" /> <!-- 로그 파일명 -->
<springProperty scope="context" name="LOG_APP_MAX_HISTORY" source="logging.app.max-history" /> <!-- 로그 보관 기간 -->
<springProperty scope="context" name="LOG_APP_MAX_FILE_SIZE" source="logging.app.max-file-size" /> <!-- 로그 파일 최대 용량 -->
<springProperty scope="context" name="LOG_APP_FILE_LOGPATTERN" source="logging.app.file.logpattern" /> <!-- 로그 기록 형식 -->
<springProperty scope="context" name="LOG_DB_FILE_NAME" source="logging.db.filename" /> <!-- 로그 파일명(쿼리) -->
<springProperty scope="context" name="LOG_DB_MAX_HISTORY" source="logging.db.max-history" /> <!-- 로그 보관 기간(쿼리) -->
<springProperty scope="context" name="LOG_DB_MAX_FILE_SIZE" source="logging.db.max-file-size" /> <!-- 로그 파일 최대 용량(쿼리) -->
<springProperty scope="context" name="LOG_DB_FILE_LOGPATTERN" source="logging.db.file.logpattern" /> <!-- 로그 기록 형식(쿼리) -->
<springProperty scope="context" name="LOG_EXCEPTION_FILE_NAME" source="logging.exception.filename" /> <!-- 로그 파일명(쿼리) -->
<springProperty scope="context" name="LOG_EXCEPTION_MAX_HISTORY" source="logging.exception.max-history" /> <!-- 로그 보관 기간(쿼리) -->
<springProperty scope="context" name="LOG_EXCEPTION_MAX_FILE_SIZE" source="logging.exception.max-file-size" /> <!-- 로그 파일 최대 용량(쿼리) -->
<springProperty scope="context" name="LOG_EXCEPTION_FILE_LOGPATTERN" source="logging.exception.file.logpattern" /> <!-- 로그 기록 형식(쿼리) -->
<!-- ConsoleAppender Start -->
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_APP_FILE_LOGPATTERN}</pattern>
</encoder>
</appender>
<!-- ConsoleAppender End -->
<!-- FileAppender Start -->
<!-- APPLICATION Appender-->
<appender name="APPLICATION" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILEPATH}/${LOG_APP_FILE_NAME}.${LOG_FILENAME_EXTENSION}</file>
<encoder>
<pattern>${LOG_APP_FILE_LOGPATTERN}</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILEPATH}/${LOG_APP_FILE_NAME}.%d{yyyy-MM-dd}.%i.${LOG_FILENAME_EXTENSION}</fileNamePattern>
<maxFileSize>${LOG_APP_MAX_FILE_SIZE}</maxFileSize>
<maxHistory>${LOG_APP_MAX_HISTORY}</maxHistory>
</rollingPolicy>
</appender>
<!--QUERY Appender-->
<appender name="QUERY" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILEPATH}/${LOG_DB_FILE_NAME}.${LOG_FILENAME_EXTENSION}</file>
<encoder>
<pattern>${LOG_DB_FILE_LOGPATTERN}</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILEPATH}/${LOG_DB_FILE_NAME}.%d{yyyy-MM-dd}.%i.${LOG_FILENAME_EXTENSION}</fileNamePattern>
<maxFileSize>${LOG_DB_MAX_FILE_SIZE}</maxFileSize>
<maxHistory>${LOG_DB_MAX_HISTORY}</maxHistory>
</rollingPolicy>
</appender>
<!--QUERY Appender-->
<appender name="EXCEPTION" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILEPATH}/${LOG_EXCEPTION_FILE_NAME}.${LOG_FILENAME_EXTENSION}</file>
<encoder>
<pattern>${LOG_EXCEPTION_FILE_LOGPATTERN}</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILEPATH}/${LOG_EXCEPTION_FILE_NAME}.%d{yyyy-MM-dd}.%i.${LOG_FILENAME_EXTENSION}</fileNamePattern>
<maxFileSize>${LOG_EXCEPTION_MAX_FILE_SIZE}</maxFileSize>
<maxHistory>${LOG_EXCEPTION_MAX_HISTORY}</maxHistory>
</rollingPolicy>
</appender>
<!-- FileAppender End -->
<!-- logger start-->
<logger name="jdbc.audit" level="OFF"/>
<logger name="jdbc.sqlonly" level="OFF"/>
<logger name="jdbc.resultset" level="OFF"/>
<logger name="jdbc.connection" level="OFF"/>
<logger name="log4jdbc.debug" level="OFF"/>
<logger name="egovframework" level="${LOG_LEVEL}" />
<logger name="org.egovframe" level="${LOG_LEVEL}" />
<logger name="org.springframework" level="${LOG_LEVEL}" />
<logger name="jdbc.sqltiming" level="${LOG_LEVEL_QUERY}" additivity="false">
<springProfile name="dev | prod">
<appender-ref ref="QUERY"/>
</springProfile>
<springProfile name="local">
<appender-ref ref="STDOUT" />
</springProfile>
</logger>
<springProfile name="dev | prod">
<logger name="egovframework.com.exception.GlobalExceptionHandler">
<appender-ref ref="EXCEPTION"/>
</logger>
<logger name="egovframework.com.cmm.filter.ExceptionHandlerFilter">
<appender-ref ref="EXCEPTION"/>
</logger>
</springProfile>
<!-- logger End-->
<springProfile name="local">
<root level="${LOG_LEVEL}">
<appender-ref ref="STDOUT" />
</root>
</springProfile>
<springProfile name="dev | prod">
<root level="${LOG_LEVEL}">
<appender-ref ref="APPLICATION" />
</root>
</springProfile>
</configuration>
application.properties
properties를 나눈 방식에 따라 application-${your-active-profile}.properties에 작성하면 된다.
.
.
.
#tibero
Globals.tibero.DriverClassName=net.sf.log4jdbc.DriverSpy
Globals.tibero.Url=jdbc:log4jdbc:tibero:thin:@your_tibero_ip:8629:tibero
Globals.tibero.UserName=your-userName
Globals.tibero.Password=your_password
.
.
.
# LOGBACK 설정
logging.level.root=DEBUG
logging.level.query=DEBUG
logging.filepath=your_log_dir
logging.filename.extension=log
# LOGBACK APPLICATION 설정
logging.app.filename=app
logging.app.max-history=30
logging.app.max-file-size=100MB
logging.app.file.logpattern=[yourProjectName]%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
# LOGBACK DB-QUERY 설정
logging.db.filename=query
logging.db.max-history=30
logging.db.max-file-size=100MB
logging.db.file.logpattern=[yourProjectName]%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
# LOGBACK EXCEPTION 설정
logging.exception.filename=exception
logging.exception.max-history=30
logging.exception.max-file-size=100MB
logging.exception.file.logpattern=[yourProjectName]%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
필자는 application, query, exception으로 나누어 file logging만 구현했기 때문에 자세한 사용법은 아래 링크를 참조하길 바란다.
https://logback.qos.ch/manual/index.html
Logback Manual
The logback manual The complete logback manual documents the latest version of logback framework. In over 150 pages and dozens of concrete examples, it covers both basic and advanced logback features, including: the overall logback architecture discussion
logback.qos.ch
https://www.baeldung.com/spring-boot-logging
빌드, 실행, 배포
서버에서 빌드하여 배포하는 방식이라면 상관 없겠지만, IDE에서 빌드하고 jar나 war 파일을 업로드 하는 방식으로 배포를 진행하고 있다면 IDE에서 빌드를 진행한다면 꼭
mvn dependency:purge-local-repository
명령어를 날리거나,
dependency reload 버튼을 눌러 의존성을 참조시킨 후 빌드하기를 바란다.
로컬에서 구성하는 독자 분들은 실행하여 결과를 확인하면 된다.!
배포후 log가 있는 경로에 가면 3개의 파일이 생성된 것을 볼수 있다.
로그 파일 뒤에 붙은 날짜는 설정해 놓은 pattern으로 로그 정책에 따라 백업되는 것인데... 관련 설정은 위 logback 사이트를 참조하길 바란다.!!