728x90

기본 Log 설정 및 Logback 설정

Spring Boot 는 내부로그는 Commons Logging 사용한다. Java Util Logging 、Log4J2 과 Logback 등 library 들을 자동옵션으로 두었다. 3가지 로그는 모두 IDE 툴 터미널에서 로그를 구체적으로 찍일수 있다. 오늘은 Logback 관련해서 알아보자

  1. 시간일자:ms 단위
  1. 로그레벨:ERROR, WARN, INFO, DEBUG or TRACE
  1. 프로세스 ID
  1. 구분자 :--- 로그시작점
  1. thread id:
  1. Logger名:클래스 명으로 대체
  1. 로그내용.
  • 로그실제 찍히는 것
2021-12-28 17:37:25.578  INFO 65136 --- [           main] com.didispace.chapter81.Application      : Started Application in 2.695 seconds (JVM running for 3.957)2021-12-28 17:37:25.579 ERROR 65136 --- [           main] com.didispace.chapter81.Application      : Hello World
2021-12-28 17:37:25.579 ERROR 65136 --- [           main] com.didispace.chapter81.Application      : Hello World
2021-12-28 17:37:25.579  WARN 65136 --- [           main] com.didispace.chapter81.Application      : Hello World
2021-12-28 17:37:25.579  INFO 65136 --- [           main] com.didispace.chapter81.Application      : Hello World

  • 로그모드 변경 방법 2가지
$ java -jar myapp.jar --debug

application.properties 에서 debug=true 속성 추가

debug=true 

로그설정

  • 컬러 설정

application.properties 에 spring.output.ansi.enabled 속성 추가

NEVER : ANSI-colored 사용안함.

DETECT : ANSI 여부를 판단하고 맞으면 color 로 로그출력

ALWAYS : 항상 ANSI-colored 모드로 출력 ,ANSI 지원하지 않을시 여러가지 "잡"정보가 나오므로 추천하지 않음.

Spring Boot 1.x 기본 NEVER 이지만 2.x 버전 이후 DETECT 로 변경 됨. 때문에 위 로그 이미지는 기본으로 컬러가 출력 되었다. 요즘 다들 Spring Boot 2.x 를 사용하기때문에 별도로 컬러 설절은 하지 않아도 된다.
  • 로그파일 만들기

application.properties 에

logging.file.name=run.log #파일명

logging.file.path=./ #파일경로

  • 로그파일 롤링
**logging.logback.rollingpolicy.file-name-pattern: "/logs/abc.%d{yyyy-MM-dd}.%i" #파일 생성 규칙 패턴**
**logging.logback.rollingpolicy.clean-history-on-start: # 구동시 기존 로그 지우는가 ,기본 false 임.**
**logging.logback.rollingpolicy.max-history: 30 # 로그파일 유지 개수**
**logging.logback.rollingpolicy.max-file-size: # 파일별 사이즈 제한**
**logging.logback.rollingpolicy.total-size-cap: #**

Log Level 컨틀롤

  • 설정 포맷
💡
logging.level.*=LEVEL
💡
logging.level : * 은 패키지 명 혹은 Logger 명
💡
LEVEL : TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF
  • 예시
**logging.level.com.didispace=DEBUG # com.blake 패키지하위 모든 class는 DEBUG 레벨사용**
**logging.level.root=WARN #root 관련은 로그는 WARN level 사용**
  • 커스텀 설정

일반적으로 ApplicationContext 생성서 Log 모듈이 초기화 된다. Spring 설정파일의 영향을 받지 않는다. 때문에 Springboot 외부에서 Log관련 설정을 자유롭게 할수 있다.

아래와 같은 이름을 springboot에서 사용하는데

💡
Logback:logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy
💡
Log4j2:log4j2-spring.xml, log4j2.xml
💡
JDK (Java Util Logging):logging.properties

SpringBoot 공식사이트에서 -spring 를 붙여서 사용하라고 권장한다.

logback.xml 아닌 logback-spring.xml 을 사용해야 된다.

추가로 application.properties 에 아래와 같은 속성 값도 설정가능하다.

logging.pattern.console:터미널에 찍힐 로그 양식 (JDK Logger 안됨.)

logging.pattern.file:파일에 쓰기위한 로그 양식(JDK Logger 안됨.)

끝!


내저장소 바로가기 luxury515

'Springboot2.x 강좌 > 로그의 기본설정' 카테고리의 다른 글

Springboot 2.6.x 설정 및 사용  (0) 2023.04.11
TinyLog 사용하기  (0) 2023.04.11
728x90

아래와 같은 패키지 구조를 권장한다.

