ㅁ 로그, ajax 관련 프로젝트
ㅁ Spring legacy project 생성
- 베이스 패키지는 com.br.ajax로 했다.
모든 클래스는 전부 저 베이스 패키지로 해야 한다.
빈 스캐닝할 때 베이스 패키지를 기준으로 하기 때문에 2번째 레벨 등에서 쪼개지면 안 된다.
- 빈등록이 제대로 되려면 베이스 패키지로 com.br.ajax를 가지고 있어야 한다.
ㅁ 프로젝트를 만들면 바로 pom.xml을 바꾼다.
<?xml version="1.0" encoding="UTF-8"?>
<modelVersion>4.0.0</modelVersion>
<groupId>com.br</groupId>
<artifactId>ajax</artifactId>
<name>03_Spring_Logging_AJAX</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<java-version>11</java-version>
<org.springframework-version>5.3.27</org.springframework-version>
<org.aspectj-version>1.9.19</org.aspectj-version>
<org.slf4j-version>2.0.7</org.slf4j-version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- Logging -->
<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>1.2.15</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>
<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- Lombok 라이브러리 추가 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java-version}</source>
<target>${java-version}</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>org.test.int1.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 기존의 프로젝트에서 pom.xml 내의 내용을 복사해서 붙여넣기 한다.
- 그런데 <modelVersion> ~ </version> 부분은은 가져오면 안된다.
- <properties> 부터 </build>까지만 복사해서 붙여넣기 한다.
- <dependencies>가 필요한 라이브러리 목록이었다.
- 자바 버전은 바로 반영이 안돼서, 프로젝트 우클릭 - maven - update project를 해서 자바 버전이 11버전으로 잘 바뀌었는지 확인한다.
ㅁ 샘플 파일들 삭제
<%@ page session="false" %>
<html>
<head>
<title>Home</title>
</head>
<body>
<h1>
Hello world!
</h1>
<P> The time on the server is ${serverTime}. </P>
</body>
</html>
- src/main/webapp/WEB-INF/views/home.jsp
- 얘네가 만들어놓은 메인페이지
- 그리고 home.jsp로 포워딩하는 HomeController.java도 만들어져있다.
컨트롤러용 클래스여서 @Controller 어노테이션이 있다.
- /가 오면 home으로 이동되게끔 반환하고 있다.
- 이 페이지에서 쓰라고 날짜과녈ㄴ해서 담아놨다. home.jsp에서 el구문으로 꺼내서 출력
package com.br.ajax;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Handles requests for the application home page.
*/
@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
return "home";
}
}
- src/main/java/com/br/ajax/HomeController.java
ㅁ 패키지 생성
- com.br.ajax.controller
- com.br.ajax.dto
- com.br.ajax.service
- dao는 두지 않고 service에서 받았다고 가정한다.
ㅁ views 폴더에 main.jsp 생성
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h3>메인페이지 입니다</h3>
</body>
</html>
ㅁ MvcController 클래스 생성
package com.br.ajax.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MvcController {
@RequestMapping(value= {"/", "/main.do"})
public String mainPage() {
System.out.println("MvcController의 mainPage 메소드 작동됨");
return "main";
}
}
- 컨트롤러 역할을 하는 클래스에 바로 @Controller를 붙인다.
- 디버깅(데이터가 잘 담겼는지 확인하는 등)할 때 이때까지 System.out.println문을 사용해 왔다.
이 메소드가 잘 실행되는지 확인용으로 디버깅용으로 출력문을 작성했었다.
- 기본적으로 이 출력문은 운영되는 서버의 콘솔에 출력된다.
개발이 다 끝나고 실제 배포되면 운영되는 서버의 콘솔창에 출력된다.
ㅁ 브라우저로 접속
- 서버 우클릭 - add and remove로 이전 프로젝트는 내리고 지금 프로젝트를 올린다.
- http://localhost:8888/ajax를 입력해서 메인페이지에 접속해본다.
- 콘솔에도 출력되어 있다.
- 과부하가 심해서 로그로 출력하는 방법이 좋다.
ㅁ
- 출력문은 성능 저하를 야기시켜서 로그를 출력한다.
- 알게모르게 서버를 start하는 순간 읽어들이는게 굉장히 많다.
콘솔창에 사실 로그 출력이 되고 있었다. 검은 글씨가 전부 로그 출력이다.
web.xml 거쳐서 ~를 읽어들인다.
서버 start 되는 순간 일어나는 일이 굉장히 많다.
- 서버를 start하는 순간 스프링 객체들도 생성되고 거기에 로그 출력문들이 있어서 검은 글씨의 로그가 출력되고 있었다.
엄청 많은데 그중에 INFO 레벨 이상의 로그만 출력되고 있는 것이다.
로그의 레벨도 엄청 많다.
- 알게모르게 스프링 프레임워크를 쓰는 순간
- 요즘은 로깅 프레임워크로 log4j보다 로그백을 쓰는 추세로 바뀌었다.
- src/main/resources의 log4j.xml 덕분에 로그 출력이 되고 있었다.
- log4j.xml을 열어본다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<!-- 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.br.ajax">
<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>
- ConsoleAppender로 콘솔에 출력하고 있다.
- %p가 의미하는게 로그 레벨이다. %-5p로 INFO가 출력된다.
- %c가 의미하는게 그 로그가 어느 클래스에 위치하고 있는지 org.springframework.web.context.ContextLoader가 출력된다.
- %m은 출력 메세지
- %n은 줄바꿈
- 그리고 아래에 info 레벨 이상의 로그만 출력되게 설정되어 있다.
- 로그백을 쓰면 패턴도 달라진다.
- 콘솔 말고 파일에도 출력할 수 있다.
ㅁ
- 로그 관련한 파일(xml)이 src/main/resources에만 ~이름으로 있으면 된다.
- log4j.xml를 삭제해도 된다. 나중엔 삭제한다. 근데 지금은 사용하지 않도록 이름만 log4j_notuse.xml로 바꿔본다.
<?xml version="1.0" encoding="UTF-8"?>
<modelVersion>4.0.0</modelVersion>
<groupId>com.br</groupId>
<artifactId>ajax</artifactId>
<name>03_Spring_Logging_AJAX</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<java-version>11</java-version>
<org.springframework-version>5.3.27</org.springframework-version>
<org.aspectj-version>1.9.19</org.aspectj-version>
<org.slf4j-version>2.0.7</org.slf4j-version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.14</version>
<!-- <scope>test</scope> 이건 지워야 함 -->
</dependency>
<dependency> <!-- 기존 dependency 중 이것만 남기고 다 지운다.-->
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- Lombok 라이브러리 추가 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java-version}</source>
<target>${java-version}</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>org.test.int1.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
- pom.xml에서 기존의 log4j 관련 라이브러리도 삭제한다.
<!-- Logging --> 중에서 맨 처음 dependency빼고 다 삭제한다.
- mvnrepository에서 "logback"을 검색한다.
"logback classic module"을 클릭해서 usages가 높은 1.4.14 버전으로 한다.
ㅁ src/main/resources에 logback.xml 생성
- 이름을 이렇게 작성해야 자동으로 실행된다.
- 로그 관련 환경설정 파일 작성법이 어렵다.
- 수정할 일은 없다. 그래도 볼 줄은 알아야 한다.
- 제일 바깥은 <configuration> 태그로 감싸있어야 한다. <configuration> 태그를 작성한다.
ㅁ 로깅(로그 출력)을 하는 이유
- 시스템 작동상태를 기록하거나 시스템의 동작들을 분석하기 위해 기록해둘 필요가 있다.
- 사용자가 시스템을 이용하면서 발생되는 문제점(장애)들을 기록해놔야 후에 유지보수할 수 있다.
- ~ 제일 먼저 해야하는 것이 로그 파일을 ~
- 디버깅 용으로 데이터들을 출력할 때 print로 출력시 성능저하를 야기시킨다.
또 콘솔에만 출력된다. 파일에 출력하려면 Stream으로 파일에 출력하는 코드를 작성해야 한다.
ㅁ 로그 장점
- 프로그램의 문제 파악에 용이
- 빠르고 효율적인 디버깅이 가능
- 로그 이력을 저장매체(파일, RDB=관계형데이터베이스 등)에 외부로 남길 수 있다.
ㅁ 로그 단점
- 로그에 대한 디바이스(저장매체) 입출력으로 인해 런타임 오버헤드가 발생할 수 있다.
(이것만의 과부하가 발생한다)(확인했으면 지워주기도 해야함)
- 로깅을 위한 추가 코드로 인해서 전체 코드 사이즈가 증가될 수 있다.
- 무분별한 로그 출력은 혼란을 야기하거나 애플리케이션 성능에 영향을 미친다.
※ Logging Framework (log4j, logback, log4j2, slf4j, ...)
- 스프링은 기본적으로 log4j가 잡혀있다.
- 스프링부트는 기본적으로 logback가 잡혀있다.
- 요즘에는 logback을 많이 쓰는 추세다. 이게 내부적으로 log4j보다 10배정도 빠르다고 한다.
- slf4j가 다양한 logging framework 간에 중간 인터페이스 역할을 수행하면서 호환성을 보장하는 역할을 수행한다.
그래서 pom.xml에서도 slf4j 부분은 지우지 않았다.
※ <appender>
- 전달된 로그를 어디(콘솔, 파일, db)에 어떤 형식으로 출력할지 결정하는 태그
- ConsoleAppender : 로그를 콘솔에 출력하기 위한 appender
- JDBCAppender : 로그를 RDB에 출력하기 위한 appender
- FileAppender : 로그를 파일에 출력하기 위한 appender
- RollingFileAppender : FileAppender를 보완한 개념
- logback 공식 사이트에서도 영어로 설명이 있다. 그런데 보기 어려워서 ~
- ~ 그래서 결국 RollingFileAppender를 쓴다.
일정 조건(일정 용량, 일정 날짜 등) 후에 기존 파일을 백업 파일로 바꾸고 다시 새로운 파일로 로깅을 시작한다.
※ <encoder> + <pattern>
- 로그를 어떤 패턴으로 출력할건지 형식을 지정한다.
- %logger : 로거 주체 (로그가 출력되는 클래스)
- %logger{0}으로 하면 클래스명이 출력된다.
- %logger{1}하면 패키지 이름의 마지막 부분과 클래스명이 출력된다.
- 그냥 %logger를 두면 풀 클래스명이다. 풀 클래스명으로 두는게 좋다.
- %line : 로거 발생 줄 수
- %level : 로그 레벨
- %msg : 로그 메세지
- %date : yyyy-MM-dd HH:mm:ss:SSS 형식으로 로그 출력 날짜가 출력된다.
- %date{포맷} : 내가 지정한 날짜 및 시간에 대한 포맷을 반영한 로그출력 날짜가 출력된다.
ㅁ logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender class="ch.qos.logback.core.ConsoleAppender" name="consoleLog">
<encoder>
<pattern>%-5level: [%date{yyyy-MM-dd HH:mm:ss}] [%logger:%line] - %msg%n</pattern>
</encoder>
</appender>
</configuration>
- <pattern>%msg%n</pattern>
유지보수를 위해 남기는것이기 때문에 이렇게 메세지만 딸랑 출력해서는 의미가 없다.
- %와 레벨 사이에 "-5"를 두면 5칸의 공간을 확보하고 왼쪽부터 출력하겠다는 의미다.
- 패턴은 마음대로 하면 된다. 대괄호를 써본다.
밀리세컨드까지도 출력한다면 그냥 %date만 쓰면 된다.
- 회사가면 패턴을 내가 지정하진 않는다. 이미 정해져 있다.
- 이렇게 appender 태그만 둔다고 로그가 출력되진 않는다.
어떤 패키지의 ~
ㅁ <root>
- 현재 애플리케이션의 "모든 패키지 안"의 "특정 레벨 이상"의 로그를 "특정 appender"에 적용시켜서 출력하는 태그.
- root logger라고도 한다.
- 작성방법
<root level="로그레벨(해당 레벨 이상의 로그만이 출력됨)">
<appender-ref ref="로그를 출력시킬 appender 이름" />
</root>
※ 로그 레벨 (아래로 갈수록 레벨이 높아짐)
- TRACE : 디버깅보다 상세한 정보 표현용
- DEBUG : 개발 단계에서 디버깅용
- INFO : 정보성 메세지 기록용
- WARN : 처리는 가능하나 향후 시스템 에러의 원인이 될 수 있는 경고성 메세지 기록용
- ERROR : 요청 처리 중 문제가 발생 기록용
- FATAL : 아주 심각한 시스템적인 문제 발생 기록용
- 결론부터 말하면 회사가서도 우리가 쓸 로그 레벨은 DEBUG와 INFO 뿐이다.
- 사실 외부 매체에 디버깅용을 남길 필요는 없다. 외부에 기록할 것은 INFO 메세지로 남기면 된다.
- 이 모든것을 우리가 다 콘솔이나 파일에 출력하진 않고 어떤 레벨 이상만 남긴다.
ㅁ logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender class="ch.qos.logback.core.ConsoleAppender" name="consoleLog">
<encoder>
<pattern>%-5level: [%date{yyyy-MM-dd HH:mm:ss}] [%logger:%line] - %msg%n</pattern>
</encoder>
</appender>
<!-- (1) root logger 만으로 모든 클래스 내의 로그가 출력되도록 -->
<root level="DEBUG">
<appender-ref ref="consoleLog" />
</root>
</configuration>
ㅁ MvcController
package com.br.ajax.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MvcController {
private Logger logger = LoggerFactory.getLogger(MvcController.class);
@RequestMapping(value= {"/", "/main.do"})
public String mainPage() {
// 출력문은 성능저하를 야기시킴 => 로그 출력 권장
// System.out.println("MvcController의 mainPage 메소드 작동됨");
// 다음과 같은 레벨로 다음과 같은 메세지가 출력되도록 작성. 각각의 레벨별로 출력.
logger.trace("trace msg");
logger.debug("debug msg");
logger.info("info msg");
logger.warn("warn msg");
logger.error("error msg");
// slf4j는 fatal은 없다.
return "main";
}
}
- Logger 객체가 필요하다. 다른 곳에서도 필요할 수 있으니 전역 필드?로 둔다.
- debug 이상 레벨만 출력이 돼야 한다. trace는 나오면 안된다.
- 페이지 요청도 안했는데 서버 스타트하는 순간 로그가 엄청 나온다.
- 서버를 켜놓은 상태로 작업하면 실시간으로 반영돼서 오류가 날 수 있다.
life cycle어쩌구 spring framework 어쩌구 오류가 날 수 있다. sts 껐다가 다시 실행하면 된다ㅣ.
- 서버 스타트하는 순간 스프링과 관련된 ~
- 기존에는 INFO라고 붙은 것만 실행됐었다.
- HandlerMapping
- 메소드가 실행되게끔 메인페이지가 보여지게한다.
- 우리가 작성한 패턴대로 콘솔에 로그가 출력된다.
- DEBUG 이상의 레벨만 출력되게 했기 때문에 trace는 보여지지 않는다.
- 그런데 debug 이상 출력되게 했더니 너무 무분별하게 많이 출력된다.
org.springframework는 INFO만,
내가 만든 com.br.ajax는 DEBUG레벨 이상만 출력되게끔 따로따로 설정해본다.
ㅁ Logger 임포트
- Logger를 import할 때 logback의 Logger가 아닌 slf4j의 Logger여야 한다.
- slf4j도 2개가 있는데 internal이 아닌 "org.slf4j.Logger"여야 한다.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
- import를 하면 위와 같은 import문 2줄이 생긴다.
ㅁ <logger>
- 현재 애플리케이션의 "특정 패키지 안"의 "특정 레벨 이상"의 로그를 "특정 appender"에 적용시켜 출력
- 작성방법
<logger name="특정패키지" level="로그레벨" [additivity="true | false"]>
[<appender-ref ref="로그를 출력시킬 appender 이름" />]
</logger>
- 다시 또 root logger가 있으면 또 출력이 된다.
- 기본적으로 logger가 먼저 반영되고 root logger가 반영된다.
- root logger로 전달 여부. 기본값 true.
ㅁ logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender class="ch.qos.logback.core.ConsoleAppender" name="consoleLog">
<encoder>
<pattern>%-5level: [%date{yyyy-MM-dd HH:mm:ss}] [%logger:%line] - %msg%n</pattern>
</encoder>
</appender>
<!-- (1) root logger 만으로 모든 클래스 내의 로그가 출력되도록
<root level="DEBUG">
<appender-ref ref="consoleLog" />
</root>
-->
<!-- (2) logger 태그로 특정 패키지를 따로 레벨 지정 -->
<logger name="org.springframework" level="INFO">
<appender-ref ref="consoleLog" />
</logger>
<logger name="com.br.ajax" level="DEBUG">
<appender-ref ref="consoleLog" />
</logger>
</configuration>
- name의 특정 패키지에 level별로 특정 레벨 이상만 consoleLog appender로 적용시켜서 출력한다.
- warn, error는 발생하지 않아서 안보이는 것이다.
- http://localhost:8888/ajax를 요청하면 콘솔에 이렇게 4줄만 찍힌다.
========================================================================
ㅁ logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender class="ch.qos.logback.core.ConsoleAppender" name="consoleLog">
<encoder>
<pattern>%-5level: [%date{yyyy-MM-dd HH:mm:ss}] [%logger:%line] - %msg%n</pattern>
</encoder>
</appender>
<appender class="ch.qos.logback.core.FileAppender" name="fileLog">
<file>/logs/ajax.log</file>
<append>true</append>
<immediateFlush>true</immediateFlush>
<encoder>
<pattern>%-5level: [%date{yyyy-MM-dd HH:mm:ss}] [%logger:%line] - %msg%n</pattern>
</encoder>
</appender>
<!-- (1) root logger 만으로 모든 클래스 내의 로그가 출력되도록
<root level="DEBUG">
<appender-ref ref="consoleLog" />
</root>
-->
<!-- (2) logger 태그로 특정 패키지를 따로 레벨 지정 -->
<logger name="org.springframework" level="INFO">
<appender-ref ref="consoleLog" />
</logger>
<logger name="com.br.ajax" level="DEBUG">
<appender-ref ref="consoleLog" />
</logger>
</configuration>
주로 파일에 많이 쓴다.
- FileAppender를 만든다. 로그가 파일에 기록되게끔 한다.
- appender는 위에 모아놓는게 좋다.
- <file>, <append>, <immediateFlush>를 작성해야 한다. 공식 문서에 다 나와있다.
- <file> 태그. c드라이브의 /logs라는 폴더에 ajax.log라는 파일로 기록되게끔 작성한다.
폴더가 없으면 만들어진다.
- <append>, <immediateFlush>는 true.
- FileAppender는 등록만하고 사용되게끔 하지 않았다.
쓰지도 않았는데 왜 등록했냐는 거다.
ㅁ logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender class="ch.qos.logback.core.ConsoleAppender" name="consoleLog">
<encoder>
<pattern>%-5level: [%date{yyyy-MM-dd HH:mm:ss}] [%logger:%line] - %msg%n</pattern>
</encoder>
</appender>
<appender class="ch.qos.logback.core.FileAppender" name="fileLog">
<file>/logs/ajax.log</file>
<append>true</append>
<immediateFlush>true</immediateFlush>
<encoder>
<pattern>%-5level: [%date{yyyy-MM-dd HH:mm:ss}] [%logger:%line] - %msg%n</pattern>
</encoder>
</appender>
<!-- (1) root logger 만으로 모든 클래스 내의 로그가 출력되도록
<root level="DEBUG">
<appender-ref ref="consoleLog" />
</root>
-->
<!-- (2) logger 태그로 특정 패키지를 따로 레벨 지정 -->
<logger name="org.springframework" level="INFO">
<appender-ref ref="consoleLog" />
<appender-ref ref="fileLog" />
</logger>
<logger name="com.br.ajax" level="DEBUG">
<appender-ref ref="consoleLog" />
<appender-ref ref="fileLog" />
</logger>
</configuration>
- 각각 <appender-ref ref="fileLog" />를 추가해서 콘솔에도 출력하고 파일에도 출력하게 한다.
- 서버 스타트 후 메인페이지 접속. 정상적으로 로그가 출력된다.
- 콘솔에 출력된 로그가 파일에도 쌓여있다.
- 그런데 FileAppender는 파일 하나에만 쌓이기 때문에 좋지 않다.
=========================================================
ㅁ logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender class="ch.qos.logback.core.ConsoleAppender" name="consoleLog">
<encoder>
<pattern>%-5level: [%date{yyyy-MM-dd HH:mm:ss}] [%logger:%line] - %msg%n</pattern>
</encoder>
</appender>
<appender class="ch.qos.logback.core.FileAppender" name="fileLog">
<file>/logs/ajax.log</file>
<append>true</append>
<immediateFlush>true</immediateFlush>
<encoder>
<pattern>%-5level: [%date{yyyy-MM-dd HH:mm:ss}] [%logger:%line] - %msg%n</pattern>
</encoder>
</appender>
<appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="rollingFileLog">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>/logs/ajax-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-5level: [%date{yyyy-MM-dd HH:mm:ss}] [%logger:%line] - %msg%n</pattern>
</encoder>
</appender>
<!-- (1) root logger 만으로 모든 클래스 내의 로그가 출력되도록
<root level="DEBUG">
<appender-ref ref="consoleLog" />
</root>
-->
<!-- (2) logger 태그로 특정 패키지를 따로 레벨 지정 -->
<logger name="org.springframework" level="INFO">
<appender-ref ref="consoleLog" />
<appender-ref ref="fileLog" />
<appender-ref ref="rollingFileLog" />
</logger>
<logger name="com.br.ajax" level="DEBUG">
<appender-ref ref="consoleLog" />
<appender-ref ref="fileLog" />
<appender-ref ref="rollingFileLog" />
</logger>
</configuration>
- 기존 파일은 백업파일이 되고 새로운 로그 파일이 생성된다.
- 보관 정책을 적어야 한다.
- <rollingPolicy> 태그는 로그 파일의 보관 정책을 설정하는 데 사용된다.
- <fileNamePattern> 태그. 똑같은 날짜에도 한 파일에 용량이 많이 쌓이면 여러 파일이 만들어질 수 있다.
뒤에 넘버링되게 %i를 두면 0부터 1, 2, 3, ...이 붙는다.
- <maxHistory>로 파일의 보관일을 정할 수 있다. 30일로 정한다.
- <totalSizeCap>은 로그 파일 전체의 용량을 어느정도로 허용할건지.
10GB를 넘으면 오래된것부터 지워진다.
- 이걸 더 짧게 쓸 수 있다.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender class="ch.qos.logback.core.ConsoleAppender" name="consoleLog">
<encoder>
<pattern>%-5level: [%date{yyyy-MM-dd HH:mm:ss}] [%logger:%line] - %msg%n</pattern>
</encoder>
</appender>
<appender class="ch.qos.logback.core.FileAppender" name="fileLog">
<file>/logs/ajax.log</file>
<append>true</append>
<immediateFlush>true</immediateFlush>
<encoder>
<pattern>%-5level: [%date{yyyy-MM-dd HH:mm:ss}] [%logger:%line] - %msg%n</pattern>
</encoder>
</appender>
<appender class="ch.qos.logback.core.rolling.RollingFileAppender" name="rollingFileLog">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>/logs/ajax-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-5level: [%date{yyyy-MM-dd HH:mm:ss}] [%logger:%line] - %msg%n</pattern>
</encoder>
</appender>
<!-- (1) root logger 만으로 모든 클래스 내의 로그가 출력되도록
<root level="DEBUG">
<appender-ref ref="consoleLog" />
</root>
-->
<!-- (2) logger 태그로 특정 패키지를 따로 레벨 지정
<logger name="org.springframework" level="INFO">
<appender-ref ref="consoleLog" />
<appender-ref ref="fileLog" />
<appender-ref ref="rollingFileLog" />
</logger>
<logger name="com.br.ajax" level="DEBUG">
<appender-ref ref="consoleLog" />
<appender-ref ref="fileLog" />
<appender-ref ref="rollingFileLog" />
</logger>
-->
<!-- (3) root logger와 logger 함께 사용하기 -->
<logger name="org.springframework" level="INFO" />
<logger name="com.br.ajax" level="DEBUG" />
<!-- appender를 안쓰고 root logger로 전달. additivity가 기본값 true로 되어 있다. -->
<!-- 그 밖의 다른 패키지의 것들은 WARN레벨 이상으로 설정. -->
<!-- 모든 패키지에 적용됨. 기본 로그 레벨은 WARN으로 설정 -->
<root level="WARN">
<appender-ref ref="consoleLog" />
<appender-ref ref="fileLog" />
<appender-ref ref="rollingFileLog" />
</root>
</configuration>
- logger 태그에 appender를 쓰지 않고 ~
- <rollingPolicy> 태그의 <fileNamePattern> 태그에 %i도 있어야 한다. 빠지면 콘솔 출력도 이상하게 나오고 파일도 안만들어 진다.
ㅁ print와 log의 차이
- print와 log는 모두 프로그램 실행 중 정보를 출력하거나 확인하는 데 사용되지만, 목적과 기능성에서 차이가 있습니다.
- print는 주로 콘솔에 단순히 메시지를 출력하는 용도로, 디버깅이나 테스트 시 임시로 사용됩니다.
반면, log는 시스템의 상태와 오류를 기록하여 나중에 분석할 수 있도록 하는데, 다양한 로그 레벨(INFO, DEBUG, ERROR 등)을 지원하며 파일이나 외부 저장소에 기록됩니다.
- 또한, log는 출력 외에 로그 회전(롤링), 포맷팅, 필터링 등의 기능도 제공하여 운영 환경에서 문제를 추적하고 해결하는 데 중요한 역할을 합니다.