Log4j 란?

Apache에서 만든 로깅용 자바기반의 오픈소스 라이브러리이다. 디버깅용이나 로그 적재에 주로사용한다.

Log for java라는 뜻을 줄여서 Log4j 라고 하며 Jakarta-project에서 Java를 위한 프로젝트 중 하나이다.

간단히 말해 로그를 출력하기 위한 라이브러리이다.

 

log4j 가 없어도 로그는 출력할 수 있지만 각 소스마다 sysout을 이용해 로그를 출력한다면 디버깅 로그가 필요 없을 경우에는 모두 추적하여 삭제하는 번거러움이 있고 sysout은 호출하면 할 수록 성능이 저하된다. 

(궁금한 경우 루프문을 작성하여 sysout을 계속 출력해 보면 알 수 있다.)

 


취약점

최근 log4j에 대한 취약점으로 큰 이슈가 발생하였다.  JNDI Injection 취약점을 이용하여 서버에서 악성 코드를 실행할 수 있다.  아래와 같은 위험이 있다고 한다.

  • 대상 컴퓨터(서버)의 모든 권한 취득 가능
  • 비밀번호 없이 내부망 접근 가능
  • 악성 프로그램(랜섬웨어 등) 실행 가능
  • 기업의 중요한 자료 삭제 등

 


취약점 조치

버전 업그레이드를 위해 공홈에서 사용중인 JDK 버전에 맞는 라이브러리를 다운로드 받는다. 

https://logging.apache.org/log4j/2.x/download.html#

 

Log4j – Download Apache Log4j 2

<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apa

logging.apache.org

파일을 다운로드 받은 후 압축파일의 내용을 보면 필요한 파일보다 더 많은 파일이 있다. 

지금 우리가 수행할 작업은 1.x 버전에서 2.x 버전으로 변경하는 작업이므로 해당 작업을 중점으로 설명하겠다. 

( log4j2-2.x.x 버전 업그레이드는 라이브러리만 교체하면 되는것으로 알고 있음)

 

 

 

<라이브러리 교체>

프로젝트의 상황에 따라 일부 라이브러리는 차이가 있을 수 있다. 

(예를 들면 remix / jdbc가 붙은 log4j 관련 라이브러리)

그러나 버전 업그레이드에 반드시 필요한 파일은 3개가 있으며 slf4j를 사용하는 경우 1개의 교체해야하는 파일이 있다.

log4j-core-2.x.x.jar log4j-api-2.x.x.jar log4j-web-2.x.x.jar log4j-slf4j-impl-2.x.x.jar
추가 Slf4j 사용시 추가
( 교체 : slf4j-log4j12-1.x.x.jar)

 

기존 프로젝트에 삭제되어야하는 라이브러리는 다음과 같다.

  • log4j-1.x.x.jar
  • slf4j-log4j12-1.x.x.jar  (slf4j를 사용하지 않는 경우에는 해당 파일이 없을 수 있으나 대부분의 프로젝트에는 있다.)

추가되어야 하는 라이브러리 

  • log4j-core-2.x.x.jar
  • log4j-api-2.x.x.jar
  • log4j-web-2.x.x.jar
  • log4j-slf4j-impl-2.x.x.jar  / log4j2-2.x.x용 바인딩 파일 (slf4j를 사용하지 않는 경우 제외)

slf4j를 사용하며 기존에 존재하는 라이브러리 중 유지해야하는 라이브러리

  • slf4j-api-1.x.x.jar    /  기존 소스에서 API를 사용하고 있으므로 삭제하지 않음

 

 

<설정 변경 : WEB.XML>

log4j2.xml 경로설정 - log4j-2.x 버전에서는 web.xml에서 경로나 파일명을 명시하지 않으면 기본값으로 classpath: 경로에서 log4j2-test.xml 또는 log4j2.xml 파일을 찾는다고 한다. (log4j 에서도 동일했던 것 같은데..) 

하지만 명확하게 하기 위해 별도의 설정을 하도록한다.

<!-- 대상파일 web.xml -->

<!-- AS-IS  : 변경 전-->
<context-param>
	<param-name>log4jConfigLocation</param-name>
    <param-value>classpath:/log4j.xml</param-value>
</context-param>

<listener>
	<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>

<!-- To-be : 변경 후-->
<context-param>
	<param-name>log4jConfiguration</param-name>
    <param-value>classpath:/log4j2.xml</param-value>
</context-param>
<listener>
	<listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class>
</listener>

web.xml에 해당 코드가 없는경우 추가하도록 한다.

경험으로는 spring 3.1 버전에서는 해당 코드가 없는 프로젝트가 있었으며 spring 3.2 이후부터는 해당 코드가 있었다.

 

 

 

<설정 변경 : LOG4J2.XML>

log4j2.xml 파일에 대한 작성법은 여기서 다루지 않도록 한다.
     관련링크 :   https://logging.apache.org/log4j/2.x/manual/configuration.html

 

Log4j – Configuring Log4j 2

Configuration Inserting log requests into the application code requires a fair amount of planning and effort. Observation shows that approximately 4 percent of code is dedicated to logging. Consequently, even moderately sized applications will have thousan

logging.apache.org

 

사용중인 설정파일의 내용은 아래와 같다. 

