Web/Java+Spring

Logging

WakaraNai 2021. 9. 10. 13:29
728x90
반응형
  • 정보를 제공하는 일련의 기록인 로그(log)를 생성하도록 시스템을 작성하는 활동
  • 프린트 줄 넣기(printlining)는 간단한, 보통은 일시적인, 로그를 생성하기만 한다.
  • 시스템 설계자들은 시스템의 복잡성 때문에 로그를 이해하고 사용해야 한다.
  • 로그가 제공하는 정보의 양은, 이상적으로는 프로그램이 실행되는 중에도, 설정 가능해야 한다.
  • 일반적으로 로그 기록의 이점
    - 로그는 재현하기 힘든 버그에 대한 유용한 정보를 제공할 수 있다.
    - 로그는 성능에 관한 통계와 정보를 제공할 수 있다.
    - 설정이 가능할 때, 로그는 예기치 못한 특정 문제들을 디버그하기 위해, 그 문제들을 처리하도록 코드를 수정하여 다시 적용하지(redeploy) 않아도, 일반적인 정보를 갈무리할 수 있게 한다.

 

 

로그를 출력하는 방법

  • System.out.print() : 웹의 속도가 느려진다는 단점
  • logging 라이브러리 이용
    • java.util.logging : 기능이 많이 부족
    • Apache Commons logging
    • Log4j  => 개선 =>  Logback
    • 실습에서는 LogBack을 이용

 

SLF4J

  • 수많은 logging 라이브러리들을 하나의 통일된 방식으로 사용할 수 있는 방법을 SLF4J가 제공
    • 더 좋은 logging 라이브러리가 나와도 기존 코드가 변경될 필요가 없다는 장점
  • 이 때 SLF4J는 logging Facade라고 함
    • logging에 대한 추상 레이어를 제공하는 것이고 interface의 모음

 

 

Maven에 SLF4J와 Logback 의존성 추가하기

  • 참고로 logback-classic 1.2.3은 이미 slf4j-api 1.7.25에 대한 의존성을 가지고 있기 때문에 slf-j-api를 추가할 필요는 없다.
  • Spring은 기본적으로 아파치 재단의 commons-logging을 사용.
    • 그래서 logback라이브러리를 사용하려면 그 부분이 사용되지 않도록 설정해주어야 함.
      • Spring라이브러리에서 commons-logging을 제거하면, Spring을 사용할 때 commons-logging라이브러리를 찾으면서 오류가 발생
        • ClassNotFoundException
      • 이러한 오류를 제거하기 위해서 jcl-over-slf4j를 추가한다.

 

 

Logback 설정

  • logback.xml
    • configuration
      • Appender 설정
        • ConsoleAppender : 콘솔에 로그를 어떤 포맷으로 출력할지를 설정
          •     <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
                    <encoder> <!-- name을 보통 CONSOLE로, class를 ConsoleAppender로 -->
                        <Pattern>%d{HH:mm} %-5level %logger{36} - %msg%n</Pattern>
                         <!-- Pattern에서 로그 출력 방식 정의 -->
                         <!-- {36}으로 최대 자릿수 표현 -->
                         <!-- %-5level : 로그 레벨을 5의 고정폭 값으로 출력하라 -->
                         <!-- %msg : %message의 alias로 사용자가 출력한 메시지가 출력됨 -->
                         <!-- %n : 줄바꿈 -->
                    </encoder>
                </appender>
        • FileAppender : 파일에 로그를 어떤 포맷으로 출력할지를 설정
        • RollingFileAppender : 로그의 양이 많아지면, 하나의 파일로 관리하기 어려워지는 경우가 생긴다.
          이런 문제를 해결하기 위해 하루 단위로 로그를 관리하고자 할 경우 사용된다.
        •     <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
                
                    <!-- name을 보통 FILE로, class를 RollingFileAppender -->
                
                <file>access.log</file>  <!-- 여기에 기록되는 파일명을 적어둠-->
                <!-- 디렉토리 경로를 생략했기에 이클립스에서 실행하면 이클립스 설치 경로에 로그 파일 생성 -->
                
                  
                  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                  <!-- 로그 파일이 언제 백업될지 결정 -->
                  <!-- 지금은 하루 단위로 로그 파일 생성 : access-년월일가진 로그 파일 30개 생성 -->
                      <fileNamePattern>access-%d{yyyy-MM-dd}.log</fileNamePattern>
                      <maxHistory>30</maxHistory>
                  </rollingPolicy>
                  <encoder>
                      <Pattern>%d{HH:mm} %-5level %logger{36} - %msg%n</Pattern>
                  </encoder>
              </appender>
      • logger 설정
        • Log level 설정
          • 아래 쪽으로 갈 수록 심각한 오류
          • 개발 시에는 자세한 오류를, 운영 중에는 간략한 오류를 보는 등 레벨 설정이 필요 
            • 개인 개발, 공동 개발, 테스트, 운영에 따라 각각 level을 나누면 좋음
            1. trace : debug보다 세분화된 정보
            2. debug : 디버깅하는데 유용한 세분화된 정보
            3. info : 진행상황 같은 일반 정보
            4. warn : 오류는 아니지만 잠재적인 오류 원인이 될 수 있는 경고성 정보
            5. error : 요청을 처리하는 중 문제가 발생한 오류 정보
      • root 설정
      •   <!-- 레벨 지정-->
         <!--org.springframework로 시작하는 패키지에 속한 클래스에서 출력하는 로그는 info 이상의 레벨 로그로 출력-->
         <logger name="org.springframework" level="info"/>
         <!--kr.or.connect로 시작하는 패키지에 속한 클래스에서 출력하는 로그는 debug 이상의 레벨 로그로 출력-->
         <logger name="kr.or.connect" level="debug"/>
            <root level="debug">
            	<!-- 모든 대상에 CONSOLE과 FILE Appender를 적용하라는 것을 의미 -->
                <!-- 즉, console과 file 양쪽에 로그를 출력 -->
                <appender-ref ref="CONSOLE"/>
                <appender-ref ref="FILE"/>
            </root>

 

 

