> For the complete documentation index, see [llms.txt](https://docs.sysout.co.kr/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.sysout.co.kr/web/back-end/what-is-spring-framework/etc/logging.md).

# Logging

## Logging이란

`Logging`이란 기록(`Log`)을 남기는 행위를 말한다.\
기록을 남기는 것은 분석 및 오류를 개선하는데 매우 중요한 영향을 미치기 때문에 체계적으로 관리할 필요가 있다.\
자바에서는 로그 관리를 위해 여러 프레임워크들이 존재한다.\
이 문서에서는 `log4j`를 이용하여 로그를 작성하는 방법에 대해서 다룬다.

* log4j
* logback

이외에도 다양한 프레임워크들이 존재하며, spring에서는 이들을 통합하여 `Slf4j` 모듈로 관리할 수 있도록 구성되어 있다.

![](https://4208234536-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M_TNZwLuHV9ipYLbvRq%2F-MdFkMTO5CTlf6UQDcdj%2F-MdFkkoLmhbwklhjA5fE%2Fimage.png?alt=media\&token=e7939116-1b9b-4b86-8055-bd763a348b50)

![](https://4208234536-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M_TNZwLuHV9ipYLbvRq%2F-MdFkMTO5CTlf6UQDcdj%2F-MdFkpC5C_luLHtY0pnh%2Fimage.png?alt=media\&token=f228a59f-2767-4104-8be3-ccdddefb2863)

## Logging Dependency

`Logging`을 사용하기 위한 Dependency는 다음과 같다.

* org.slf4j.slf4j-api
* org.slf4j.jcl-over-slf4j
* org.slf4j.slf4j-log4j12
* log4j.log4j

## pom.xml

```xml
<dependencies>
    <!-- 생략 -->
    <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>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>${log4j-version}</version>
        <exclusions>
            <exclusion>
                <groupId>javax.mail</groupId>
                <artifactId>mail</artifactId>
            </exclusion>
            <exclusion>
                <groupId>javax.jms</groupId>
                <artifactId>jms</artifactId>
            </exclusion>
            <exclusion>
                <groupId>com.sun.jdmk</groupId>
                <artifactId>jmxtools</artifactId>
            </exclusion>
            <exclusion>
                <groupId>com.sun.jmx</groupId>
                <artifactId>jmxri</artifactId>
            </exclusion>
        </exclusions>
        <scope>runtime</scope>
    </dependency>
    <!-- 생략 -->
</dependencies>
```

위의 설정에서 `<exclusions>`는 로깅을 배제할 모듈을 지정하여 불필요한 로깅을 방지한다.\
`spring mvc project`를 생성하면 기본적으로 설정이 되어 있기 때문에 버전을 제외하고는 수정할 내용이 없다.

`properties`에 버전 설정은 다음과 같이 되어있다.

```markup
<properties>
    <org.slf4j-version>1.7.30</org.slf4j-version>
    <log4j-version>1.2.15</log4j-version>
</properties>
```

## Logger 생성

`Logger`를 생성해야 Log를 출력할 수 있다.\
중요한 것은 Logger의 종류가 매우 많다는 것이며, 우리는 통합 모듈인 `org.slf4j.Logger`를 사용할 것이다.

### 생성 방식 1(instance)

```java
Logger log = LoggerFactory.getLogger(this.getClass());
```

원하는 위치에 위의 코드를 작성하면 Logger가 생성된다. 반드시 import를 org.slf4j.Logger로 해야 한다.\
Logger는 `org.slf4j.LoggerFactory` 클래스를 이용하여 생성하며, 생성 시 **클래스 정보**를 넘겨주도록 되어 있다.\
따라서 현재 객체의 클래스 정보를 반환하는 `this.getClass()` 명령을 사용하여 정보를 넘길 수 있다.\
하지만 static 방식으로 생성할 경우 `this.getClass()` 명령이 작동하지 않기 때문에 다른 방식을 사용해야 한다.

### 생성 방식 2(static)

```java
static Logger log = LoggerFactory.getLogger(MyCustomClass.class);
```

해당 클래스의 `멤버 필드`자리에 코드를 작성하면 Logger가 생성된다. `MyCustomClass`는 임시로 만든 클래스이므로 해당 클래스의 이름을 적으면 된다.\
모든 클래스는 static 내장 객체로 class정보 객체를 가지고 있기 때문에 이를 이용하여 static Logger를 생성할 수 있다.

### 생성 방식 3(annotation)

위의 방식들은 Logger를 직접 만들어야 한다는 불편한 점이 있는데, 이를 개선한 형태가 Annotation 형태이다. `Lombok` 라이브러리에서 지원하는 기능이며 `@Slf4j` 를 대상클래스 위에 선언하면 자동으로 다음 Logger가 생성된다.  [Lombok 설정](/web/back-end/what-is-spring-framework/spring-environment-setting/lombok.md#lombok-3)

```java
@Slf4j
public class MyCustomClass{
    //아래의 변수가 annotation의 효과를 받아 자동으로 생성된다.
    //public static final Logger log = LoggerFactory.getLogger(MyCustomClass.class);

}
```

따라서 편하게 Logger를 이용할 수 있게 된다.

## Logging 출력

다음과 같은 테스트 코드를 `src/main/test` 내부에 작성한다.

```java
@Slf4j
public class Test01 {
	
	@Test
	public void test() {
		log.debug("debug 테스트");
		log.info("info 테스트");
		log.warn("warn 테스트");
		log.error("error 테스트");
	}
	
}
```

아무런 설정도 하지 않은 상황이라면, 다음 결과화면을 확인할 수 있다.

```
warn 테스트
error 테스트
```

`System.out.println` 메소드처럼 출력은 되지만 전부 출력이 되지 않는 것을 확인할 수 있는데, 이는 Logging의 메시지 수준(Level)에 대해서 알아야 이해할 수 있다.

## Logging Level

`Logging`에서는 메시지를 단순 출력하는 것이 아니라 심각도 수준(Level)을 부여하여 상황에 맞게 사용하도록 지원하고 있다.\
`Log4j`에서 사용하는 수준은 총 6가지이다.

* `trace` : 일상적인 모든 활동에 대한 **기록**
* `debug` : **개발**에 사용하는 추적 메시지
* `info` : 사용자가 알아야 할 **정보** 메시지
* `warn` : 오류가 발생할 우려가 있는 **경고** 메시지
* `error` : 프로세스 처리 중 발생한 **오류** 메시지
* `fatal` : 시스템이 다운될 수 있는 **치명적 오류** 메시지

## Logging Setting

위에서 살펴본 Logging Level 과 더불어 알아야 할 것이 `Logging 설정`이다.\
`Logging 설정`을 통해 우리가 원하는 수준의 메시지만 볼 수 있도록 설정할 수 있다.\
특정 수준만 골라서 보는 것은 불가능하며, 지정한 수준보다 심각한 수준은 모두 출력된다.

* 메시지 수준을 `debug`로 설정하면, `debug` 또는 그보다 심각한 수준의 메시지가 출력된다.
  * debug, info, warn, error, fatal
* 메시지 수준을 `warn`으로 설정하면, `warn` 또는 그보다 심각한 수준의 메시지가 출력된다.
  * warn, error, fatal

설정파일은 `src/main/resources`와 `src/test/resources`에 각각 `log4j.xml`이란 이름으로 존재한다(Spring MVC Project 기준)

<div align="left"><img src="https://4208234536-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M_TNZwLuHV9ipYLbvRq%2F-MdFkMTO5CTlf6UQDcdj%2F-MdFkuSoMnszRHXT-O0R%2Fimage.png?alt=media&amp;token=09822c1e-832a-42a8-8a8c-8152a297edbb" alt=""></div>

설정 파일을 수정하여 원하는 형태의 로깅을 구현할 수 있다.

## Logging Setting File Config

기본적으로 제공되는 `log4j.xml` 파일을 열면 다음과 같은 화면이 나온다.

<div align="left"><img src="https://4208234536-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M_TNZwLuHV9ipYLbvRq%2F-MdFkMTO5CTlf6UQDcdj%2F-MdFkxJedjBjetDz1mEY%2Fimage.png?alt=media&amp;token=63b72b0a-e9bb-4ef0-b7d2-626540e790f3" alt=""></div>

위의 화면은 전체적인 디자인을 볼 수 있도록 제공되는 화면으로, 전체적인 구조를 볼 수 있지만 코드를 살펴보기 어려우므로 보는 방식을 변경해야 한다.

<div align="left"><img src="https://4208234536-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M_TNZwLuHV9ipYLbvRq%2F-MdFkMTO5CTlf6UQDcdj%2F-MdFkz-KKnJSKdnehn88%2Fimage.png?alt=media&amp;token=eb9bfb2f-42f7-4463-9442-7059f7df8a8c" alt=""></div>

`Source` 탭을 클릭하면 다음과 같은 코드를 확인할 수 있다.

```markup
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "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="%-5p: %c - %m%n" />
		</layout>
	</appender>
	
	<!-- Application Loggers -->
	<logger name="com.hakademy.app">
		<level value="info" />
	</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="warn" />
		<appender-ref ref="console" />
	</root>
	
</log4j:configuration>

```

## 파일 형태

파일의 형태를 살펴보면 다음과 같다.

### xml header

```xml
<?xml version="1.0" encoding="UTF-8"?>
```

xml이면 반드시 있어야 하는 헤더 선언이다.

### 문서 선언(DTD : Document Type Definition)

```xml
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
```

이 문서에서 사용할 태그의 정의 정보가 들어있다.

### XML Namespace 선언(xmlns) 및 최상위 영역 생성

```xml
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

</log4j:configuration>
```

이 설정에서 사용할 최상위 영역을 선언하고, xmlns 설정을 통해 내부에서 사용할 정보들을 불러온다.

### appender

[공식 API 문서 보기](https://logging.apache.org/log4j/2.x/manual/appenders.html)\
`Appender` 태그는 **로그 출력 도구**이다.\
모든 출력 클래스는 `Appender`를 상속받아 구현해야 하며 주요 Appender는 다음과 같다.

* `org.apache.log4j.ConsoleAppender`
  * 콘솔에 출력을 수행하는 도구
* `org.apache.log4j.FileAppender`
  * 파일에 출력을 수행하는 도구
* `org.apache.log4j.RollingFileAppender`
  * 파일에 지정한 방식에 의해 분할 출력을 수행하는 도구
* `org.apache.log4j.DailyRollingFileAppender`
  * 파일에 일자별로 출력을 수행하는 도구
* `org.apache.log4j.JdbcAppender`
  * Database에 출력을 수행하는 도구
* `org.apache.log4j.SMTPAppender`
  * 이메일에 출력을 수행하는 도구
* `org.apache.log4j.NTEventAppender`
  * 윈도우 시스템 로그에 출력을 수행하는 도구

### **ConsoleAppender 예시**

```markup
<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.SSS} [%-5p] %c - %m%n" />
    </layout>
</appender>
```

### **FileAppender 예시**

```markup
<appender name="xml" class="org.apache.log4j.FileAppender">
    <param name="file" value="D:/log.xml"></param>
    <param name="Append" value="true"></param>
    <layout class="org.apache.log4j.xml.XMLLayout"></layout>
</appender>
```

### **DailyRollingFileAppender 예시**

```markup
<appender name="daily" class="org.apache.log4j.DailyRollingFileAppender">
    <param name="file" value="D:/log/daily"></param>
    <param name="Append" value="true"></param>
    <!-- 파일의 날짜 패턴 -->
    <param name="DatePattern" value="'.'yyyy-MM-dd'.log'"></param>
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5p] %c - %m%n" />
    </layout>
</appender>
```

### **Appender 옵션 설명**

> Target 설정

`ConsoleAppender`에서 설정하며 표준 출력 통로(System.out)을 지정하여 메시지가 콘솔 화면에 출력되도록 설정한다.

```markup
<param name="Target" value="System.out" />
```

> File

`FileAppender`, `DailyRollingFileAppender` 등과 같이 파일에 출력하는 Appender에서 사용한다.\
상대경로로 작성 시 프로젝트 root를 기준으로 설정되며, 절대경로로 작성도 가능하다.\
만약 폴더가 존재하지 않는다면 폴더를 생성해준다.

```markup
<param name="file" value="D:/log.xml"></param>
```

> Append

로그를 덧붙일 것인지(Append), 덮어쓸 것인지(Overwrite) 설정할 수 있다.

```markup
<param name="Append" value="true"></param>
```

### layout

[공식 API 문서 보기](https://logging.apache.org/log4j/2.x/manual/layouts.html)\
`Layout`을 통하여 출력되는 메시지의 형태를 설정할 수 있다.\
Log4j의 모든 레이아웃은 `Layout`을 상속받아 구현되도록 되어 있다.\
그 중 패턴을 지정할 수 있는 `PatternLayout`의 주요 형식은 다음과 같다.

* `%p` : priority, 메시지의 수준이 출력될 자리
* `%m` : message, 출력하고자 하는 메시지의 자리
* `%n` : new-line, 개행 문자의 자리
* `%c` : category, 로그가 출력되는 대상의 카테고리가 출력될 자리
* `%d` : date, 시간이 출력될 자리

모든 자리는 숫자를 이용하여 형식을 설정할 수 있다.

* `%5p` : 메시지 수준을 +5 형식으로 출력(5칸에 우측(`+`) 정렬)
* `%-5p` : 메시지 수준을 -5 형식으로 출력(5칸에 좌측(`-`) 정렬)

> Layout 예시

```markup
<layout class="org.apache.log4j.PatternLayout">
    <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5p] %c - %m%n" />
</layout>
```

### logger

`logger` 설정을 이용해서 패키지별 또는 써드파티(3rd party)의 Logging level을 설정할 수 있다.\
만약 생략할 경우 `root` 설정을 따라간다.\
삭제해도 무방하다.

> 예시

```markup
<logger name="org.springframework.core">
    <level value="warn" />
</logger>
```

### root

로그 메시지의 출력 수준과 방식을 설정할 수 있다.

* `priority` : 로그 출력 수준을 지정
* `appender-ref` : 로그 출력 Appender의 id를 설정. 복수 개 설정 가능

> 예시

```markup
<root>
    <priority value="debug" />
    <appender-ref ref="console" />
    <appender-ref ref="daily" />
</root>
```

## 최종 파일 양식

기본적인 설정들이 되어 있는 파일 양식은 다음과 같다.

```markup
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

	<!-- 
		Appender : 로그를 출력하는 도구(위치 결정 도구)
		- ...Appender라는 이름을 가짐
		- ConsoleAppender : 출력을 콘솔창에 수행
		- FileAppender : 출력을 파일에 수행
		- DailyRollingFileAppender : 출력을 일정 기간 단위의 파일에 수행
		- JdbcAppender : 출력을 DB에 수행
		- SMTPAppender :  출력을 이메일에 수행
		- NTEventAppender : 출력을 윈도우 시스템 로그에 수행
		
		Layout : 출력 형식
		- %p : priority, 메시지 수준(Level)
		- %m : message, 출력하고자 하는 메시지
		- %n : newline, 개행(엔터)
		- %c : category, 카테고리 출력, 뒤에 {n}를 붙이면 패키지 깊이(depth)를 설정할 수 있음
		- %d : date, 시간 설정(자바 시간양식과 동일. SimpleDateFormat 참조)
				- y(연), M(월), d(일), E(요일), a(오전/오후) 
				- H(24시), h(12시), m(분), s(초), S(밀리초)
	-->
	<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.SSS} [%-5p] %c - %m%n" />
		</layout>
	</appender>
	
	<appender name="xml" class="org.apache.log4j.FileAppender">
<!-- 		<param name="file" value="log.xml"></param> -->
		<param name="file" value="D:/log.xml"></param>
		<param name="Append" value="true"></param>
		<layout class="org.apache.log4j.xml.XMLLayout"></layout>
	</appender>
	
	<appender name="daily" class="org.apache.log4j.DailyRollingFileAppender">
		<param name="file" value="D:/log/daily"></param>
		<param name="Append" value="true"></param>
		<!-- 파일의 날짜 패턴 -->
		<param name="DatePattern" value="'.'yyyy-MM-dd'.log'"></param>
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5p] %c - %m%n" />
		</layout>
	</appender>
	
	<!-- 3rdparty Loggers -->
	<logger name="org.springframework.core">
		<level value="warn" />
	</logger>	
	
	<logger name="org.springframework.beans">
		<level value="warn" />
	</logger>
	
	<logger name="org.springframework.context">
		<level value="warn" />
	</logger>

	<logger name="org.springframework.web">
		<level value="warn" />
	</logger>
	
	<!-- Root Logger -->
	<root>
		<priority value="debug" />
		<appender-ref ref="console" />
        <!-- <appender-ref ref="daily" /> -->
	</root>
	
</log4j:configuration>
```