com
  +- example
    +- myproject
      +- Application.java
      |
      +- domain
      |  +- Customer.java
      |  +- CustomerRepository.java
      |
      +- service
      |  +- CustomerService.java
      |
      +- web
      |  +- CustomerController.java
      |

  • root packagecom.example.myproject,모든 class와 기타 package는 모두 root package 하위에 위치.
  • application main class:Application.java,해당 class는 root packag 하위에 위치,일반적으로 해당 class에서 componet scan 설정을 한다.root package 하위에 위치함으로서 수동설정을 줄이면서Spring 필요한것들도 정상적으로 loading 되게 할수 있다.
  • com.example.myproject.domain package:엔티티 매핑 관계 정의 및 데이터 액세스 관련 인터페이스 및 구현
  • com.example.myproject.servicepackage:비즈니스 로직 작성에 사용되는 인터페이스 및 구현
  • com.example.myproject.web:Spring MVC의 Controller 등웹 계층 관련 구현을 작성하는 데 사용됩니다.

여기서 주의 해야 될 부분은 모든 root package 와 application main class 위치다.

application main class 가 root package 의 하위에 위치하였기때문에 하위 class 들도 마찬가지로 root package 의 하위에 위치 하도록 설정해야 되며 default 설정에서는 springboot가 알아서 root package 및 하위 class들을 초기화를 진행한다. 하지만 가끔 default 환경아닌 임의로 구성한 환경이라면? 예를 들어 com.example.myproject.web 가 root package:com.example.myproject 와 동일한 위치에 놓인다면?

com
  +- example
    +- myproject
      +- Application.java
      |
      +- domain
      |  +- Customer.java
      |  +- CustomerRepository.java
      |
      +- service
      |  +- CustomerService.java
      |
    +- web
    |  +- CustomerController.java
    |

이렇게 하면 Application.java 는 com.example.myproject.web 내의 Controller 를 자동으로 스캔할수 없고 마찬가지로 Controller를 초기화 할수 없다. 이때

@ComponentScan(basePackages="com.example")

를 추가하여 강제로 위치를 지정해 준다.

@SpringBootApplication
@ComponentScan(basePackages="com.example")
public class Bootstrap {

    public static void main(String[] args) {
        SpringApplication.run(Bootstrap.class, args);
    }

}

또 다른 방법은 @Bean 어노테이션을 이용하는것

@SpringBootApplication
public class Bootstrap {

    public static void main(String[] args) {
        SpringApplication.run(Bootstrap.class, args);
    }

    @Bean
    public CustomerController customerController() {
        return new CustomerController();
    }

}

뭐 어찌됐든 해결은 가능하나 현업에서는 이런 방식을 권장하지 않는다. 만일 팀장이나 초기 프로젝트 세팅하는 개발자가 이렇게 했다면 이글을 바탕으로 설명을 해주라!

끝!


내저장소 바로가기 luxury515

'Springboot2.x 강좌 > 로그의 기본설정' 카테고리의 다른 글

Logback설정  (1) 2023.04.11
TinyLog 사용하기  (0) 2023.04.11
728x90

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>
<properties>
    <tinylog.version>2.4.1</tinylog.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.tinylog</groupId>
        <artifactId>tinylog-api</artifactId>
        <version>${tinylog.version}</version>
    </dependency>
    <dependency>
        <groupId>org.tinylog</groupId>
        <artifactId>tinylog-impl</artifactId>
        <version>${tinylog.version}</version>
    </dependency>
    <dependency>
        <groupId>org.tinylog</groupId>
        <artifactId>slf4j-tinylog</artifactId>
        <version>${tinylog.version}</version>
    </dependency>
    <dependency>
        <groupId>org.tinylog</groupId>
        <artifactId>jcl-tinylog</artifactId>
        <version>${tinylog.version}</version>
    </dependency>
    <dependency>
        <groupId>org.tinylog</groupId>
        <artifactId>log4j1.2-api</artifactId>
        <version>${tinylog.version}</version>
    </dependency>

</dependencies>

테스트 코드

@Slf4j
@SpringBootApplication
public class LogDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(LogDemoApplication.class, args);

        log.error("Hello World");
        log.warn("Hello World");
        log.info("Hello World");
        log.debug("Hello World");
        log.trace("Hello World");
    }

}

구동 로그확인

TinylogLogger 가 적용된것을 확인 할수 있다.

resources 폴더 안에 tinylog.properties 파일 생성
writer=console
writer.format={date: HH:mm:ss.SSS} {level}: {message}

Log 출력 상태

끝!


내저장소 바로가기 luxury515

'Springboot2.x 강좌 > 로그의 기본설정' 카테고리의 다른 글

Logback설정  (1) 2023.04.11
Springboot 2.6.x 설정 및 사용  (0) 2023.04.11
728x90

RabbitMQ는 AMQP(Advanced Message Queuing Protocol)를 구현한 오픈소스 메시지 브로커. AMQP는 MQ를 오픈 소스에 기반한 표준 프로토콜. 프로토콜만 맞다면 다른 AMQP를 사용한 애플리케이션끼리 통신이 가능하고 플러그인을 통해서 SMTP, STOMP 프로토콜과의 확장이 가능.

기본 설정 및 사용법만 빠르게 알아보자.

  • pom.xml 에서 spring-boot-starter-amqp library 추가 한다.
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.46</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
  • application.properties 설정