<!-- 신규작성 파일 : log4j2.xml -->
<?xml version="1.0" encoding="UTF-8"?>

<Configuration status="DEBUG">
	<Properties>
		<Property name="logNm">log_pattern_Layout</Property>
		<Property name="layoutPattern">[%d{ISO8601}][%5p] [%c{4}]: %m%n</Property>
	</Properties>
			
	<Appenders>
		<Console name="console" target="SYSTEM_OUT">
		    <PatternLayout pattern="${layoutPattern}" />
		</Console>
		<RollingRandomAccessFile
			name="rolling_log"
			fileName="./logs/file.log"
			filePattern="./logs/pjt/pjt-%d{yyyy-MM-dd_HH}_%i.log"
			immediateFlush="false"
			append="true"
			ignoreExceptions="false">
			<PatternLayout pattern="${layoutPattern}" />
			<Policies>
				<!-- 30MB 용량이 초과시 DefaultRolloverStrategy 정책만큼 넘버링 -->
				<SizeBasedTriggeringPolicy size="30 MB" />
				<!-- 일별 로그 파일 생성-->
				<TimeBasedTriggeringPolicy interval="1" modulate="true" />
			</Policies>
			<!-- 롤링 파일 1000개 까지 생성 -->
			<DefaultRolloverStrategy max="1000" />
		</RollingRandomAccessFile>
		
		<Async name="async_log" includeLocation="true">
			<AppenderRef ref="rolling_log" />
		</Async>

		
	</Appenders>

	

	<Loggers>
		<!-- 스프링 프레임워크에서 찍는건 level을 info로 설정 -->
		<logger name="org.springframework" level="info" additivity="false" >
			<AppenderRef ref="console" />
			<AppenderRef ref="async_log" />
		</logger>
		<Logger name="jdbc.connection" level="info" additivity="false">
			<AppenderRef ref="async_log" />
		</Logger>
		<Logger name="jdbc.audit" level="error" additivity="false">
			<AppenderRef ref="async_log" />
		</Logger>
		<Logger name="jdbc.resultset" level="error" additivity="false">
			<AppenderRef ref="async_log" />
		</Logger>
		<Logger name="jdbc.resultsettable" level="error" additivity="false">
			<AppenderRef ref="async_log" />
		</Logger>
		
		<Logger name="jdbc.sqltiming" level="error" additivity="false">
			<AppenderRef ref="async_log" />
		</Logger>
		<Logger name="com.ibatis" level="error" additivity="false">
			<AppenderRef ref="async_log" />
		</Logger>
		<Logger name="java.sql" level="error" additivity="false">
			<AppenderRef ref="async_log" />
		</Logger>
		<Logger name="com.kpmg.kr" level="error" additivity="false">
			<AppenderRef ref="async_log" />
		</Logger>
		

		<Root level="debug">
			<AppenderRef ref="async_log" />
			<AppenderRef ref="console" />
		</Root>
	</Loggers>

</Configuration>

 

 

<설정 변경 : JAVA CODE내 사용>

slf4j를 사용했다면 과거 1.X 버전을 사용할때랑 동일하므로 변경할 내용이 없으나. 사용하지 않는 다면 아래 예제를 참조하여 수정하도록 한다.( 보통은 slf4j를 상당수 사용하기 때문에 ....)

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

// logger name이 현재 클래스 경로인 것을 찾는다. 
private static Logger logger = LogManager.getLogger(this.getClass()); 

prvate static void write(String s){
	logger.info(s);
}

 

작업을 완료하고 콘솔(예를 들면 이클립스)에서 Was 기동시 붉은색 로그가 아닌 검정색 로그가 주르륵 올라간다면 적용이 완료되었다고 볼 수 있다. 

설정오류(설정에 오류는 있으나 컴파일 오류가 안나는 상태)의 경우 Was 기동시 붉은색 로그만 나타나게 된다면

즉, 스프링 관련 로그같은 별도의 검정색 텍스트가 보이지 않는다면 설정이 잘못되었다고 볼 수 있다. 

 

<오류 발생 Case>

모두 적용을 완료한 후 톰켓 기준 WAS 기동시 에러메세지가 나타나는 경우가 있다. 

 org.apache.tomcat.util.bcel.classfile.ClassFormatException: Invalid byte tag in constant pool: 19

해당 현상은 톰켓 버그로 이 문제가 해결된 버전은 Tomcat을 사용함으로써 해결이 가능하다.

  • Tomcat 9.0.x : 9.0.0.M18 이후
  • Tomcat 8.5.x : 8.5.12 이상 
  • Tomcat 8.0.x : 8.0.42 이상
  • Tomcat 7.0.x : 7.0.76 이상

해당내용 관련 StackOverflow

https://stackoverflow.com/questions/23541532/org-apache-tomcat-util-bcel-classfile-classformatexception-invalid-byte-tag-in

 

org.apache.tomcat.util.bcel.classfile.ClassFormatException: Invalid byte tag in constant pool: 15

I'm porting a webapp from Tomcat 7 to another server with Tomcat 7 but with Java 8. Tomcat starts successfully but in log catalina.out I get: org.apache.tomcat.util.bcel.classfile.ClassFormatExce...

stackoverflow.com

 

+ Recent posts