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를 추가한다.
- Spring라이브러리에서 commons-logging을 제거하면, Spring을 사용할 때 commons-logging라이브러리를 찾으면서 오류가 발생
- 그래서 logback라이브러리를 사용하려면 그 부분이 사용되지 않도록 설정해주어야 함.
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>
- ConsoleAppender : 콘솔에 로그를 어떤 포맷으로 출력할지를 설정
- logger 설정
- Log level 설정
- 아래 쪽으로 갈 수록 심각한 오류
- 개발 시에는 자세한 오류를, 운영 중에는 간략한 오류를 보는 등 레벨 설정이 필요
- 개인 개발, 공동 개발, 테스트, 운영에 따라 각각 level을 나누면 좋음
-
- trace : debug보다 세분화된 정보
- debug : 디버깅하는데 유용한 세분화된 정보
- info : 진행상황 같은 일반 정보
- warn : 오류는 아니지만 잠재적인 오류 원인이 될 수 있는 경고성 정보
- error : 요청을 처리하는 중 문제가 발생한 오류 정보
- Log level 설정
- 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>
- Appender 설정
- configuration
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. 결과
접속 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 |