spring.rabbitmq.username=demo
spring.rabbitmq.password=demo
#혹은 기타 ip
spring.rabbitmq.host=127.0.0.1  
spring.rabbitmq.port=5672
spring.rabbitmq.virtual-host=/
# 수동ACK , 자동 ACK모드 활성화 하지 않는다. 목적은 에러 발생후 message 유실되는걸 방지. 기본 값 : none
spring.rabbitmq.listener.simple.acknowledge-mode=manual
  • 예제코드 작성
package com.demo.config;

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * RabbitMQ설정
 *
 * @author Blake
 * @since 2022/12/27 0011
 */
@Configuration
public class RabbitConfig {

    public static final String DEFAULT_BOOK_QUEUE = "dev.book.register.default.queue";
    public static final String MANUAL_BOOK_QUEUE = "dev.book.register.manual.queue";

    @Bean
    public Queue defaultBookQueue() {
        return new Queue(DEFAULT_BOOK_QUEUE, true);
    }

    @Bean
    public Queue manualBookQueue() {
        return new Queue(MANUAL_BOOK_QUEUE, true);
    }
}
  • Book 객체 생성
public class Book implements java.io.Serializable {

    private static final long serialVersionUID = -2164058270260403154L;

    private String id;
    private String name;
	// get set ...생략 
}
  • Controller 작성
package com.demo.controller;

import com.demo.config.RabbitConfig;
import com.demo.entity.Book;
import com.demo.handler.BookHandler;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Blake
 * @since 2022/12/27 0002
 */
@RestController
@RequestMapping(value = "/books")
public class BookController {

    private final RabbitTemplate rabbitTemplate;

    @Autowired
    public BookController(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    /**
     * this.rabbitTemplate.convertAndSend(RabbitConfig.DEFAULT_BOOK_QUEUE, book); 对应 {@link BookHandler#listenerAutoAck}
     * this.rabbitTemplate.convertAndSend(RabbitConfig.MANUAL_BOOK_QUEUE, book); 对应 {@link BookHandler#listenerManualAck}
     */
    @GetMapping
    public void defaultMessage() {
        Book book = new Book();
        book.setId("1");
        book.setName(" 하이 ! Spring Boot");
        this.rabbitTemplate.convertAndSend(RabbitConfig.DEFAULT_BOOK_QUEUE, book);
        this.rabbitTemplate.convertAndSend(RabbitConfig.MANUAL_BOOK_QUEUE, book);
    }
}
일반적으로 spring-boot-data-amqp 는 자동 ACK(nowledgement) 이다. 즉 소비자(Consumer)는 MQ 는 message 소비완료되고 자동으로 ACK 한다. 하지만 여기서 약간의 문제가 있는데 바로 이렇게 하는경우 "에러 발생"시 message는 유실되지 않겠지만 무한으로 루프를 돌기때문에 Disk 공간을 모소할수 있다. 소비회수를 지정할수 있지만 이 또한 완벽한 해결방법은 아니라고 한다. 현재 가장 이상적인 방법은 수동 ACK 설정으로 하고 "에러발생" 시 message를 다른 message대기 열에 넣어 보상처리한다.
package com.demo.handler;

import com.demo.config.RabbitConfig;
import com.demo.entity.Book;
import com.rabbitmq.client.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * BOOK_QUEUE 소비자
 *
 * @author Blake
 * @since 2022/12/27 0011
 */
@Component
public class BookHandler {

    private static final Logger log = LoggerFactory.getLogger(BookHandler.class);

    @RabbitListener(queues = {RabbitConfig.DEFAULT_BOOK_QUEUE})
    public void listenerAutoAck(Book book, Message message, Channel channel) {
        
        final long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            log.info("[listenerAutoAck 모니터링하고 있는 message] - [{}]", book.toString());
            
            channel.basicAck(deliveryTag, false);
        } catch (IOException e) {
            try {
                
                channel.basicRecover();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

    @RabbitListener(queues = {RabbitConfig.MANUAL_BOOK_QUEUE})
    public void listenerManualAck(Book book, Message message, Channel channel) {
        log.info("[listenerManualAck 모니터링하고 있는 message] - [{}]", book.toString());
        try {
            
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (IOException e) {
            
        }
    }
}
  • main class
package com.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author Blake
 */
@SpringBootApplication
public class RabbitMqDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(RabbitMqDemoApplication.class, args);
    }
}
  • application 구동하면
2022-12-27 23:04:26.708  INFO 23752 --- [cTaskExecutor-1] com.demo.handler.BookHandler           : [listenerAutoAck 모니터링하고 있는 message] - [com.battcn.entity.Book@77d8be18]
2022-12-27 23:04:26.709  INFO 23752 --- [cTaskExecutor-1] com.demo.handler.BookHandler           : [listenerAutoAck 모니터링하고 있는 message] - [com.battcn.entity.Book@8bb452]

내저장소 바로가기 luxury515

+ Recent posts