로그 ( Log )
로그는 기록을 남기는 것을 의미합니다.
구체적으로는 프로그램 개발이나 운영 시 발생하는 문제점을 추적 하거나 운영 상태를 모니터링 하는 정보를 기록하는 것이죠.
또한 분석을 통해 통계를 낼 수도 있기 때문에 기록을 남기는 것은 중요하다고 할 수 있습니다.
하지만 로그를 남기면 성능이 나빠진다는 단점이 있는데, 그 보다 로그를 통해 얻는 정보가 훨씬 많기 때문에 필요한 부분에 파일로써 로그를 남기는 것이 중요합니다.
Logback
스프링에서는 기본적으로 commons.logging 라이브러리 ( Apache의 JCL, Jakarta Commons Logging )을 사용합니다.
즉 스프링 개발을 할 때 스프링이 뿜어내는 메시지는 JCL에 의존하여 로그를 남기는 것입니다.
실제로 spring-context 라이브러리를 설치할 때 프로젝트 폴더의 Maven Libarary를 확인해보시면 commons.logging 라이브러리를 확인할 수 있습니다.
예전에는 스프링에서 로그를 남길 때 Log4J를 사용했었는데, 성능 및 기능상의 이유로 대체 logger들이 많아졌고, 현재 대부분은 SLF4J 인터페이스를 구현한 Logback을 사용합니다.
Spring이 기존에 사용하던 로그 라이브러리 JCL 대신, 새로운 라이브러리 Logback을 사용하도록 하기 위해서는 SLF4J가 필요합니다.
즉 SLF4J는 JCL과 Log4J의 징검다리 역할을 한다고 보면 됩니다.
환경 설정
pom.xml
<properties>
<org.springframework-version> 4.2.1.RELEASE </org.springframework-version>
<jcloverslf4j.version>1.7.6</jcloverslf4j.version>
<logback.version>1.1.1</logback.version>
</properties>
<dependencies>
<!-- Spring core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<!-- JCL 제외 -->
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Logback -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${jcloverslf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
...
앞서 언급했듯이 spring-context에서는 기본적으로 commons-logging 라이브러리를 사용하고 있으므로 Logback 라이브러리로 대체하기 위해서는 spring-context 라이브러리를 추가할 때 commons-logging 라이브러리를 제외 시켜야 합니다.
JCL을 제외시켰기 때문에 기존에 JCL을 통해 로그를 남기던 코드들은 에러를 발생 시킬 것입니다.
그래서 필요한 것이 jcl-over-slf4j 라이브러리이며, 일종의 다리 역할을 합니다.
실제로는 SLF4J을 구현한 logback-classic 라이브러리가 로그를 남기게 됩니다.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 콘솔로 로그를 남김 -->
<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<charset>UTF-8</charset>
<!-- 로그 메시지 패턴 -->
<Pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n
</Pattern>
</encoder>
</appender>
<!-- 파일로 로그를 남김 -->
<appender name="fileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>c:\LogExample\logexample2.log</file>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<Pattern>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n
</Pattern>
</encoder>
<!-- 로그를 남기는 파일의 용량이 50KB가 넘으면 이를 압축 파일로 만들고 새로 로그 파일로 만들라는 정책 -->
<triggeringPolicy
class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10KB</MaxFileSize>
</triggeringPolicy>
<!-- 파일을 덮어쓰는 정책 -->
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<FileNamePattern>C:\LogExample\logexample2.%i.log.zip</FileNamePattern>
<!--
MinIndex가 1이고, MaxIndex가 10이므로, 위의 파일 이름 패턴에 따라 아래의 로그 파일이 생길 것이다.
logexample2.1.log.zip logexample2.2.log.zip .... logexample2.10.log.zip
이 상태에서 또 10KB가 넘으면 logexample2.1.log.zip이 된다.
-->
<MinIndex>1</MinIndex>
<MaxIndex>10</MaxIndex>
</rollingPolicy>
</appender>
<!--
com.victolee.logExample 아래 패키지 로그들만 consoleAppender, fileAppender 방법으로 로그를 남긴다.
물론 <appender-ref ref="consoleAppender" />를 추가하여 콘솔로도 로그를 남길 수 있다.
-->
<logger name="com.victolee.logExample" level="info" additivity="false">
<appender-ref ref="fileAppender" />
</logger>
<!-- root는 글로벌 로거를 의미하며, 위의 logger에 해당하지 않으면 root 로거가 실행된다. -->
<root level="warn">
<appender-ref ref="consoleAppender" />
</root>
</configuration>
Log를 어떻게 남길지에 대한 설정 파일 입니다.
콘솔로 로그를 남기는 방법이 있고, 파일로 로그를 남기는 방법이 있습니다. ( 두 가지 방법을 함께 사용할 수도 있습니다. )
파일로 로그를 남길 경우, 어느 경로에 파일일 남길 것인지, 파일의 용량을 어느 정도 크기로 제한을 할 것인지, 파일이 제한한 용량을 초과했을 시 어떻게 할 것인지에 대한 정책들을 작성합니다.
그리고 어느 패키지 또는 클래스에 대해서 로그를 남길 것인지,
어느 정도 수준에 대해서 로그를 남글 것인지도 설정할 수 있습니다.
테스트
로그가 잘 되는지 확인하기 위해 com.victolee.logExample 패키지를 생성한 후, 컨트롤러를 작성해주세요.
com.victolee.logExample/logExample
@Controller public class logExample { private static final Log LOG = LogFactory.getLog( logExample.class ); @RequestMapping("/log") @ResponseBody public String logExam() { LOG.debug( "#ex1 - debug log" ); LOG.info( "#ex1 - info log" ); LOG.warn( "#ex1 - warn log" ); LOG.error( "#ex1 - error log" ); return "콘솔 또는 파일경로 확인"; } }
com.victolee.logExample 패키지를 생성한 이유는 위의 설정 파일의 <logger>에서 범위를 지정했기 때문입니다.
그리고 파일로 로그를 남기겠다고 했으므로 요청을 했을 시 C:\LogExample\logexample2.log 경로에 파일이 작성되는지 확인해보겠습니다.
레벨
그리고 파일을 한 번 열어보면 아래와 같을 것입니다.
logExample 클래스를 보시면 4 가지의 level에 대해서 로그를 찍도록 했지만, debug 수준의 로그는 찍히지 않았습니다.
그 이유는 <logger>에서 level을 info로 설정했기 때문입니다.
debug , info , warn , error 은 level을 의미합니다.
그리고 level은 debug < info < warn < error 순으로 높습니다.
따라서 level = " info "라면 info 보다 level이 낮은 debug는 로그로 남기지 않을 것입니다.
파일 정책
마지막으로 파일 정책에 대해 확인해보겠습니다.
브라우저에서 새로고침을 계속 눌러서 ( 15초 정도? ) 서버에 요청을 계속 보내겠습니다.
설정 파일에서 작성한 정책대로 10KB가 넘어가면 압축을 하게 됩니다.
만약 logexample2.10.log.zip 까지 압축 파일이 생성되었다면 다음 압축 파일은 rolling 정책에 의해 logexample2.1.log.zip에 덮어 씌어질 것입니다.
이상으로 Logback을 통해 로그를 남기는 방법에 대해 알아보았습니다.
이 글에서는 간단하게 테스트할 목적으로 컨트롤러에 작성을 했지만 실제로는 예외가 발생할 수 있는 부분, 통계가 필요한 부분에 로그를 남기면 좋을 것입니다.