Logger 객체 선언

로그를 남기고자 하는 클래스에, SLF4J 라이브러리를 이용하여,

LoggerFactory logger 객체를 필드로 선언

 

  • 문자열을 + 연산자를 이용해서 연결하면 속도가 굉장히 느려지기 때문에 제외
    • SLF4J의 로그 출력 메소드의 첫 번째 인자로 출력할 로그 문자열을 넣고
    • 두번째부터 가변인자를 받음 -> 인자로 여러 개 넘기세요
  • 로그는 남길 변수의 수만큼 {} 을 이용
  • 로그 수준에 따라 debug(), info(), warn(), error() 메소드를 이용
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
.......
private Logger logger = LoggerFactory.getLogger(this.getClass());

예시

logger.trace("{} {} 출력", "값1", "값2");
logger.debug("{} {} 출력", "값1", "값2");
logger.info("{} {} 출력", "값1", "값2");
logger.warn("{} {} 출력", "값1", "값2");
logger.error("{} {} 출력", "값1", "값2");

 

 

 

 

 

실습

1. pom.xml에 의존성 추가

  	<dependency>
   	  <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.25</version>
    
   	</dependency>

  	<dependency>
   	  <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.3</version>
    
   	</dependency>
   	
   	
   	
   	 <dependency>
   	  <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
      <!--  <exclusions>
      	<exclusion>
      		<groupId>commons-logging</groupId>
      		<artifactId>commons-logging</artifactId>
      	</exclusion>
      </exclusions>  -->
   	</dependency>



2. src/main/resources에 logback.xml 생성하여 다음 내용 추가

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder> <!-- name을 보통 CONSOLE로, class를 ConsoleAppender로 -->
            <Pattern>%d{HH:mm} %-5level %logger{36} - %msg%n</Pattern>
             <!-- Pattern에서 로그 출력 방식 정의 -->
             <!-- {36}으로 최대 자릿수 표현 -->
             <!-- %-5level : 로그 레벨을 5의 고정폭 값으로 출력하라 -->
             <!-- %msg : %message의 alias로 사용자가 출력한 메시지가 출력됨 -->
             <!-- %n : 줄바꿈 -->
        </encoder>
    </appender>
    
    
     <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
      
          <!-- name을 보통 FILE로, class를 RollingFileAppender -->
      
      <file>access.log</file>  <!-- 여기에 기록되는 파일명을 적어둠-->
      <!-- 디렉토리 경로를 생략했기에 이클립스에서 실행하면 이클립스 설치 경로에 로그 파일 생성 -->
      
        
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!-- 로그 파일이 언제 백업될지 결정 -->
        <!-- 지금은 하루 단위로 로그 파일 생성 : access-년월일가진 로그 파일 30개 생성 -->
            <fileNamePattern>access-%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <Pattern>%d{HH:mm} %-5level %logger{36} - %msg%n</Pattern>
        </encoder>
    </appender>
    
      <!-- 레벨 지정-->
 <!--org.springframework로 시작하는 패키지에 속한 클래스에서 출력하는 로그는 info 이상의 레벨 로그로 출력-->
 <logger name="org.springframework" level="info"/>
 <!--kr.or.connect로 시작하는 패키지에 속한 클래스에서 출력하는 로그는 debug 이상의 레벨 로그로 출력-->
 <logger name="kr.or.connect" level="debug"/>
    <root level="debug">
    	<!-- 모든 대상에 CONSOLE과 FILE Appender를 적용하라는 것을 의미 -->
        <!-- 즉, console과 file 양쪽에 로그를 출력 -->
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </root>
    
</configuration>

 

 

3. LogInterceptor.java에 추가

package kr.or.connect.guestbook.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class LogInterceptor extends HandlerInterceptorAdapter {
	
	//추가
	private Logger logger = LoggerFactory.getLogger(this.getClass());
	 
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		//추가
		logger.debug("{} 가 종료되었습니다. {}를 view로 사용합니다.", handler.toString(), modelAndView.getViewName());
		//System.out.println(handler.toString() + " 가 종료되었습니다.  " + modelAndView.getViewName() + "을 view로 사용합니다.");
	}

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		//추가
		logger.debug("{}를 호출했습니다.", handler.toString());
		//System.out.println(handler.toString() + " 를 호출했습니다.");
		return true;
	}
}

 

 

 

4. 결과

login 완료 시

 

방명록 출력 시

 

접속 OS, 브라우저, 언어

접속자 수 및 모바일 접속 여부도 나옴

728x90
반응형

'Web > Java+Spring' 카테고리의 다른 글

ArgumentResolver  (0) 2021.07.13
Interceptor  (0) 2021.07.13
Spring MVC에서 Session 사용하기  (0) 2021.07.08
RestController - Rest API by Spring MVC  (0) 2021.06.10
Layered Architecture 실습 - 방명록  (0) 2021.06.09